withdrawWithLookup.ts 5.7 KB

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