withdrawWithLookup.ts 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import * as anchor from "@coral-xyz/anchor";
  2. import { decode, mapProof } from "../utils";
  3. import { PROGRAM_ID as BUBBLEGUM_PROGRAM_ID } from "@metaplex-foundation/mpl-bubblegum";
  4. import {
  5. SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,
  6. SPL_NOOP_PROGRAM_ID,
  7. } from "@solana/spl-account-compression";
  8. import { getAsset, getAssetProof } from "../readAPI";
  9. import {
  10. AccountMeta,
  11. AddressLookupTableProgram,
  12. PublicKey,
  13. SystemProgram,
  14. Transaction,
  15. TransactionMessage,
  16. VersionedTransaction,
  17. sendAndConfirmTransaction,
  18. } from "@solana/web3.js";
  19. import { connection, keypair, program, programID } from "./constants";
  20. async function main() {
  21. // TODO change all of these to your values
  22. const assetId1 = "DGWU3mHenDerCvjkeDsKYEbsvXbWvqdo1bVoXy3dkeTd";
  23. const assetId2 = "14JojSTdBZvP7f77rCxB3oQK78skTVD6DiXrXUL4objg"; //"D2CoMLCRfsfv1EAiNbaBHfoU1Sqf1964KXLGxEfyUwWo";
  24. const tree1 = new anchor.web3.PublicKey(
  25. "trezdkTFPKyj4gE9LAJYPpxn8AYVCvM7Mc4JkTb9X5B"
  26. );
  27. const tree2 = new anchor.web3.PublicKey(
  28. "trezdkTFPKyj4gE9LAJYPpxn8AYVCvM7Mc4JkTb9X5B"
  29. );
  30. const receiver1 = new anchor.web3.PublicKey(
  31. "Andys9wuoMdUeRiZLgRS5aJwYNFv4Ut6qQi8PNDTAPEM"
  32. );
  33. const receiver2 = new anchor.web3.PublicKey(
  34. "Andys9wuoMdUeRiZLgRS5aJwYNFv4Ut6qQi8PNDTAPEM"
  35. );
  36. // ---
  37. const lookupTable = await createLookupTable();
  38. const [vaultPDA, _bump] = anchor.web3.PublicKey.findProgramAddressSync(
  39. [Buffer.from("cNFT-vault", "utf8")],
  40. programID
  41. );
  42. const [treeAuthority1, _bump2] = anchor.web3.PublicKey.findProgramAddressSync(
  43. [tree1.toBuffer()],
  44. BUBBLEGUM_PROGRAM_ID
  45. );
  46. const [treeAuthority2, _bump3] = anchor.web3.PublicKey.findProgramAddressSync(
  47. [tree2.toBuffer()],
  48. BUBBLEGUM_PROGRAM_ID
  49. );
  50. const asset1 = await getAsset(assetId1);
  51. const asset2 = await getAsset(assetId2);
  52. const proof1 = await getAssetProof(assetId1);
  53. const proofPathAsAccounts1 = mapProof(proof1);
  54. const proof2 = await getAssetProof(assetId2);
  55. const proofPathAsAccounts2 = mapProof(proof2);
  56. const ixData1 = getInstructionData(asset1, proof1);
  57. const ixData2 = getInstructionData(asset2, proof2);
  58. const remainingAccounts: AccountMeta[] = [
  59. ...proofPathAsAccounts1,
  60. ...proofPathAsAccounts2,
  61. ];
  62. const ix = await program.methods
  63. .withdrawTwoCnfts(...ixData1, ...ixData2)
  64. .accounts({
  65. leafOwner: vaultPDA,
  66. merkleTree1: tree1,
  67. newLeafOwner1: receiver1,
  68. treeAuthority1: treeAuthority1,
  69. merkleTree2: tree2,
  70. newLeafOwner2: receiver2,
  71. treeAuthority2: treeAuthority2,
  72. bubblegumProgram: BUBBLEGUM_PROGRAM_ID,
  73. compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,
  74. logWrapper: SPL_NOOP_PROGRAM_ID,
  75. systemProgram: anchor.web3.SystemProgram.programId,
  76. })
  77. .remainingAccounts(remainingAccounts)
  78. .instruction();
  79. await extendLookupTable(
  80. lookupTable,
  81. proofPathAsAccounts1.map((acc) => acc.pubkey)
  82. );
  83. await extendLookupTable(
  84. lookupTable,
  85. proofPathAsAccounts2.map((acc) => acc.pubkey)
  86. );
  87. const lookupTableAccount = await connection
  88. .getAddressLookupTable(lookupTable)
  89. .then((res) => res.value);
  90. if (!lookupTableAccount) {
  91. console.log("could not fetch ATL!");
  92. return;
  93. }
  94. await new Promise((_) => setTimeout(_, 30000));
  95. const messageV0 = new TransactionMessage({
  96. payerKey: keypair.publicKey,
  97. recentBlockhash: (await connection.getLatestBlockhash()).blockhash,
  98. instructions: [ix],
  99. }).compileToV0Message([lookupTableAccount]);
  100. const transactionV0 = new VersionedTransaction(messageV0);
  101. transactionV0.sign([keypair]);
  102. const txid = await connection.sendTransaction(transactionV0);
  103. console.log(txid);
  104. }
  105. function getInstructionData(
  106. asset: any,
  107. proof: any
  108. ): [number[], number[], number[], anchor.BN, number, number] {
  109. const root = decode(proof.root);
  110. const dataHash = decode(asset.compression.data_hash);
  111. const creatorHash = decode(asset.compression.creator_hash);
  112. const nonce = new anchor.BN(asset.compression.leaf_id);
  113. const index = asset.compression.leaf_id;
  114. const proofLength = proof.proof.length;
  115. return [root, dataHash, creatorHash, nonce, index, proofLength];
  116. }
  117. main();
  118. async function extendLookupTable(
  119. lookupTableAddress: PublicKey,
  120. proofHashes: PublicKey[]
  121. ) {
  122. const extendInstruction = AddressLookupTableProgram.extendLookupTable({
  123. payer: keypair.publicKey,
  124. authority: keypair.publicKey,
  125. lookupTable: lookupTableAddress,
  126. addresses: [...proofHashes],
  127. });
  128. const tx = new Transaction();
  129. tx.add(extendInstruction);
  130. const sx = await sendAndConfirmTransaction(connection, tx, [keypair], {
  131. commitment: "finalized",
  132. });
  133. console.log(sx);
  134. console.log("ALT extended!");
  135. }
  136. async function createLookupTable(): Promise<PublicKey> {
  137. const slot = await connection.getSlot();
  138. const [lookupTableInst, lookupTableAddress] =
  139. AddressLookupTableProgram.createLookupTable({
  140. authority: keypair.publicKey,
  141. payer: keypair.publicKey,
  142. recentSlot: slot,
  143. });
  144. console.log(lookupTableAddress.toBase58());
  145. const extendInstruction = AddressLookupTableProgram.extendLookupTable({
  146. payer: keypair.publicKey,
  147. authority: keypair.publicKey,
  148. lookupTable: lookupTableAddress,
  149. addresses: [
  150. programID,
  151. SystemProgram.programId,
  152. BUBBLEGUM_PROGRAM_ID,
  153. SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,
  154. SPL_NOOP_PROGRAM_ID,
  155. // you could add more addresses here, like merkle trees, leaf owners etc.
  156. ],
  157. });
  158. const tx = new Transaction();
  159. tx.add(lookupTableInst).add(extendInstruction);
  160. const sx = await sendAndConfirmTransaction(connection, tx, [keypair], {
  161. commitment: "finalized",
  162. });
  163. console.log(sx);
  164. console.log("ALT created");
  165. return lookupTableAddress;
  166. }