withdrawWithLookup.ts 6.2 KB

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