tokens.ts 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. import {
  2. address,
  3. createSolanaClient,
  4. generateKeyPairSigner,
  5. getExplorerLink,
  6. getSignatureFromTransaction,
  7. signTransactionMessageWithSigners,
  8. SolanaClusterMoniker,
  9. } from "gill";
  10. import { loadKeypairSignerFromFile } from "gill/node";
  11. import {
  12. buildCreateTokenTransaction,
  13. buildMintTokensTransaction,
  14. buildTransferTokensTransaction,
  15. getAssociatedTokenAccountAddress,
  16. TOKEN_2022_PROGRAM_ADDRESS,
  17. } from "gill/programs";
  18. /** Turn on debug mode */
  19. global.__GILL_DEBUG__ = true;
  20. /** Set the debug mode log level (default: `info`) */
  21. global.__GILL_DEBUG_LEVEL__ = "debug";
  22. /**
  23. * With debug mode enabled and the log level of `debug`:
  24. *
  25. * `sendAndConfirmTransaction` will now auto log the following:
  26. * - explorer link to view the transaction
  27. * - serialized base64 transaction, to inspect on the Solana Explorer's Inspector
  28. * https://explorer.solana.com/tx/inspector
  29. *
  30. * This can greatly assist troubleshooting efforts
  31. */
  32. /**
  33. * Load a keypair signer from the local filesystem
  34. *
  35. * This defaults to the file path used by the Solana CLI: `~/.config/solana/id.json`
  36. */
  37. const signer = await loadKeypairSignerFromFile();
  38. console.log("address:", signer.address);
  39. /**
  40. * Declare what Solana network cluster we want our code to interact with
  41. */
  42. const cluster: SolanaClusterMoniker = "devnet";
  43. /**
  44. * Create a client connection to the Solana blockchain
  45. *
  46. * Note: `urlOrMoniker` can be either a Solana network moniker or a full URL of your RPC provider
  47. */
  48. const { rpc, sendAndConfirmTransaction } = createSolanaClient({
  49. urlOrMoniker: cluster,
  50. });
  51. /**
  52. * Declare our token mint and desired token program
  53. */
  54. const tokenProgram = TOKEN_2022_PROGRAM_ADDRESS;
  55. // const tokenProgram = TOKEN_PROGRAM_ADDRESS;
  56. const mint = await generateKeyPairSigner();
  57. /**
  58. * Get the latest blockhash (aka transaction lifetime). This acts as a recent timestamp
  59. * for the blockchain to key on when processing your transaction
  60. *
  61. * Pro tip: only request this value just before you are going to use it your code
  62. */
  63. const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
  64. console.log("latestBlockhash:", latestBlockhash);
  65. /**
  66. * Create a transaction that will create a new token (with metadata)
  67. *
  68. * - this will use the original SPL token by default (`TOKEN_PROGRAM_ADDRESS`)
  69. */
  70. const createTokenTx = await buildCreateTokenTransaction({
  71. feePayer: signer,
  72. latestBlockhash,
  73. mint,
  74. // mintAuthority, // default=same as the `feePayer`
  75. metadata: {
  76. isMutable: true, // if the `updateAuthority` can change this metadata in the future
  77. name: "Only Possible On Solana",
  78. symbol: "OPOS",
  79. uri: "https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/Climate/metadata.json",
  80. },
  81. // updateAuthority, // default=same as the `feePayer`
  82. decimals: 2, // default=9,
  83. tokenProgram, // default=TOKEN_PROGRAM_ADDRESS, token22 also supported
  84. // default cu limit set to be optimized, but can be overridden here
  85. // computeUnitLimit?: number,
  86. // obtain from your favorite priority fee api
  87. // computeUnitPrice?: number, // no default set
  88. });
  89. /**
  90. * Sign the transaction with the provided `signer` from when it was created
  91. */
  92. let signedTransaction = await signTransactionMessageWithSigners(createTokenTx);
  93. console.log("signedTransaction:");
  94. console.log(signedTransaction);
  95. /**
  96. * Get the transaction signature after it has been signed by the `feePayer`
  97. */
  98. let signature = getSignatureFromTransaction(signedTransaction);
  99. /**
  100. * Log the Solana Explorer link for the transaction we are about to send
  101. */
  102. console.log("\nExplorer Link (for creating the mint):");
  103. console.log(
  104. getExplorerLink({
  105. cluster,
  106. transaction: signature,
  107. }),
  108. );
  109. /**
  110. * Actually send the transaction to the blockchain and confirm it
  111. */
  112. await sendAndConfirmTransaction(signedTransaction);
  113. /**
  114. * Declare the wallet address that we want to mint the tokens to
  115. */
  116. const mintToDestination = address(
  117. "nicktrLHhYzLmoVbuZQzHUTicd2sfP571orwo9jfc8c",
  118. );
  119. /**
  120. * Create a transaction that mints new tokens to the `destination` wallet address
  121. * (raising the token's overall supply)
  122. *
  123. * - be sure to use the correct token program that the `mint` was created with
  124. * - ensure the `mintAuthority` is the correct signer in order to actually mint new tokens
  125. */
  126. const mintTokensTx = await buildMintTokensTransaction({
  127. feePayer: signer,
  128. latestBlockhash,
  129. mint,
  130. mintAuthority: signer,
  131. amount: 2000, // note: be sure to consider the mint's `decimals` value
  132. // if decimals=2 => this will mint 20.00 tokens
  133. // if decimals=4 => this will mint 0.200 tokens
  134. destination: mintToDestination,
  135. // use the correct token program for the `mint`
  136. tokenProgram, // default=TOKEN_PROGRAM_ADDRESS
  137. // default cu limit set to be optimized, but can be overridden here
  138. // computeUnitLimit?: number,
  139. // obtain from your favorite priority fee api
  140. // computeUnitPrice?: number, // no default set
  141. });
  142. console.log("Transaction to mint tokens:");
  143. console.log(mintTokensTx);
  144. /**
  145. * Sign the transaction with the provided `signer` from when it was created
  146. */
  147. signedTransaction = await signTransactionMessageWithSigners(mintTokensTx);
  148. signature = getSignatureFromTransaction(signedTransaction);
  149. console.log(
  150. "\nExplorer Link (for minting the tokens to the destination wallet):",
  151. );
  152. console.log(
  153. getExplorerLink({
  154. cluster,
  155. transaction: signature,
  156. }),
  157. );
  158. await sendAndConfirmTransaction(signedTransaction);
  159. /**
  160. * Get the token balance of a wallet's associated token account (ata)
  161. *
  162. * In this case, we are checking our original wallet's ata
  163. */
  164. let { value: postMintBalance } = await rpc
  165. .getTokenAccountBalance(
  166. await getAssociatedTokenAccountAddress(
  167. mint,
  168. mintToDestination,
  169. tokenProgram,
  170. ),
  171. )
  172. .send();
  173. console.log(
  174. "token balance after minting to 'mintToDestination':",
  175. postMintBalance,
  176. );
  177. /**
  178. * We will generate a new, random wallet in order to show that this wallet's ata
  179. * will be automatically created during the token transfer transaction
  180. */
  181. const transferToDestination = await generateKeyPairSigner();
  182. console.log("transfer to destination:", transferToDestination.address);
  183. /**
  184. * The `authority` address that can authorize the token transfer.
  185. * This is usually the user's wallet or the delegated authority
  186. */
  187. const authority = address("7sZoCrE3cGgEpNgxcPnGffDeWfTewKnk6wWdLxmYA7Cy");
  188. /**
  189. * Create a transaction that mints new tokens to the `destination` wallet address
  190. * (raising the token's overall supply)
  191. *
  192. * - be sure to use the correct token program that the `mint` was created with
  193. * - ensure the `mintAuthority` is the correct signer in order to actually mint new tokens
  194. */
  195. const transferTokensTx = await buildTransferTokensTransaction({
  196. feePayer: signer,
  197. latestBlockhash,
  198. mint,
  199. authority,
  200. amount: 900, // note: be sure to consider the mint's `decimals` value
  201. // if decimals=2 => this will mint 9.00 tokens
  202. // if decimals=4 => this will mint 0.090 tokens
  203. destination: transferToDestination,
  204. // use the correct token program for the `mint`
  205. tokenProgram, // default=TOKEN_PROGRAM_ADDRESS
  206. // default cu limit set to be optimized, but can be overridden here
  207. // computeUnitLimit?: number,
  208. // obtain from your favorite priority fee api
  209. // computeUnitPrice?: number, // no default set
  210. });
  211. /**
  212. * Sign the transaction with the provided `signer` from when it was created
  213. */
  214. signedTransaction = await signTransactionMessageWithSigners(transferTokensTx);
  215. signature = getSignatureFromTransaction(signedTransaction);
  216. console.log("\nExplorer Link (for transferring tokens to the new wallet):");
  217. console.log(
  218. getExplorerLink({
  219. cluster,
  220. transaction: signature,
  221. }),
  222. );
  223. // process.exit();
  224. await sendAndConfirmTransaction(signedTransaction);
  225. /**
  226. * Now that we have transferred tokens FROM the source (in this example, the `signer`),
  227. * we can check this wallets current balance by deriving the ATA
  228. */
  229. const sourceAta = await getAssociatedTokenAccountAddress(
  230. mint,
  231. authority,
  232. tokenProgram,
  233. );
  234. const { value: updatedBalance } = await rpc
  235. .getTokenAccountBalance(sourceAta)
  236. .send();
  237. console.log("new token balance for original source/authority", updatedBalance);
  238. /**
  239. * We can also check the destination wallet's balance,
  240. * including that their ATA was created automatically!
  241. */
  242. const destinationAta = await getAssociatedTokenAccountAddress(
  243. mint,
  244. transferToDestination,
  245. tokenProgram,
  246. );
  247. const { value: destinationWalletBalance } = await rpc
  248. .getTokenAccountBalance(destinationAta)
  249. .send();
  250. console.log("token balance for destination wallet:", destinationWalletBalance);
  251. console.log("Complete.");