compressed-nft.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  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. treeAuthority, // treeAuthority
  116. receiver, // leafOwner
  117. receiver, // leafDelegate
  118. treeKeypair.publicKey, // merkleTree
  119. wallet.publicKey, // payer
  120. wallet.publicKey, // treeDelegate
  121. randomUri // uri
  122. )
  123. .accounts({ dataAccount: dataAccount }) // dataAccount required by Solang even though its unused.
  124. .remainingAccounts([
  125. {
  126. pubkey: wallet.publicKey, // payer (and tree delegate in this example)
  127. isWritable: true,
  128. isSigner: true,
  129. },
  130. {
  131. pubkey: receiver, // new leaf owner
  132. isWritable: false,
  133. isSigner: false,
  134. },
  135. {
  136. pubkey: treeAuthority, // tree authority
  137. isWritable: true,
  138. isSigner: false,
  139. },
  140. {
  141. pubkey: treeKeypair.publicKey, // tree account address
  142. isWritable: true,
  143. isSigner: false,
  144. },
  145. {
  146. pubkey: SPL_NOOP_PROGRAM_ID,
  147. isWritable: false,
  148. isSigner: false,
  149. },
  150. {
  151. pubkey: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,
  152. isWritable: false,
  153. isSigner: false,
  154. },
  155. {
  156. pubkey: BUBBLEGUM_PROGRAM_ID,
  157. isWritable: false,
  158. isSigner: false,
  159. },
  160. {
  161. pubkey: SystemProgram.programId,
  162. isWritable: false,
  163. isSigner: false,
  164. },
  165. ])
  166. .rpc({ skipPreflight: true });
  167. console.log("Your transaction signature", tx);
  168. });
  169. });