_setup.ts 7.6 KB

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