bankrun.test.ts 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import { PublicKey, Keypair, SystemProgram, Transaction, TransactionInstruction, LAMPORTS_PER_SOL } from '@solana/web3.js';
  2. import { ProgramTestContext, BanksClient, start } from 'solana-bankrun';
  3. import { createAMint, deserializeOfferAccount, encodeBigint, getMakeOfferInstructionData, getTakeOfferInstructionData, mintTo } from './utils';
  4. import { AccountLayout, ASSOCIATED_TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync, TOKEN_PROGRAM_ID } from '@solana/spl-token';
  5. import { assert } from 'chai';
  6. const PROGRAM_ID = new PublicKey('z7msBPQHDJjTvdQRoEcKyENgXDhSRYeHieN1ZMTqo35');
  7. describe('Escrow Program', () => {
  8. let context: ProgramTestContext;
  9. let client: BanksClient;
  10. let payer: Keypair;
  11. let maker = Keypair.generate();
  12. let taker = Keypair.generate();
  13. const mint_a = Keypair.generate();
  14. const mint_b = Keypair.generate();
  15. let makerAccountA: PublicKey;
  16. let makerAccountB: PublicKey;
  17. let takerAccountA: PublicKey;
  18. let takerAccountB: PublicKey;
  19. const id = BigInt(1);
  20. const token_a_offered_amount = BigInt(2 * 10 ** 9);
  21. const token_b_wanted_amount = BigInt(5 * 10 ** 9);
  22. const [offer, offerBump] = PublicKey.findProgramAddressSync(
  23. [Buffer.from('offer'), maker.publicKey.toBuffer(), Buffer.from(encodeBigint(id))],
  24. PROGRAM_ID,
  25. );
  26. const vault = getAssociatedTokenAddressSync(mint_a.publicKey, offer, true);
  27. before(async () => {
  28. context = await start([{ name: 'escrow_program', programId: PROGRAM_ID }], []);
  29. client = context.banksClient;
  30. payer = context.payer;
  31. {
  32. const tx = new Transaction();
  33. tx.add(
  34. SystemProgram.transfer({
  35. fromPubkey: payer.publicKey,
  36. toPubkey: maker.publicKey,
  37. lamports: LAMPORTS_PER_SOL,
  38. }),
  39. SystemProgram.transfer({
  40. fromPubkey: payer.publicKey,
  41. toPubkey: taker.publicKey,
  42. lamports: LAMPORTS_PER_SOL,
  43. }),
  44. );
  45. tx.recentBlockhash = context.lastBlockhash;
  46. tx.sign(payer);
  47. await client.processTransaction(tx);
  48. }
  49. await createAMint(context, payer, mint_a);
  50. await createAMint(context, payer, mint_b);
  51. makerAccountA = getAssociatedTokenAddressSync(mint_a.publicKey, maker.publicKey, false);
  52. makerAccountB = getAssociatedTokenAddressSync(mint_b.publicKey, maker.publicKey, false);
  53. takerAccountA = getAssociatedTokenAddressSync(mint_a.publicKey, taker.publicKey, false);
  54. takerAccountB = getAssociatedTokenAddressSync(mint_b.publicKey, taker.publicKey, false);
  55. await mintTo(context, payer, maker.publicKey, mint_a.publicKey);
  56. // await mintTo(context, payer, maker.publicKey, mint_b.publicKey);
  57. // await mintTo(context, payer, taker.publicKey, mint_a.publicKey);
  58. await mintTo(context, payer, taker.publicKey, mint_b.publicKey);
  59. });
  60. it('Should make an offer successfully', async () => {
  61. const tx = new Transaction();
  62. tx.add(
  63. new TransactionInstruction({
  64. programId: PROGRAM_ID,
  65. keys: [
  66. { pubkey: maker.publicKey, isSigner: true, isWritable: true },
  67. { pubkey: mint_a.publicKey, isSigner: false, isWritable: false },
  68. { pubkey: mint_b.publicKey, isSigner: false, isWritable: false },
  69. { pubkey: makerAccountA, isSigner: false, isWritable: true },
  70. { pubkey: offer, isSigner: false, isWritable: true },
  71. { pubkey: vault, isSigner: false, isWritable: true },
  72. {
  73. pubkey: TOKEN_PROGRAM_ID,
  74. isSigner: false,
  75. isWritable: false,
  76. },
  77. {
  78. pubkey: SystemProgram.programId,
  79. isSigner: false,
  80. isWritable: false,
  81. },
  82. {
  83. pubkey: ASSOCIATED_TOKEN_PROGRAM_ID,
  84. isSigner: false,
  85. isWritable: false,
  86. },
  87. ],
  88. data: getMakeOfferInstructionData(id, token_a_offered_amount, token_b_wanted_amount),
  89. }),
  90. );
  91. tx.recentBlockhash = context.lastBlockhash;
  92. tx.sign(maker);
  93. // process the transaction
  94. await client.processTransaction(tx);
  95. const offerAccount = await client.getAccount(offer);
  96. assert.isNotNull(offerAccount);
  97. assert.equal(offerAccount?.owner.toBase58(), PROGRAM_ID.toBase58());
  98. const offerAccountData = deserializeOfferAccount(offerAccount.data);
  99. assert.equal(offerAccountData.id, Number(id));
  100. assert.equal(offerAccountData.maker.toBase58(), maker.publicKey.toBase58());
  101. assert.equal(offerAccountData.token_mint_a.toBase58(), mint_a.publicKey.toBase58());
  102. assert.equal(offerAccountData.token_mint_b.toBase58(), mint_b.publicKey.toBase58());
  103. assert.equal(offerAccountData.token_b_wanted_amount, Number(token_b_wanted_amount));
  104. assert.equal(offerAccountData.bump, offerBump);
  105. const rawVaultAccount = await client.getAccount(vault);
  106. assert.isNotNull(rawVaultAccount);
  107. const decodedVaultAccount = AccountLayout.decode(rawVaultAccount?.data);
  108. assert.equal(decodedVaultAccount.amount, token_a_offered_amount);
  109. });
  110. it('Should take an offer successfully', async () => {
  111. const tx = new Transaction();
  112. tx.add(
  113. new TransactionInstruction({
  114. programId: PROGRAM_ID,
  115. keys: [
  116. { pubkey: taker.publicKey, isSigner: true, isWritable: true },
  117. { pubkey: maker.publicKey, isSigner: false, isWritable: true },
  118. { pubkey: mint_a.publicKey, isSigner: false, isWritable: false },
  119. { pubkey: mint_b.publicKey, isSigner: false, isWritable: false },
  120. { pubkey: takerAccountA, isSigner: false, isWritable: true },
  121. { pubkey: takerAccountB, isSigner: false, isWritable: true },
  122. { pubkey: makerAccountB, isSigner: false, isWritable: true },
  123. { pubkey: offer, isSigner: false, isWritable: true },
  124. { pubkey: vault, isSigner: false, isWritable: true },
  125. {
  126. pubkey: TOKEN_PROGRAM_ID,
  127. isSigner: false,
  128. isWritable: false,
  129. },
  130. {
  131. pubkey: SystemProgram.programId,
  132. isSigner: false,
  133. isWritable: false,
  134. },
  135. {
  136. pubkey: ASSOCIATED_TOKEN_PROGRAM_ID,
  137. isSigner: false,
  138. isWritable: false,
  139. },
  140. ],
  141. data: getTakeOfferInstructionData(),
  142. }),
  143. );
  144. tx.recentBlockhash = context.lastBlockhash;
  145. tx.sign(taker);
  146. // process the transaction
  147. await client.processTransaction(tx);
  148. const rawMakerAccountB = await client.getAccount(makerAccountB);
  149. assert.isNotNull(rawMakerAccountB);
  150. const decodedMakerAccountB = AccountLayout.decode(rawMakerAccountB?.data);
  151. assert.equal(decodedMakerAccountB.amount, token_b_wanted_amount);
  152. const rawTakerAccountA = await client.getAccount(takerAccountA);
  153. assert.isNotNull(rawTakerAccountA);
  154. const decodedTakerAccountA = AccountLayout.decode(rawTakerAccountA?.data);
  155. assert.equal(decodedTakerAccountA.amount, token_a_offered_amount);
  156. const offerAccount = await client.getAccount(offer);
  157. assert.isNull(offerAccount);
  158. const vaultAccount = await client.getAccount(vault);
  159. assert.isNull(vaultAccount);
  160. });
  161. });