_setup.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. import { getCreateAccountInstruction } from '@solana-program/system';
  2. import {
  3. Address,
  4. BaseTransactionMessage,
  5. Commitment,
  6. Rpc,
  7. RpcSubscriptions,
  8. SolanaRpcApi,
  9. SolanaRpcSubscriptionsApi,
  10. TransactionMessageWithBlockhashLifetime,
  11. TransactionMessageWithFeePayer,
  12. TransactionPlanExecutor,
  13. TransactionPlanner,
  14. TransactionSigner,
  15. airdropFactory,
  16. appendTransactionMessageInstructions,
  17. assertIsSendableTransaction,
  18. assertIsTransactionWithBlockhashLifetime,
  19. createSolanaRpc,
  20. createSolanaRpcSubscriptions,
  21. createTransactionMessage,
  22. createTransactionPlanExecutor,
  23. createTransactionPlanner,
  24. generateKeyPairSigner,
  25. getSignatureFromTransaction,
  26. lamports,
  27. pipe,
  28. sendAndConfirmTransactionFactory,
  29. setTransactionMessageFeePayerSigner,
  30. setTransactionMessageLifetimeUsingBlockhash,
  31. signTransactionMessageWithSigners,
  32. } from '@solana/kit';
  33. import {
  34. TOKEN_PROGRAM_ADDRESS,
  35. getInitializeAccountInstruction,
  36. getInitializeMintInstruction,
  37. getMintSize,
  38. getMintToInstruction,
  39. getTokenSize,
  40. } from '../src';
  41. type Client = {
  42. rpc: Rpc<SolanaRpcApi>;
  43. rpcSubscriptions: RpcSubscriptions<SolanaRpcSubscriptionsApi>;
  44. };
  45. export const createDefaultSolanaClient = (): Client => {
  46. const rpc = createSolanaRpc('http://127.0.0.1:8899');
  47. const rpcSubscriptions = createSolanaRpcSubscriptions('ws://127.0.0.1:8900');
  48. return { rpc, rpcSubscriptions };
  49. };
  50. export const generateKeyPairSignerWithSol = async (
  51. client: Client,
  52. putativeLamports: bigint = 1_000_000_000n
  53. ) => {
  54. const signer = await generateKeyPairSigner();
  55. await airdropFactory(client)({
  56. recipientAddress: signer.address,
  57. lamports: lamports(putativeLamports),
  58. commitment: 'confirmed',
  59. });
  60. return signer;
  61. };
  62. export const createDefaultTransaction = async (
  63. client: Client,
  64. feePayer: TransactionSigner
  65. ) => {
  66. const { value: latestBlockhash } = await client.rpc
  67. .getLatestBlockhash()
  68. .send();
  69. return pipe(
  70. createTransactionMessage({ version: 0 }),
  71. (tx) => setTransactionMessageFeePayerSigner(feePayer, tx),
  72. (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx)
  73. );
  74. };
  75. export const signAndSendTransaction = async (
  76. client: Client,
  77. transactionMessage: BaseTransactionMessage &
  78. TransactionMessageWithFeePayer &
  79. TransactionMessageWithBlockhashLifetime,
  80. commitment: Commitment = 'confirmed'
  81. ) => {
  82. const signedTransaction =
  83. await signTransactionMessageWithSigners(transactionMessage);
  84. const signature = getSignatureFromTransaction(signedTransaction);
  85. assertIsSendableTransaction(signedTransaction);
  86. assertIsTransactionWithBlockhashLifetime(signedTransaction);
  87. await sendAndConfirmTransactionFactory(client)(signedTransaction, {
  88. commitment,
  89. });
  90. return signature;
  91. };
  92. export const createDefaultTransactionPlanner = (
  93. client: Client,
  94. feePayer: TransactionSigner
  95. ): TransactionPlanner => {
  96. return createTransactionPlanner({
  97. createTransactionMessage: async () => {
  98. const { value: latestBlockhash } = await client.rpc
  99. .getLatestBlockhash()
  100. .send();
  101. return pipe(
  102. createTransactionMessage({ version: 0 }),
  103. (tx) => setTransactionMessageFeePayerSigner(feePayer, tx),
  104. (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx)
  105. );
  106. },
  107. });
  108. };
  109. export const createDefaultTransactionPlanExecutor = (
  110. client: Client,
  111. commitment: Commitment = 'confirmed'
  112. ): TransactionPlanExecutor => {
  113. return createTransactionPlanExecutor({
  114. executeTransactionMessage: async (transactionMessage) => {
  115. const signedTransaction =
  116. await signTransactionMessageWithSigners(transactionMessage);
  117. assertIsSendableTransaction(signedTransaction);
  118. assertIsTransactionWithBlockhashLifetime(signedTransaction);
  119. await sendAndConfirmTransactionFactory(client)(signedTransaction, {
  120. commitment,
  121. });
  122. return { transaction: signedTransaction };
  123. },
  124. });
  125. };
  126. export const getBalance = async (client: Client, address: Address) =>
  127. (await client.rpc.getBalance(address, { commitment: 'confirmed' }).send())
  128. .value;
  129. export const createMint = async (
  130. client: Client,
  131. payer: TransactionSigner,
  132. mintAuthority: Address,
  133. decimals: number = 0
  134. ): Promise<Address> => {
  135. const space = BigInt(getMintSize());
  136. const [transactionMessage, rent, mint] = await Promise.all([
  137. createDefaultTransaction(client, payer),
  138. client.rpc.getMinimumBalanceForRentExemption(space).send(),
  139. generateKeyPairSigner(),
  140. ]);
  141. const instructions = [
  142. getCreateAccountInstruction({
  143. payer,
  144. newAccount: mint,
  145. lamports: rent,
  146. space,
  147. programAddress: TOKEN_PROGRAM_ADDRESS,
  148. }),
  149. getInitializeMintInstruction({
  150. mint: mint.address,
  151. decimals,
  152. mintAuthority,
  153. }),
  154. ];
  155. await pipe(
  156. transactionMessage,
  157. (tx) => appendTransactionMessageInstructions(instructions, tx),
  158. (tx) => signAndSendTransaction(client, tx)
  159. );
  160. return mint.address;
  161. };
  162. export const createToken = async (
  163. client: Client,
  164. payer: TransactionSigner,
  165. mint: Address,
  166. owner: Address
  167. ): Promise<Address> => {
  168. const space = BigInt(getTokenSize());
  169. const [transactionMessage, rent, token] = await Promise.all([
  170. createDefaultTransaction(client, payer),
  171. client.rpc.getMinimumBalanceForRentExemption(space).send(),
  172. generateKeyPairSigner(),
  173. ]);
  174. const instructions = [
  175. getCreateAccountInstruction({
  176. payer,
  177. newAccount: token,
  178. lamports: rent,
  179. space,
  180. programAddress: TOKEN_PROGRAM_ADDRESS,
  181. }),
  182. getInitializeAccountInstruction({ account: token.address, mint, owner }),
  183. ];
  184. await pipe(
  185. transactionMessage,
  186. (tx) => appendTransactionMessageInstructions(instructions, tx),
  187. (tx) => signAndSendTransaction(client, tx)
  188. );
  189. return token.address;
  190. };
  191. export const createTokenWithAmount = async (
  192. client: Client,
  193. payer: TransactionSigner,
  194. mintAuthority: TransactionSigner,
  195. mint: Address,
  196. owner: Address,
  197. amount: bigint
  198. ): Promise<Address> => {
  199. const space = BigInt(getTokenSize());
  200. const [transactionMessage, rent, token] = await Promise.all([
  201. createDefaultTransaction(client, payer),
  202. client.rpc.getMinimumBalanceForRentExemption(space).send(),
  203. generateKeyPairSigner(),
  204. ]);
  205. const instructions = [
  206. getCreateAccountInstruction({
  207. payer,
  208. newAccount: token,
  209. lamports: rent,
  210. space,
  211. programAddress: TOKEN_PROGRAM_ADDRESS,
  212. }),
  213. getInitializeAccountInstruction({ account: token.address, mint, owner }),
  214. getMintToInstruction({ mint, token: token.address, mintAuthority, amount }),
  215. ];
  216. await pipe(
  217. transactionMessage,
  218. (tx) => appendTransactionMessageInstructions(instructions, tx),
  219. (tx) => signAndSendTransaction(client, tx)
  220. );
  221. return token.address;
  222. };