token-proxy.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. const anchor = require("@coral-xyz/anchor");
  2. const { assert } = require("chai");
  3. const {
  4. splTokenProgram,
  5. SPL_TOKEN_PROGRAM_ID,
  6. } = require("@coral-xyz/spl-token");
  7. describe("program", () => {
  8. const provider = anchor.AnchorProvider.local();
  9. const TEST_PROGRAM_IDS = [
  10. SPL_TOKEN_PROGRAM_ID,
  11. new anchor.web3.PublicKey("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"),
  12. ];
  13. const TOKEN_PROGRAMS = TEST_PROGRAM_IDS.map((programId) =>
  14. splTokenProgram({
  15. provider,
  16. programId,
  17. })
  18. );
  19. // Configure the client to use the local cluster.
  20. anchor.setProvider(provider);
  21. const program = anchor.workspace.TokenProxy;
  22. TOKEN_PROGRAMS.forEach((tokenProgram) => {
  23. const name =
  24. tokenProgram.programId === SPL_TOKEN_PROGRAM_ID ? "token" : "token-2022";
  25. describe(name, () => {
  26. let mint = null;
  27. let from = null;
  28. let to = null;
  29. it("Initializes test state", async () => {
  30. mint = await createMint(tokenProgram);
  31. from = await createTokenAccount(
  32. tokenProgram,
  33. mint,
  34. provider.wallet.publicKey
  35. );
  36. to = await createTokenAccount(
  37. tokenProgram,
  38. mint,
  39. provider.wallet.publicKey
  40. );
  41. });
  42. it("Creates a token account", async () => {
  43. const newMint = await createMint(tokenProgram);
  44. const authority = provider.wallet.publicKey;
  45. const [tokenAccount] = anchor.web3.PublicKey.findProgramAddressSync(
  46. [
  47. authority.toBytes(),
  48. newMint.toBytes(),
  49. Buffer.from("token-proxy-account"),
  50. ],
  51. program.programId
  52. );
  53. await program.rpc.proxyCreateTokenAccount({
  54. accounts: {
  55. authority,
  56. mint: newMint,
  57. tokenAccount,
  58. systemProgram: anchor.web3.SystemProgram.programId,
  59. tokenProgram: tokenProgram.programId,
  60. },
  61. });
  62. const account = await getTokenAccount(provider, tokenAccount);
  63. assert.isTrue(account.amount.eq(new anchor.BN(0)));
  64. });
  65. it("Creates an associated token account", async () => {
  66. const newMint = await createMint(tokenProgram);
  67. const authority = provider.wallet.publicKey;
  68. const associatedTokenProgram = new anchor.web3.PublicKey(
  69. "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
  70. );
  71. const [tokenAccount] = anchor.web3.PublicKey.findProgramAddressSync(
  72. [
  73. authority.toBytes(),
  74. tokenProgram.programId.toBytes(),
  75. newMint.toBytes(),
  76. ],
  77. associatedTokenProgram
  78. );
  79. await program.rpc.proxyCreateAssociatedTokenAccount({
  80. accounts: {
  81. tokenAccount,
  82. mint: newMint,
  83. authority,
  84. systemProgram: anchor.web3.SystemProgram.programId,
  85. tokenProgram: tokenProgram.programId,
  86. associatedTokenProgram,
  87. },
  88. });
  89. const account = await getTokenAccount(provider, tokenAccount);
  90. assert.isTrue(account.amount.eq(new anchor.BN(0)));
  91. });
  92. it("Creates a mint", async () => {
  93. const authority = provider.wallet.publicKey;
  94. const [newMint] = anchor.web3.PublicKey.findProgramAddressSync(
  95. [
  96. authority.toBytes(),
  97. Buffer.from(name),
  98. Buffer.from("token-proxy-mint"),
  99. ],
  100. program.programId
  101. );
  102. await program.rpc.proxyCreateMint(name, {
  103. accounts: {
  104. authority,
  105. mint: newMint,
  106. systemProgram: anchor.web3.SystemProgram.programId,
  107. tokenProgram: tokenProgram.programId,
  108. },
  109. });
  110. });
  111. it("Mints a token", async () => {
  112. await program.rpc.proxyMintTo(new anchor.BN(1000), {
  113. accounts: {
  114. authority: provider.wallet.publicKey,
  115. mint,
  116. to: from,
  117. tokenProgram: tokenProgram.programId,
  118. },
  119. });
  120. const fromAccount = await getTokenAccount(provider, from);
  121. assert.isTrue(fromAccount.amount.eq(new anchor.BN(1000)));
  122. });
  123. it("Transfers a token", async () => {
  124. const preFromAccount = await getTokenAccount(provider, from);
  125. const preToAccount = await getTokenAccount(provider, to);
  126. const transferAmount = new anchor.BN(400);
  127. await program.rpc.proxyTransfer(transferAmount, {
  128. accounts: {
  129. authority: provider.wallet.publicKey,
  130. to,
  131. from,
  132. tokenProgram: tokenProgram.programId,
  133. },
  134. });
  135. const postFromAccount = await getTokenAccount(provider, from);
  136. const postToAccount = await getTokenAccount(provider, to);
  137. assert.isTrue(
  138. postFromAccount.amount.eq(preFromAccount.amount.sub(transferAmount))
  139. );
  140. assert.isTrue(
  141. postToAccount.amount.eq(preToAccount.amount.add(transferAmount))
  142. );
  143. });
  144. it("Transfers a token with optional accounts", async () => {
  145. const preFromAccount = await getTokenAccount(provider, from);
  146. const preToAccount = await getTokenAccount(provider, to);
  147. const transferAmount = new anchor.BN(10);
  148. await program.rpc.proxyOptionalTransfer(transferAmount, {
  149. accounts: {
  150. authority: provider.wallet.publicKey,
  151. to,
  152. from,
  153. mint,
  154. tokenProgram: tokenProgram.programId,
  155. },
  156. });
  157. const postFromAccount = await getTokenAccount(provider, from);
  158. const postToAccount = await getTokenAccount(provider, to);
  159. assert.isTrue(
  160. postFromAccount.amount.eq(preFromAccount.amount.sub(transferAmount))
  161. );
  162. assert.isTrue(
  163. postToAccount.amount.eq(preToAccount.amount.add(transferAmount))
  164. );
  165. });
  166. it("Does not transfer a token without optional accounts", async () => {
  167. const preFromAccount = await getTokenAccount(provider, from);
  168. const preToAccount = await getTokenAccount(provider, to);
  169. const optionalTransferIx = await program.methods
  170. .proxyOptionalTransfer(new anchor.BN(10))
  171. .accounts({
  172. authority: provider.wallet.publicKey,
  173. to,
  174. from,
  175. mint: null,
  176. tokenProgram: null,
  177. })
  178. .instruction();
  179. const tx = new anchor.web3.Transaction().add(optionalTransferIx);
  180. await provider.sendAndConfirm(tx);
  181. const postFromAccount = await getTokenAccount(provider, from);
  182. const postToAccount = await getTokenAccount(provider, to);
  183. assert.isTrue(postFromAccount.amount.eq(preFromAccount.amount));
  184. assert.isTrue(postToAccount.amount.eq(preToAccount.amount));
  185. });
  186. it("Burns a token", async () => {
  187. const preAccount = await getTokenAccount(provider, to);
  188. const burnAmount = new anchor.BN(300);
  189. await program.rpc.proxyBurn(burnAmount, {
  190. accounts: {
  191. authority: provider.wallet.publicKey,
  192. mint,
  193. from: to,
  194. tokenProgram: tokenProgram.programId,
  195. },
  196. });
  197. const postAccount = await getTokenAccount(provider, to);
  198. assert.isTrue(postAccount.amount.eq(preAccount.amount.sub(burnAmount)));
  199. });
  200. it("Set new mint authority", async () => {
  201. const newMintAuthority = anchor.web3.Keypair.generate();
  202. await program.rpc.proxySetAuthority(
  203. { mintTokens: {} },
  204. newMintAuthority.publicKey,
  205. {
  206. accounts: {
  207. accountOrMint: mint,
  208. currentAuthority: provider.wallet.publicKey,
  209. tokenProgram: tokenProgram.programId,
  210. },
  211. }
  212. );
  213. const mintInfo = await getMintInfo(provider, mint);
  214. assert.isTrue(
  215. mintInfo.mintAuthority.equals(newMintAuthority.publicKey)
  216. );
  217. });
  218. });
  219. });
  220. });
  221. // SPL token client boilerplate for test initialization. Everything below here is
  222. // mostly irrelevant to the point of the example.
  223. const serumCmn = require("@project-serum/common");
  224. async function getTokenAccount(provider, addr) {
  225. return await serumCmn.getTokenAccount(provider, addr);
  226. }
  227. async function getMintInfo(provider, mintAddr) {
  228. return await serumCmn.getMintInfo(provider, mintAddr);
  229. }
  230. async function createMint(tokenProgram) {
  231. const mint = anchor.web3.Keypair.generate();
  232. const authority = tokenProgram.provider.wallet.publicKey;
  233. const createMintIx = await tokenProgram.account.mint.createInstruction(mint);
  234. const initMintIx = await tokenProgram.methods
  235. .initializeMint2(0, authority, null)
  236. .accounts({ mint: mint.publicKey })
  237. .instruction();
  238. const tx = new anchor.web3.Transaction();
  239. tx.add(createMintIx, initMintIx);
  240. await tokenProgram.provider.sendAndConfirm(tx, [mint]);
  241. return mint.publicKey;
  242. }
  243. async function createTokenAccount(tokenProgram, mint, owner) {
  244. const vault = anchor.web3.Keypair.generate();
  245. const tx = new anchor.web3.Transaction();
  246. const createTokenAccountIx =
  247. await tokenProgram.account.account.createInstruction(vault);
  248. const initTokenAccountIx = await tokenProgram.methods
  249. .initializeAccount3(owner)
  250. .accounts({ account: vault.publicKey, mint })
  251. .instruction();
  252. tx.add(createTokenAccountIx, initTokenAccountIx);
  253. await tokenProgram.provider.sendAndConfirm(tx, [vault]);
  254. return vault.publicKey;
  255. }