tests.ts 5.0 KB

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