3

My objective here is to burn cNFTs on the frontend but it keeps failing b/c tx size is too large. I've attempted to do this with the older createBurnInstruction fn as well as the burn fn with umi. I've also attempted to fetch the proof of the cNFTs from multiple sources. These sources include Helius RPC (DAS getAssetProofBatch method) and the API that Phantom uses(I can burn these cNFT's there w/out issues). Always on mainnet env. Here's the asset on Helius

I was debugging another cNFT with this same issue and when I trimmed down the proof length by 1 it actually worked. From my code you can see that I'm determining this via the canopyDepth which at the time was 0 (same as the cNFT above that I'm debugging with now). The proof size is 24 which according to compressed.app is a valid. Something has to be off with the proofs and canopy size is my guess but I'm at a loss after a lot of debugging. Is there anything obvious to anyone that I'm missing here?

export const buildBurnCnftTxs = async (
  umi: Context,
  connection: Connection,
  cnfts: AssetToBurn[],
): Promise<TransactionBuilder[]> => {
  if (cnfts.length === 0) return [];
  const cNftIds = cnfts.map((i) => i.id);
  const cNnftProofs = await getAssetProofBatch({ ids: cNftIds });

  const builders = await Promise.all(
    cnfts.map(async (asset) => {
      const assetProof = cNnftProofs[asset.id];

      const treeAccount = await ConcurrentMerkleTreeAccount.fromAccountAddress(
        connection,
        new PublicKey(assetProof.tree_id),
      );

      const treeAuthority = treeAccount.getAuthority();
      const canopyDepth = treeAccount.getCanopyDepth() || 0;

      // parse the list of proof addresses into a valid AccountMeta[]
      const proof: AccountMeta[] = assetProof.proof
        // remove the id's that are already part of the canopy
        .slice(0, assetProof.proof.length - (!!canopyDepth ? canopyDepth : 0))
        .map((node: string) => ({
          pubkey: new PublicKey(node),
          isSigner: false,
          isWritable: false,
        }));

      const burnIx = createBurnInstruction(
        {
          merkleTree: new PublicKey(assetProof.tree_id),
          treeAuthority,
          leafOwner: new PublicKey(asset.ownership.owner),
          leafDelegate: new PublicKey(
            asset.ownership.delegate || asset.ownership.owner,
          ),
          logWrapper: SPL_NOOP_PROGRAM_ID,
          compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,
          anchorRemainingAccounts: proof,
        },
        {
          root: [...new PublicKey(assetProof.root.trim()).toBytes()],
          dataHash: [
            ...new PublicKey(asset.compression!.data_hash.trim()).toBytes(),
          ],
          creatorHash: [
            ...new PublicKey(asset.compression!.creator_hash.trim()).toBytes(),
          ],
          nonce: asset.compression!.leaf_id,
          index: asset.compression!.leaf_id,
        },
        BUBBLEGUM_PROGRAM_ID,
      );

      const builder = transactionBuilder().add({
        instruction: {
          keys: burnIx.keys.map((i) => ({
            ...i,
            pubkey: publicKey(i.pubkey),
          })),
          programId: publicKey(burnIx.programId),
          data: burnIx.data,
        },
        signers: [umi.payer],
        bytesCreatedOnChain: 0,
      });

      const txSizeInBurn = builder.getTransactionSize(umi);
      console.log({ txSizeInBurn });
      return builder;
    }),
  );

  return builders;
};

2 Answers 2

1

If your cNFTs function fail this may be because you chose a to small canopy depth. Whenever you interact with cNFTs the whole proof path needs to be included into the transaction up to the on chain canopy depth.

So if you use canopy depth 0 you will already need at least 640 for the proof alone. The max transaction size is 1232 bytes. So with signature and extra accounts there is not much room left.

I would try having a higher canopy on your tree and see if that works. Having higher canopy makes the cNFTs more expensive but also more composable since program that want to use your nfts also will have an easier time using the.

Here is a working cNFTs burn example: https://github.com/solana-developers/program-examples/blob/main/compression/cnft-burn/tests/cnft-burn.ts

1
  • I'm trying to burn existing (spam) cNFTs so I'm not choosing the canopy depth. Regardless if I build the proof myself with the canopy depth returned from treeAccount.getCanopyDepth() or I use the proof directly provided from the DAS calls it doesn't seem to work. I do believe I have a well enough understanding of how to construct cNFT merkle trees, collections, and how those play into the tx size limits. Obviously I'm still missing something here. Commented Jan 31 at 22:19
0

Your above code seems correct, but you need to use Address Lookup Tables if the proof is too large, and makes the transaction exceed the size limit: https://solana.com/docs/advanced/lookup-tables

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.