main.test.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import { Keypair, PublicKey, SystemProgram, Transaction, TransactionInstruction } from '@solana/web3.js';
  2. import * as borsh from 'borsh';
  3. import { assert } from 'chai';
  4. import { describe, it } from 'mocha';
  5. import { BanksClient, ProgramTestContext, start } from 'solana-bankrun';
  6. type PageVisits = {
  7. page_visits: number;
  8. bump: number;
  9. };
  10. const pageVisitsSchema: borsh.Schema = {
  11. struct: {
  12. discriminator: 'u64',
  13. page_visits: 'u32',
  14. bump: 'u8',
  15. },
  16. };
  17. const createPageVisitsBuffer = (data: PageVisits): Buffer => {
  18. const pageVisits = Buffer.alloc(4);
  19. pageVisits.writeUInt32LE(data.page_visits, 0);
  20. const bump = Buffer.alloc(1);
  21. bump.writeUInt8(data.bump, 0);
  22. return Buffer.concat([pageVisits, bump]);
  23. };
  24. describe('program derived addresses program', async () => {
  25. const PROGRAM_ID = new PublicKey('z7msBPQHDJjTvdQRoEcKyENgXDhSRYeHieN1ZMTqo35');
  26. let context: ProgramTestContext;
  27. let client: BanksClient;
  28. let payer: Keypair;
  29. const testUser = Keypair.generate();
  30. const instructionDiscriminators = {
  31. create: Buffer.from([0]),
  32. increment: Buffer.from([1]),
  33. };
  34. const derivePageVisitsPDA = async (user: PublicKey) => {
  35. const seed = Buffer.from('program-derived-addresses');
  36. return PublicKey.findProgramAddressSync([seed, user.toBuffer()], PROGRAM_ID);
  37. };
  38. before(async () => {
  39. context = await start([{ name: 'program_derived_addresses_program', programId: PROGRAM_ID }], []);
  40. client = context.banksClient;
  41. payer = context.payer;
  42. });
  43. it('should create a page visits tracking PDA', async () => {
  44. const [pageVisitsPDA] = await derivePageVisitsPDA(testUser.publicKey);
  45. // create the page visits data
  46. const pageVisits: PageVisits = { page_visits: 0, bump: 0 };
  47. const pageVisitsBuffer = createPageVisitsBuffer(pageVisits);
  48. const data = Buffer.concat([instructionDiscriminators.create, pageVisitsBuffer]);
  49. // create the create instruction
  50. const createIx = new TransactionInstruction({
  51. programId: PROGRAM_ID,
  52. keys: [
  53. { pubkey: payer.publicKey, isSigner: true, isWritable: true },
  54. { pubkey: testUser.publicKey, isSigner: false, isWritable: false },
  55. { pubkey: pageVisitsPDA, isSigner: false, isWritable: true },
  56. { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
  57. ],
  58. data,
  59. });
  60. // send the create transaction
  61. const createTx = new Transaction();
  62. createTx.recentBlockhash = context.lastBlockhash;
  63. createTx.add(createIx).sign(payer);
  64. // process the transaction
  65. await client.processTransaction(createTx);
  66. // fetch the counter account data
  67. const pageVisitsInfo = await client.getAccount(pageVisitsPDA);
  68. assert(pageVisitsInfo !== null, 'account should exist');
  69. });
  70. it('should visit the page and get 1 visit!', async () => {
  71. const [pageVisitsPDA] = await derivePageVisitsPDA(testUser.publicKey);
  72. // create the increment instruction
  73. const incrementIx = new TransactionInstruction({
  74. programId: PROGRAM_ID,
  75. keys: [{ pubkey: pageVisitsPDA, isSigner: false, isWritable: true }],
  76. data: instructionDiscriminators.increment,
  77. });
  78. // send the increment transaction
  79. const incrementTx = new Transaction();
  80. incrementTx.recentBlockhash = context.lastBlockhash;
  81. incrementTx.add(incrementIx).sign(payer);
  82. // process the transaction
  83. await client.processTransaction(incrementTx);
  84. // fetch the account data
  85. const pageVisitsInfo = await client.getAccount(pageVisitsPDA);
  86. assert(pageVisitsInfo !== null, 'account should exist');
  87. const data = borsh.deserialize(pageVisitsSchema, pageVisitsInfo?.data) as PageVisits;
  88. assert(data.page_visits === 1, 'page visits should be 1');
  89. });
  90. it('should visit the page and get 2 visits!', async () => {
  91. const [pageVisitsPDA] = await derivePageVisitsPDA(testUser.publicKey);
  92. // create the increment instruction
  93. const incrementIx = new TransactionInstruction({
  94. programId: PROGRAM_ID,
  95. keys: [{ pubkey: pageVisitsPDA, isSigner: false, isWritable: true }],
  96. data: instructionDiscriminators.increment,
  97. });
  98. // get last blockhash
  99. const [blockhash, _block_height] = await client.getLatestBlockhash();
  100. // send the increment transaction
  101. const incrementTx = new Transaction();
  102. incrementTx.recentBlockhash = blockhash;
  103. incrementTx.add(incrementIx).sign(payer);
  104. // process the transaction
  105. await client.processTransaction(incrementTx);
  106. // fetch the account data
  107. const pageVisitsInfo = await client.getAccount(pageVisitsPDA);
  108. assert(pageVisitsInfo !== null, 'account should exist');
  109. const data = borsh.deserialize(pageVisitsSchema, pageVisitsInfo?.data) as PageVisits;
  110. assert(data.page_visits === 2, 'page visits should be 2');
  111. });
  112. it('should read all the visits of the page', async () => {
  113. const [pageVisitsPDA] = await derivePageVisitsPDA(testUser.publicKey);
  114. // fetch the account data
  115. const pageVisitsInfo = await client.getAccount(pageVisitsPDA);
  116. assert(pageVisitsInfo !== null, 'account should exist');
  117. const data = borsh.deserialize(pageVisitsSchema, pageVisitsInfo?.data) as PageVisits;
  118. assert(data.page_visits === 2, 'page visits should be 2');
  119. });
  120. });