tests.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. import * as anchor from "@coral-xyz/anchor";
  2. import { loadWalletKey, decode, mapProof } from "./utils";
  3. import { IDL, CnftVault } from "../target/types/cnft_vault";
  4. import { PROGRAM_ID as BUBBLEGUM_PROGRAM_ID } from "@metaplex-foundation/mpl-bubblegum";
  5. import {
  6. SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,
  7. SPL_NOOP_PROGRAM_ID,
  8. } from "@solana/spl-account-compression";
  9. import { getAsset, getAssetProof } from "./readAPI";
  10. import { AccountMeta } from "@solana/web3.js";
  11. describe("cNFT Vault", () => {
  12. const provider = anchor.AnchorProvider.env();
  13. anchor.setProvider(provider);
  14. const payer = provider.wallet as anchor.Wallet;
  15. const program = anchor.workspace.CnftVault as anchor.Program<CnftVault>;
  16. const [vaultPDA, _bump] = anchor.web3.PublicKey.findProgramAddressSync(
  17. [Buffer.from("cNFT-vault", "utf8")],
  18. program.programId
  19. );
  20. console.log("Vault address: " + vaultPDA.toBase58());
  21. it("Withdraw a cNFT!", async () => {
  22. // we expect the cNFT to already be in the vault
  23. // you can send it there (to vaultPDA) using any regular wallet
  24. // the cNFT has the following asset id
  25. const assetId = "DGWU3mHenDerCvjkeDsKYEbsvXbWvqdo1bVoXy3dkeTd"; // TODO
  26. // and is compressed in the following tree
  27. const tree = new anchor.web3.PublicKey(
  28. "trezdkTFPKyj4gE9LAJYPpxn8AYVCvM7Mc4JkTb9X5B"
  29. ); // TODO
  30. const receiver = payer.publicKey; // you can define any pubkey as the receiver here
  31. const [treeAuthority, _bump2] =
  32. anchor.web3.PublicKey.findProgramAddressSync(
  33. [tree.toBuffer()],
  34. BUBBLEGUM_PROGRAM_ID
  35. );
  36. const asset = await getAsset(assetId);
  37. const proof = await getAssetProof(assetId);
  38. const proofPathAsAccounts = mapProof(proof);
  39. const root = decode(proof.root);
  40. const dataHash = decode(asset.compression.data_hash);
  41. const creatorHash = decode(asset.compression.creator_hash);
  42. const nonce = new anchor.BN(asset.compression.leaf_id);
  43. const index = asset.compression.leaf_id;
  44. const sx = await program.methods
  45. .withdrawCnft(root, dataHash, creatorHash, nonce, index)
  46. .accounts({
  47. leafOwner: vaultPDA,
  48. merkleTree: tree,
  49. newLeafOwner: receiver,
  50. treeAuthority: treeAuthority,
  51. bubblegumProgram: BUBBLEGUM_PROGRAM_ID,
  52. compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,
  53. logWrapper: SPL_NOOP_PROGRAM_ID,
  54. systemProgram: anchor.web3.SystemProgram.programId,
  55. })
  56. .remainingAccounts(proofPathAsAccounts)
  57. .rpc();
  58. console.log("Success!");
  59. console.log(` Tx Signature: ${sx}`);
  60. });
  61. it("Withdraw two cNFTs!", async () => {
  62. // TODO change all of these to your values
  63. const assetId1 = "DGWU3mHenDerCvjkeDsKYEbsvXbWvqdo1bVoXy3dkeTd";
  64. const assetId2 = "14JojSTdBZvP7f77rCxB3oQK78skTVD6DiXrXUL4objg";
  65. const tree1 = new anchor.web3.PublicKey(
  66. "trezdkTFPKyj4gE9LAJYPpxn8AYVCvM7Mc4JkTb9X5B"
  67. );
  68. const tree2 = new anchor.web3.PublicKey(
  69. "Feywkti8LLBLfxhSGmYgzUBqpq89qehfB1SMTYV1zCu"
  70. );
  71. const receiver1 = new anchor.web3.PublicKey(
  72. "Andys9wuoMdUeRiZLgRS5aJwYNFv4Ut6qQi8PNDTAPEM"
  73. );
  74. const receiver2 = new anchor.web3.PublicKey(
  75. "Andys9wuoMdUeRiZLgRS5aJwYNFv4Ut6qQi8PNDTAPEM"
  76. );
  77. // ---
  78. const [treeAuthority1, _bump2] =
  79. anchor.web3.PublicKey.findProgramAddressSync(
  80. [tree1.toBuffer()],
  81. BUBBLEGUM_PROGRAM_ID
  82. );
  83. const [treeAuthority2, _bump3] =
  84. anchor.web3.PublicKey.findProgramAddressSync(
  85. [tree2.toBuffer()],
  86. BUBBLEGUM_PROGRAM_ID
  87. );
  88. const asset1 = await getAsset(assetId1);
  89. const asset2 = await getAsset(assetId2);
  90. const proof1 = await getAssetProof(assetId1);
  91. const proofPathAsAccounts1 = mapProof(proof1);
  92. const proof2 = await getAssetProof(assetId2);
  93. const proofPathAsAccounts2 = mapProof(proof2);
  94. const ixData1 = getInstructionData(asset1, proof1);
  95. const ixData2 = getInstructionData(asset2, proof2);
  96. const remainingAccounts: AccountMeta[] = [
  97. ...proofPathAsAccounts1,
  98. ...proofPathAsAccounts2,
  99. ];
  100. const sx = await program.methods
  101. .withdrawTwoCnfts(...ixData1, ...ixData2)
  102. .accounts({
  103. leafOwner: vaultPDA,
  104. merkleTree1: tree1,
  105. newLeafOwner1: receiver1,
  106. treeAuthority1: treeAuthority1,
  107. merkleTree2: tree2,
  108. newLeafOwner2: receiver2,
  109. treeAuthority2: treeAuthority2,
  110. bubblegumProgram: BUBBLEGUM_PROGRAM_ID,
  111. compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,
  112. logWrapper: SPL_NOOP_PROGRAM_ID,
  113. systemProgram: anchor.web3.SystemProgram.programId,
  114. })
  115. .remainingAccounts(remainingAccounts)
  116. .rpc();
  117. console.log("Success!");
  118. console.log(` Tx Signature: ${sx}`);
  119. });
  120. });
  121. function getInstructionData(
  122. asset: any,
  123. proof: any
  124. ): [number[], number[], number[], anchor.BN, number, number] {
  125. const root = decode(proof.root);
  126. const dataHash = decode(asset.compression.data_hash);
  127. const creatorHash = decode(asset.compression.creator_hash);
  128. const nonce = new anchor.BN(asset.compression.leaf_id);
  129. const index = asset.compression.leaf_id;
  130. const proofLength = proof.proof.length;
  131. return [root, dataHash, creatorHash, nonce, index, proofLength];
  132. }