increment.test.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import {
  2. SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE,
  3. appendTransactionMessageInstruction,
  4. isProgramError,
  5. isSolanaError,
  6. lamports,
  7. pipe,
  8. } from '@solana/web3.js';
  9. import test from 'ava';
  10. import {
  11. TOKEN_ERROR__INVALID_PDA,
  12. TOKEN_ERROR__INVALID_PROGRAM_OWNER,
  13. TOKEN_PROGRAM_ADDRESS,
  14. fetchCounter,
  15. findCounterPda,
  16. getIncrementInstruction,
  17. getIncrementInstructionAsync,
  18. } from '../src/index.js';
  19. import {
  20. createCounterForAuthority,
  21. createDefaultSolanaClient,
  22. createDefaultTransaction,
  23. generateKeyPairSignerWithSol,
  24. getBalance,
  25. signAndSendTransaction,
  26. } from './_setup.js';
  27. test('it increments an existing counter by 1 by default', async (t) => {
  28. // Given an authority key pair with an associated counter account of value 0.
  29. const client = createDefaultSolanaClient();
  30. const authority = await generateKeyPairSignerWithSol(client);
  31. const [counterPda] = await createCounterForAuthority(client, authority);
  32. t.is((await fetchCounter(client.rpc, counterPda)).data.value, 0);
  33. // When we increment the counter account.
  34. const incrementIx = await getIncrementInstructionAsync({ authority });
  35. await pipe(
  36. await createDefaultTransaction(client, authority),
  37. (tx) => appendTransactionMessageInstruction(incrementIx, tx),
  38. (tx) => signAndSendTransaction(client, tx)
  39. );
  40. // Then we expect the counter account to have a value of 1.
  41. const counter = await fetchCounter(client.rpc, counterPda);
  42. t.is(counter.data.value, 1);
  43. });
  44. test('it can increment an existing counter by a specified amount', async (t) => {
  45. // Given an authority key pair with an associated counter account of value 0.
  46. const client = createDefaultSolanaClient();
  47. const authority = await generateKeyPairSignerWithSol(client);
  48. const [counterPda] = await createCounterForAuthority(client, authority);
  49. t.is((await fetchCounter(client.rpc, counterPda)).data.value, 0);
  50. // When we increment the counter account by 5.
  51. const incrementIx = await getIncrementInstructionAsync({
  52. authority,
  53. amount: 5,
  54. });
  55. await pipe(
  56. await createDefaultTransaction(client, authority),
  57. (tx) => appendTransactionMessageInstruction(incrementIx, tx),
  58. (tx) => signAndSendTransaction(client, tx)
  59. );
  60. // Then we expect the counter account to have a value of 5.
  61. const counter = await fetchCounter(client.rpc, counterPda);
  62. t.is(counter.data.value, 5);
  63. });
  64. test('it cannot increment a counter that does not exist', async (t) => {
  65. // Given an authority key pair with no associated counter account.
  66. const client = createDefaultSolanaClient();
  67. const authority = await generateKeyPairSignerWithSol(client);
  68. const [counterPda] = await findCounterPda({ authority: authority.address });
  69. t.is(await getBalance(client, counterPda), lamports(0n));
  70. // When we try to increment the inexistent counter account.
  71. const incrementIx = await getIncrementInstructionAsync({ authority });
  72. const transactionMessage = pipe(
  73. await createDefaultTransaction(client, authority),
  74. (tx) => appendTransactionMessageInstruction(incrementIx, tx)
  75. );
  76. const promise = signAndSendTransaction(client, transactionMessage);
  77. // Then we expect the program to throw an error.
  78. const error = await t.throwsAsync(promise);
  79. t.true(
  80. isSolanaError(
  81. error,
  82. SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE
  83. )
  84. );
  85. t.true(
  86. isProgramError(
  87. error.cause,
  88. transactionMessage,
  89. TOKEN_PROGRAM_ADDRESS,
  90. TOKEN_ERROR__INVALID_PROGRAM_OWNER
  91. )
  92. );
  93. });
  94. test('it cannot increment a counter that belongs to another authority', async (t) => {
  95. // Given two authority key pairs such that
  96. // only one of them (authority A) is associated with a counter account.
  97. const client = createDefaultSolanaClient();
  98. const [authorityA, authorityB] = await Promise.all([
  99. generateKeyPairSignerWithSol(client),
  100. generateKeyPairSignerWithSol(client),
  101. ]);
  102. const [counterPda] = await createCounterForAuthority(client, authorityA);
  103. // When authority B tries to increment the counter account of authority A.
  104. const incrementIx = getIncrementInstruction({
  105. authority: authorityB,
  106. counter: counterPda,
  107. });
  108. const transactionMessage = pipe(
  109. await createDefaultTransaction(client, authorityB),
  110. (tx) => appendTransactionMessageInstruction(incrementIx, tx)
  111. );
  112. const promise = signAndSendTransaction(client, transactionMessage);
  113. // Then we expect the program to throw an error.
  114. const error = await t.throwsAsync(promise);
  115. t.true(
  116. isSolanaError(
  117. error,
  118. SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE
  119. )
  120. );
  121. t.true(
  122. isProgramError(
  123. error.cause,
  124. transactionMessage,
  125. TOKEN_PROGRAM_ADDRESS,
  126. TOKEN_ERROR__INVALID_PDA
  127. )
  128. );
  129. });