test.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. import { Blockhash, Keypair, PublicKey, SystemProgram, Transaction, TransactionInstruction } from '@solana/web3.js';
  2. import { BN } from 'bn.js';
  3. import * as borsh from 'borsh';
  4. import { assert, expect } from 'chai';
  5. import { describe, test } from 'mocha';
  6. import { BanksClient, ProgramTestContext, Rent, start } from 'solana-bankrun';
  7. // This is a helper class to assign properties to the class
  8. class Assignable {
  9. constructor(properties) {
  10. for (const [key, value] of Object.entries(properties)) {
  11. this[key] = value;
  12. }
  13. }
  14. }
  15. enum MyInstruction {
  16. CreateFav = 0,
  17. GetFav = 1,
  18. }
  19. class CreateFav extends Assignable {
  20. number: number;
  21. instruction: MyInstruction;
  22. color: string;
  23. hobbies: string[];
  24. toBuffer() {
  25. return Buffer.from(borsh.serialize(CreateNewAccountSchema, this));
  26. }
  27. static fromBuffer(buffer: Buffer): CreateFav {
  28. return borsh.deserialize(
  29. {
  30. struct: {
  31. number: 'u64',
  32. color: 'string',
  33. hobbies: {
  34. array: {
  35. type: 'string',
  36. },
  37. },
  38. },
  39. },
  40. buffer,
  41. ) as CreateFav;
  42. }
  43. }
  44. const CreateNewAccountSchema = {
  45. struct: {
  46. instruction: 'u8',
  47. number: 'u64',
  48. color: 'string',
  49. hobbies: {
  50. array: {
  51. type: 'string',
  52. },
  53. },
  54. },
  55. };
  56. class GetFav extends Assignable {
  57. toBuffer() {
  58. return Buffer.from(borsh.serialize(GetFavSchema, this));
  59. }
  60. }
  61. const GetFavSchema = {
  62. struct: {
  63. instruction: 'u8',
  64. },
  65. };
  66. describe('Favorites Solana Native', () => {
  67. // Randomly generate the program keypair and load the program to solana-bankrun
  68. const programId = PublicKey.unique();
  69. let context: ProgramTestContext;
  70. let client: BanksClient;
  71. let payer: Keypair;
  72. let blockhash: Blockhash;
  73. beforeEach(async () => {
  74. context = await start([{ name: 'favorites_native', programId }], []);
  75. client = context.banksClient;
  76. // Get the payer keypair from the context, this will be used to sign transactions with enough lamports
  77. payer = context.payer;
  78. blockhash = context.lastBlockhash;
  79. });
  80. test('Set the favorite pda and cross-check the updated data', async () => {
  81. const favoritesPda = PublicKey.findProgramAddressSync([Buffer.from('favorite'), payer.publicKey.toBuffer()], programId)[0];
  82. const favData = { instruction: MyInstruction.CreateFav, number: 42, color: 'blue', hobbies: ['coding', 'reading', 'traveling'] };
  83. const favorites = new CreateFav(favData);
  84. const ix = new TransactionInstruction({
  85. keys: [
  86. { pubkey: payer.publicKey, isSigner: true, isWritable: true },
  87. { pubkey: favoritesPda, isSigner: false, isWritable: true },
  88. { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
  89. ],
  90. programId,
  91. data: favorites.toBuffer(),
  92. });
  93. const tx = new Transaction().add(ix);
  94. tx.feePayer = payer.publicKey;
  95. tx.recentBlockhash = blockhash;
  96. tx.sign(payer);
  97. tx.recentBlockhash = blockhash;
  98. await client.processTransaction(tx);
  99. const account = await client.getAccount(favoritesPda);
  100. const data = Buffer.from(account.data);
  101. const favoritesData = CreateFav.fromBuffer(data);
  102. console.log('Deserialized data:', favoritesData);
  103. expect(new BN(favoritesData.number as any, 'le').toNumber()).to.equal(favData.number);
  104. expect(favoritesData.color).to.equal(favData.color);
  105. expect(favoritesData.hobbies).to.deep.equal(favData.hobbies);
  106. });
  107. test("Check if the test fails if the pda seeds aren't same", async () => {
  108. // We put the wrong seeds knowingly to see if the test fails because of checks
  109. const favoritesPda = PublicKey.findProgramAddressSync([Buffer.from('favorite'), payer.publicKey.toBuffer()], programId)[0];
  110. const favData = { instruction: MyInstruction.CreateFav, number: 42, color: 'blue', hobbies: ['coding', 'reading', 'traveling'] };
  111. const favorites = new CreateFav(favData);
  112. const ix = new TransactionInstruction({
  113. keys: [
  114. { pubkey: payer.publicKey, isSigner: true, isWritable: true },
  115. { pubkey: favoritesPda, isSigner: false, isWritable: true },
  116. { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
  117. ],
  118. programId,
  119. data: favorites.toBuffer(),
  120. });
  121. const tx = new Transaction().add(ix);
  122. tx.feePayer = payer.publicKey;
  123. tx.recentBlockhash = blockhash;
  124. tx.sign(payer);
  125. tx.recentBlockhash = blockhash;
  126. try {
  127. await client.processTransaction(tx);
  128. console.error('Expected the test to fail');
  129. } catch (err) {
  130. assert(true);
  131. }
  132. });
  133. test('Get the favorite pda and cross-check the data', async () => {
  134. // Creating a new account with payer's pubkey
  135. const favoritesPda = PublicKey.findProgramAddressSync([Buffer.from('favorite'), payer.publicKey.toBuffer()], programId)[0];
  136. const favData = { instruction: MyInstruction.CreateFav, number: 42, color: 'hazel', hobbies: ['singing', 'dancing', 'skydiving'] };
  137. const favorites = new CreateFav(favData);
  138. const ix = new TransactionInstruction({
  139. keys: [
  140. { pubkey: payer.publicKey, isSigner: true, isWritable: true },
  141. { pubkey: favoritesPda, isSigner: false, isWritable: true },
  142. { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
  143. ],
  144. programId,
  145. data: favorites.toBuffer(),
  146. });
  147. const tx1 = new Transaction().add(ix);
  148. tx1.feePayer = payer.publicKey;
  149. tx1.recentBlockhash = blockhash;
  150. tx1.sign(payer);
  151. tx1.recentBlockhash = blockhash;
  152. await client.processTransaction(tx1);
  153. // Getting the user's data through the get_pda instruction
  154. const getfavData = { instruction: MyInstruction.GetFav };
  155. const getfavorites = new GetFav(getfavData);
  156. const ix2 = new TransactionInstruction({
  157. keys: [
  158. { pubkey: payer.publicKey, isSigner: true, isWritable: true },
  159. { pubkey: favoritesPda, isSigner: false, isWritable: false },
  160. ],
  161. programId,
  162. data: getfavorites.toBuffer(),
  163. });
  164. const tx = new Transaction().add(ix2);
  165. tx.feePayer = payer.publicKey;
  166. tx.recentBlockhash = blockhash;
  167. tx.sign(payer);
  168. tx.recentBlockhash = blockhash;
  169. await client.processTransaction(tx);
  170. });
  171. });