withdrawWithLookup.ts 5.5 KB

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