3

I tried to make a merke tree with Bubblegum and then minted more than one merkle tree, then when I burned one of the compressed nfts I got an error like the one below.

enter image description here

what actually happened? when I created a merkle tree and minted just one compressed nft, burn worked as it should, but when I minted the mekle tree more than once and then burned one of them, then the error appeared. Below is the related code.

im using my contract to invoke bubblegum burn intruction.

anchor :

msg!("burning nft");
    // remaining_accounts are the accounts that make up the required proof
    let remaining_accounts_len = ctx.remaining_accounts.len();
    let mut accounts = Vec::with_capacity(
        7 // space for the 7 AccountMetas that are always included (below)
        + remaining_accounts_len,
    );
    accounts.extend(vec![
        AccountMeta::new_readonly(ctx.accounts.tree_authority.key(), false),
        AccountMeta::new_readonly(ctx.accounts.leaf_owner.key(), true),
        AccountMeta::new_readonly(ctx.accounts.leaf_delegate.key(), false),
        AccountMeta::new(ctx.accounts.merkle_tree.key(), false),
        AccountMeta::new_readonly(ctx.accounts.log_wrapper.key(), false),
        AccountMeta::new_readonly(ctx.accounts.compression_program.key(), false),
        AccountMeta::new_readonly(ctx.accounts.system_program.key(), false),
    ]);
    let burn_discriminator: [u8; 8] = [116, 110, 29, 56, 107, 219, 42, 93];
    let mut data = Vec::with_capacity(
        8 // The length of burn_discriminator,
        + root.len()
        + data_hash.len()
        + creator_hash.len()
        + 8 // The length of the nonce
        + 8, // The length of the index
    );
    data.extend(burn_discriminator);
    data.extend(root);
    data.extend(data_hash);
    data.extend(creator_hash);
    data.extend(nonce.to_le_bytes());
    data.extend(index.to_le_bytes());
    let mut account_infos = Vec::with_capacity(
        7 // space for the 7 AccountInfos that are always included (below)
        + remaining_accounts_len,
    );
    account_infos.extend(vec![
        ctx.accounts.tree_authority.to_account_info(),
        ctx.accounts.leaf_owner.to_account_info(),
        ctx.accounts.leaf_delegate.to_account_info(),
        ctx.accounts.merkle_tree.to_account_info(),
        ctx.accounts.log_wrapper.to_account_info(),
        ctx.accounts.compression_program.to_account_info(),
        ctx.accounts.system_program.to_account_info(),
    ]);
    // Add "accounts" (hashes) that make up the merkle proof from the remaining accounts.
    for acc in ctx.remaining_accounts.iter() {
        accounts.push(AccountMeta::new_readonly(acc.key(), false));
        account_infos.push(acc.to_account_info());
    }
    let instruction = solana_program::instruction::Instruction {
        program_id: ctx.accounts.bubblegum_program.key(),
        accounts,
        data,
    };
    solana_program::program::invoke(&instruction, &account_infos[..])?;

client side:

    log('create tx list')
    for (let i = 0; i < rewardRequiredToBurn; i++) {
      const asset = availableAssets[i]
      const txList: TransactionInstruction[] = [
        // pre
        anchor.web3.ComputeBudgetProgram.setComputeUnitLimit({
          units: 200000 * 2,
        }),
        // burn nfts
        (await new Promise<TransactionInstruction>(async (resolve, reject) => {
          const assetProof = await wrappedConnection.getAssetProof(new PublicKey(asset.id))
          const tree = new PublicKey(asset.compression.tree)
          const treeAccount = await ConcurrentMerkleTreeAccount.fromAccountAddress(
            connection,
            tree
          )
          const treeAuthority = treeAccount.getAuthority()
          const canopyDepth = treeAccount.getCanopyDepth()
          const proofPath: AccountMeta[] = assetProof.proof
            .map((node: string) => ({
              pubkey: new PublicKey(node),
              isSigner: false,
              isWritable: false,
            }))
            .slice(0, assetProof.proof.length - (!!canopyDepth ? canopyDepth : 0))
          log('proofPath', proofPath)

          const root = [...new PublicKey(assetProof.root.trim()).toBytes()]
          const dataHash = [
            ...new PublicKey(asset.compression.data_hash.trim()).toBytes(),
          ]
          const creatorHash = [
            ...new PublicKey(asset.compression.creator_hash.trim()).toBytes(),
          ]
          const nonce = asset.compression.leaf_id
          const index = asset.compression.leaf_id

          const leafDelegate = asset.ownership.delegate ? new PublicKey(asset.ownership.delegate) : new PublicKey(asset.ownership.owner)
          let _tx = await (currentProgram.methods
            .processRedeemReward(
              {
                ...reward_args.nft.metadataArgs,
                collection: {
                  ...reward_args.nft.metadataArgs.collection,
                  verified: true,
                }
              } as MetadataArgs as any,
              root,
              dataHash,
              creatorHash,
              new anchor.BN(nonce),
              index,
            )
            .accounts({
              ticket: redeemRewardUser,
              reward: created_reward,
              leafDelegate: leafDelegate,
              leafOwner: new PublicKey(asset.ownership.owner),
              treeAuthority: treeAuthority,
              merkleTree: new PublicKey(assetProof.tree_id),
              compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,
              logWrapper: SPL_NOOP_PROGRAM_ID,
              bubblegumProgram: BGUM_PROGRAM_ID,
            })
            // .remainingAccounts(proofPath)
            .instruction())
          resolve(_tx)
        })),
      ]

      const tx = new Transaction()
      txList.forEach((txItem) => tx.add(txItem))
      try {
        tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash
      } catch (error) {}
      tx.feePayer = currentWallet.publicKey
      tx.partialSign(currentWallet)
      tx.sign(currentWallet)

      const txRes = await sendAndConfirmTransaction(connection, tx, [currentWallet], {
        commitment: 'confirmed',
        skipPreflight: true,
      })
    }

1 Answer 1

3

invoke burn instruction must pass proof to remaining accounts.

const proofPath: AccountMeta[] = assetProof.proof
.map((node: string) => ({
  pubkey: new PublicKey(node),
  isSigner: false,
  isWritable: false,
}))
.slice(0, assetProof.proof.length - (!!canopyDepth ? canopyDepth : 0))


.accounts({
  ticket: redeemRewardUser,
  reward: created_reward,
  leafDelegate: leafDelegate,
  leafOwner: new PublicKey(asset.ownership.owner),
  treeAuthority: treeAuthority,
  merkleTree: new PublicKey(assetProof.tree_id),
  compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,
  logWrapper: SPL_NOOP_PROGRAM_ID,
  bubblegumProgram: BGUM_PROGRAM_ID,
})
.remainingAccounts(proofPath)
.rpc()

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.