import * as anchor from "@coral-xyz/anchor"; import { Program, Wallet } from "@coral-xyz/anchor"; import { CompressedNft } from "../target/types/compressed_nft"; import { PublicKey, SystemProgram, Transaction, Keypair, sendAndConfirmTransaction, } from "@solana/web3.js"; import { SPL_ACCOUNT_COMPRESSION_PROGRAM_ID, ValidDepthSizePair, SPL_NOOP_PROGRAM_ID, createAllocTreeIx, } from "@solana/spl-account-compression"; import { PROGRAM_ID as BUBBLEGUM_PROGRAM_ID, createCreateTreeInstruction, } from "@metaplex-foundation/mpl-bubblegum"; import { uris } from "../utils/uri"; describe("compressed-nft", () => { const provider = anchor.AnchorProvider.env(); anchor.setProvider(provider); const wallet = provider.wallet as Wallet; const connection = provider.connection; const program = anchor.workspace.CompressedNft as Program; // Generate a new keypair for the merkle tree. const treeKeypair = Keypair.generate(); // Derive the PDA that will be the tree authority. // This is required by the bubblegum program. const [treeAuthority] = PublicKey.findProgramAddressSync( [treeKeypair.publicKey.toBuffer()], BUBBLEGUM_PROGRAM_ID ); // Derive the PDA that will be used to initialize the dataAccount. // Required by Solang even though we're not using it. const [dataAccount, bump] = PublicKey.findProgramAddressSync( [Buffer.from("seed")], program.programId ); // Create a merkle tree account. before(async () => { // Maximum depth and buffer size for the merkle tree. // 2^maxDepth determines the maximum number of leaves that can be stored in the tree. // maxBufferSize determines maximum concurrent updates that can be made within one slot. const maxDepthSizePair: ValidDepthSizePair = { maxDepth: 14, maxBufferSize: 64, }; // Depth of the canopy (how much of the tree is stored on-chain) const canopyDepth = 0; // Instruction to create an account with enough space to store the merkle tree. const allocTreeIx = await createAllocTreeIx( connection, treeKeypair.publicKey, wallet.publicKey, maxDepthSizePair, canopyDepth ); // Instruction to initialize the merkle tree account with the bubblegum program. const createTreeIx = createCreateTreeInstruction( { treeAuthority, merkleTree: treeKeypair.publicKey, payer: wallet.publicKey, treeCreator: wallet.publicKey, logWrapper: SPL_NOOP_PROGRAM_ID, compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID, }, { maxBufferSize: maxDepthSizePair.maxBufferSize, maxDepth: maxDepthSizePair.maxDepth, public: true, // creating a "public" tree, so anyone can mint cnfts to it }, BUBBLEGUM_PROGRAM_ID ); try { const tx = new Transaction().add(allocTreeIx, createTreeIx); tx.feePayer = wallet.publicKey; const txSignature = await sendAndConfirmTransaction( connection, tx, [treeKeypair, wallet.payer], { commitment: "confirmed", skipPreflight: true, } ); console.log( `https://explorer.solana.com/tx/${txSignature}?cluster=devnet` ); console.log("Tree Address:", treeKeypair.publicKey.toBase58()); } catch (err: any) { console.error("\nFailed to create merkle tree:", err); throw err; } console.log("\n"); }); it("Is initialized!", async () => { // Initialize the dataAccount. const tx = await program.methods .new([bump]) .accounts({ dataAccount: dataAccount }) .rpc(); console.log("Your transaction signature", tx); }); it("Mint Compressed NFT", async () => { // Mint a compressed nft to random receiver. const receiver = Keypair.generate().publicKey; // Use a random uri (off-chain metadata) from the list for the test. const randomUri = uris[Math.floor(Math.random() * uris.length)]; const tx = await program.methods .mint( randomUri // uri ) .accounts({ tree_authority: treeAuthority, leaf_owner: receiver, leaf_delegate: receiver, merkle_tree: treeKeypair.publicKey, payer: wallet.publicKey, tree_delegate: wallet.publicKey, noop_address: SPL_NOOP_PROGRAM_ID, compression_pid: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID, bubblegum_pid: BUBBLEGUM_PROGRAM_ID, }) .rpc({ skipPreflight: true }); console.log("Your transaction signature", tx); }); });