utils.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. import * as fs from "fs/promises";
  2. import { splTokenProgram } from "@project-serum/spl-token";
  3. import { splAssociatedTokenAccountProgram } from "@project-serum/spl-associated-token-account";
  4. import {
  5. Connection,
  6. Keypair,
  7. LAMPORTS_PER_SOL,
  8. PublicKey,
  9. Signer,
  10. SystemProgram,
  11. SYSVAR_RENT_PUBKEY,
  12. Transaction,
  13. TransactionInstruction,
  14. } from "@solana/web3.js";
  15. import { AnchorProvider, Wallet } from "@project-serum/anchor";
  16. import { SPL_ATA_PROGRAM_ID, SPL_TOKEN_PROGRAM_ID } from "./constants";
  17. const KEYPAIR_PATH = "test-keypair.json";
  18. let totalTestCount = 0;
  19. let totalErrorCount = 0;
  20. export async function mainTest(cb: () => Promise<void>) {
  21. info(`Running tests...`);
  22. await cb();
  23. if (totalErrorCount) {
  24. error(
  25. `${totalErrorCount}/${totalTestCount} test${
  26. totalErrorCount > 1 ? "s" : ""
  27. } failed.`
  28. );
  29. } else {
  30. success(`All tests passed.(${totalTestCount})`);
  31. }
  32. }
  33. let testCount = 0;
  34. let errorCount = 0;
  35. export async function programTest(cb: () => Promise<void>) {
  36. const tab = " ";
  37. info(`${tab}Running '${cb.name}'...`);
  38. await cb();
  39. if (errorCount) {
  40. error(
  41. `${tab}${errorCount}/${testCount} test${errorCount > 1 ? "s" : ""} in '${
  42. cb.name
  43. }' failed.`
  44. );
  45. totalErrorCount += errorCount;
  46. } else {
  47. success(`${tab}All '${cb.name}' tests passed.`);
  48. }
  49. totalTestCount += testCount;
  50. testCount = 0;
  51. errorCount = 0;
  52. }
  53. export async function test(cb: () => Promise<void>) {
  54. const tab = " ";
  55. console.log(`${tab}Running test '\x1b[1m${cb.name}\x1b[0m'`);
  56. try {
  57. await cb();
  58. success(`${tab}Test '${cb.name}' passed.`);
  59. } catch (e) {
  60. error(`${tab}Test '${cb.name}' failed. Reason: ${e}`);
  61. errorCount++;
  62. } finally {
  63. testCount++;
  64. }
  65. }
  66. export async function loadKp() {
  67. try {
  68. const kpBytes = await fs.readFile(KEYPAIR_PATH);
  69. const kp = Keypair.fromSecretKey(
  70. Uint8Array.from(JSON.parse(kpBytes.toString()))
  71. );
  72. return kp;
  73. } catch {
  74. info("Creating test keypair file...");
  75. const randomKp = new Keypair();
  76. await fs.writeFile(
  77. KEYPAIR_PATH,
  78. JSON.stringify(Array.from(randomKp.secretKey))
  79. );
  80. return randomKp;
  81. }
  82. }
  83. let hasBalance = false;
  84. export async function getProvider() {
  85. const kp = await loadKp();
  86. const ENDPOINT = "http://localhost:8899";
  87. // const ENDPOINT = "https://api.devnet.solana.com";
  88. const conn = new Connection(ENDPOINT, {
  89. commitment: "confirmed",
  90. });
  91. const wallet = new Wallet(kp);
  92. const provider = new AnchorProvider(
  93. conn,
  94. wallet,
  95. AnchorProvider.defaultOptions()
  96. );
  97. if (!hasBalance && !(await provider.connection.getBalance(kp.publicKey))) {
  98. const txHash = await provider.connection.requestAirdrop(
  99. kp.publicKey,
  100. 1000 * LAMPORTS_PER_SOL
  101. );
  102. await confirmTx(txHash);
  103. hasBalance = true;
  104. }
  105. return provider;
  106. }
  107. export async function sleep(ms: number = 500) {
  108. return new Promise((res) => setTimeout((s) => res(s), ms));
  109. }
  110. export async function confirmTx(txHash: string) {
  111. const provider = await getProvider();
  112. const blockhashInfo = await provider.connection.getLatestBlockhash();
  113. await provider.connection.confirmTransaction({
  114. blockhash: blockhashInfo.blockhash,
  115. lastValidBlockHeight: blockhashInfo.lastValidBlockHeight,
  116. signature: txHash,
  117. });
  118. }
  119. export async function sendAndConfirmTx(
  120. ixs: TransactionInstruction[],
  121. signers: Signer[]
  122. ) {
  123. const provider = await getProvider();
  124. const blockhashInfo = await provider.connection.getLatestBlockhash();
  125. const tx = new Transaction().add(...ixs);
  126. tx.feePayer = provider.publicKey;
  127. tx.recentBlockhash = blockhashInfo.blockhash;
  128. tx.sign(...signers);
  129. const txHash = await provider.connection.sendRawTransaction(tx.serialize());
  130. await confirmTx(txHash);
  131. return txHash;
  132. }
  133. export async function simulateTx(
  134. ixs: TransactionInstruction[],
  135. signers: Signer[]
  136. ) {
  137. const provider = await getProvider();
  138. const blockhashInfo = await provider.connection.getLatestBlockhash();
  139. const tx = new Transaction().add(...ixs);
  140. tx.feePayer = provider.publicKey;
  141. tx.recentBlockhash = blockhashInfo.blockhash;
  142. tx.sign(...signers);
  143. const simulationResult = await provider.connection.simulateTransaction(tx);
  144. return simulationResult;
  145. }
  146. export async function createMint(ownerPk?: PublicKey) {
  147. const provider = await getProvider();
  148. const kp = await loadKp();
  149. if (!ownerPk) ownerPk = kp.publicKey;
  150. const tokenProgram = splTokenProgram({
  151. provider,
  152. programId: SPL_TOKEN_PROGRAM_ID,
  153. });
  154. const mintKp = new Keypair();
  155. const createMintAccountIx = await tokenProgram.account.mint.createInstruction(
  156. mintKp
  157. );
  158. const initMintIx = await tokenProgram.methods
  159. .initializeMint(6, ownerPk, null)
  160. .accounts({
  161. mint: mintKp.publicKey,
  162. rent: SYSVAR_RENT_PUBKEY,
  163. })
  164. .instruction();
  165. await sendAndConfirmTx([createMintAccountIx, initMintIx], [kp, mintKp]);
  166. return mintKp.publicKey;
  167. }
  168. export async function createTokenAccount(
  169. mintPk: PublicKey,
  170. programId: PublicKey = SPL_TOKEN_PROGRAM_ID
  171. ) {
  172. const provider = await getProvider();
  173. const kp = await loadKp();
  174. const tokenProgram = splTokenProgram({
  175. provider,
  176. programId,
  177. });
  178. const accountKp = new Keypair();
  179. const createTokenAccountIx =
  180. await tokenProgram.account.account.createInstruction(accountKp);
  181. const initAccountIx = await tokenProgram.methods
  182. .initializeAccount()
  183. .accounts({
  184. account: accountKp.publicKey,
  185. mint: mintPk,
  186. owner: kp.publicKey,
  187. rent: SYSVAR_RENT_PUBKEY,
  188. })
  189. .instruction();
  190. await sendAndConfirmTx(
  191. [createTokenAccountIx, initAccountIx],
  192. [kp, accountKp]
  193. );
  194. return accountKp.publicKey;
  195. }
  196. export async function getAta(mintPk: PublicKey, ownerPk: PublicKey) {
  197. return (
  198. await PublicKey.findProgramAddress(
  199. [ownerPk.toBuffer(), SPL_TOKEN_PROGRAM_ID.toBuffer(), mintPk.toBuffer()],
  200. SPL_ATA_PROGRAM_ID
  201. )
  202. )[0];
  203. }
  204. export async function createAta(mintPk: PublicKey, ownerPk: PublicKey) {
  205. const provider = await getProvider();
  206. const ataPk = await getAta(mintPk, ownerPk);
  207. if (!(await provider.connection.getAccountInfo(ataPk))) {
  208. const ataProgram = splAssociatedTokenAccountProgram({
  209. provider,
  210. programId: SPL_ATA_PROGRAM_ID,
  211. });
  212. await ataProgram.methods
  213. .create()
  214. .accounts({
  215. associatedAccountAddress: ataPk,
  216. fundingAddress: provider.publicKey,
  217. systemProgram: SystemProgram.programId,
  218. tokenMintAddress: mintPk,
  219. tokenProgram: SPL_TOKEN_PROGRAM_ID,
  220. walletAddress: ownerPk,
  221. })
  222. .rpc();
  223. }
  224. return ataPk;
  225. }
  226. const info = (s: string) => {
  227. console.log(`\x1b[1;36m${s}\x1b[0m`);
  228. };
  229. const success = (s: string) => {
  230. console.log(`\x1b[1;32m${s}\x1b[0m`);
  231. };
  232. const error = (s: string) => {
  233. console.log(`\x1b[1;31m${s}\x1b[0m`);
  234. };