compressed-nft.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import * as anchor from "@coral-xyz/anchor";
  2. import { Program, Wallet } from "@coral-xyz/anchor";
  3. import { CompressedNft } from "../target/types/compressed_nft";
  4. import {
  5. PublicKey,
  6. SystemProgram,
  7. Transaction,
  8. Keypair,
  9. sendAndConfirmTransaction,
  10. } from "@solana/web3.js";
  11. import {
  12. SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,
  13. ValidDepthSizePair,
  14. SPL_NOOP_PROGRAM_ID,
  15. createAllocTreeIx,
  16. } from "@solana/spl-account-compression";
  17. import {
  18. PROGRAM_ID as BUBBLEGUM_PROGRAM_ID,
  19. createCreateTreeInstruction,
  20. } from "@metaplex-foundation/mpl-bubblegum";
  21. import { uris } from "../utils/uri";
  22. describe("compressed-nft", () => {
  23. const provider = anchor.AnchorProvider.env();
  24. anchor.setProvider(provider);
  25. const wallet = provider.wallet as Wallet;
  26. const connection = provider.connection;
  27. const program = anchor.workspace.CompressedNft as Program<CompressedNft>;
  28. // Generate a new keypair for the merkle tree.
  29. const treeKeypair = Keypair.generate();
  30. // Derive the PDA that will be the tree authority.
  31. // This is required by the bubblegum program.
  32. const [treeAuthority] = PublicKey.findProgramAddressSync(
  33. [treeKeypair.publicKey.toBuffer()],
  34. BUBBLEGUM_PROGRAM_ID
  35. );
  36. // Derive the PDA that will be used to initialize the dataAccount.
  37. // Required by Solang even though we're not using it.
  38. const [dataAccount, bump] = PublicKey.findProgramAddressSync(
  39. [Buffer.from("seed")],
  40. program.programId
  41. );
  42. // Create a merkle tree account.
  43. before(async () => {
  44. // Maximum depth and buffer size for the merkle tree.
  45. // 2^maxDepth determines the maximum number of leaves that can be stored in the tree.
  46. // maxBufferSize determines maximum concurrent updates that can be made within one slot.
  47. const maxDepthSizePair: ValidDepthSizePair = {
  48. maxDepth: 14,
  49. maxBufferSize: 64,
  50. };
  51. // Depth of the canopy (how much of the tree is stored on-chain)
  52. const canopyDepth = 0;
  53. // Instruction to create an account with enough space to store the merkle tree.
  54. const allocTreeIx = await createAllocTreeIx(
  55. connection,
  56. treeKeypair.publicKey,
  57. wallet.publicKey,
  58. maxDepthSizePair,
  59. canopyDepth
  60. );
  61. // Instruction to initialize the merkle tree account with the bubblegum program.
  62. const createTreeIx = createCreateTreeInstruction(
  63. {
  64. treeAuthority,
  65. merkleTree: treeKeypair.publicKey,
  66. payer: wallet.publicKey,
  67. treeCreator: wallet.publicKey,
  68. logWrapper: SPL_NOOP_PROGRAM_ID,
  69. compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,
  70. },
  71. {
  72. maxBufferSize: maxDepthSizePair.maxBufferSize,
  73. maxDepth: maxDepthSizePair.maxDepth,
  74. public: true, // creating a "public" tree, so anyone can mint cnfts to it
  75. },
  76. BUBBLEGUM_PROGRAM_ID
  77. );
  78. try {
  79. const tx = new Transaction().add(allocTreeIx, createTreeIx);
  80. tx.feePayer = wallet.publicKey;
  81. const txSignature = await sendAndConfirmTransaction(
  82. connection,
  83. tx,
  84. [treeKeypair, wallet.payer],
  85. {
  86. commitment: "confirmed",
  87. skipPreflight: true,
  88. }
  89. );
  90. console.log(
  91. `https://explorer.solana.com/tx/${txSignature}?cluster=devnet`
  92. );
  93. console.log("Tree Address:", treeKeypair.publicKey.toBase58());
  94. } catch (err: any) {
  95. console.error("\nFailed to create merkle tree:", err);
  96. throw err;
  97. }
  98. console.log("\n");
  99. });
  100. it("Is initialized!", async () => {
  101. // Initialize the dataAccount.
  102. const tx = await program.methods
  103. .new([bump])
  104. .accounts({ dataAccount: dataAccount })
  105. .rpc();
  106. console.log("Your transaction signature", tx);
  107. });
  108. it("Mint Compressed NFT", async () => {
  109. // Mint a compressed nft to random receiver.
  110. const receiver = Keypair.generate().publicKey;
  111. // Use a random uri (off-chain metadata) from the list for the test.
  112. const randomUri = uris[Math.floor(Math.random() * uris.length)];
  113. const tx = await program.methods
  114. .mint(
  115. randomUri // uri
  116. )
  117. .accounts({
  118. tree_authority: treeAuthority,
  119. leaf_owner: receiver,
  120. leaf_delegate: receiver,
  121. merkle_tree: treeKeypair.publicKey,
  122. payer: wallet.publicKey,
  123. tree_delegate: wallet.publicKey,
  124. noop_address: SPL_NOOP_PROGRAM_ID,
  125. compression_pid: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,
  126. bubblegum_pid: BUBBLEGUM_PROGRAM_ID,
  127. })
  128. .rpc({ skipPreflight: true });
  129. console.log("Your transaction signature", tx);
  130. });
  131. });