ido-pool.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. const anchor = require("@project-serum/anchor");
  2. const assert = require("assert");
  3. const {
  4. TOKEN_PROGRAM_ID,
  5. sleep,
  6. getTokenAccount,
  7. createMint,
  8. createTokenAccount,
  9. mintToAccount,
  10. } = require("./utils");
  11. describe("ido-pool", () => {
  12. const provider = anchor.Provider.local();
  13. // Configure the client to use the local cluster.
  14. anchor.setProvider(provider);
  15. const program = anchor.workspace.IdoPool;
  16. // All mints default to 6 decimal places.
  17. const watermelonIdoAmount = new anchor.BN(5000000);
  18. // These are all of the variables we assume exist in the world already and
  19. // are available to the client.
  20. let usdcMint = null;
  21. let watermelonMint = null;
  22. let creatorUsdc = null;
  23. let creatorWatermelon = null;
  24. it("Initializes the state-of-the-world", async () => {
  25. usdcMint = await createMint(provider);
  26. watermelonMint = await createMint(provider);
  27. creatorUsdc = await createTokenAccount(
  28. provider,
  29. usdcMint,
  30. provider.wallet.publicKey
  31. );
  32. creatorWatermelon = await createTokenAccount(
  33. provider,
  34. watermelonMint,
  35. provider.wallet.publicKey
  36. );
  37. // Mint Watermelon tokens the will be distributed from the IDO pool.
  38. await mintToAccount(
  39. provider,
  40. watermelonMint,
  41. creatorWatermelon,
  42. watermelonIdoAmount,
  43. provider.wallet.publicKey
  44. );
  45. creator_watermelon_account = await getTokenAccount(
  46. provider,
  47. creatorWatermelon
  48. );
  49. assert.ok(creator_watermelon_account.amount.eq(watermelonIdoAmount));
  50. });
  51. // These are all variables the client will have to create to initialize the
  52. // IDO pool
  53. let poolSigner = null;
  54. let redeemableMint = null;
  55. let poolWatermelon = null;
  56. let poolUsdc = null;
  57. let poolAccount = null;
  58. let startIdoTs = null;
  59. let endDepositsTs = null;
  60. let endIdoTs = null;
  61. it("Initializes the IDO pool", async () => {
  62. // We use the watermelon mint address as the seed, could use something else though.
  63. const [_poolSigner, nonce] = await anchor.web3.PublicKey.findProgramAddress(
  64. [watermelonMint.toBuffer()],
  65. program.programId
  66. );
  67. poolSigner = _poolSigner;
  68. // Pool doesn't need a Redeemable SPL token account because it only
  69. // burns and mints redeemable tokens, it never stores them.
  70. redeemableMint = await createMint(provider, poolSigner);
  71. poolWatermelon = await createTokenAccount(
  72. provider,
  73. watermelonMint,
  74. poolSigner
  75. );
  76. poolUsdc = await createTokenAccount(provider, usdcMint, poolSigner);
  77. poolAccount = new anchor.web3.Account();
  78. const nowBn = new anchor.BN(Date.now() / 1000);
  79. startIdoTs = nowBn.add(new anchor.BN(5));
  80. endDepositsTs = nowBn.add(new anchor.BN(10));
  81. endIdoTs = nowBn.add(new anchor.BN(15));
  82. // Atomically create the new account and initialize it with the program.
  83. await program.rpc.initializePool(
  84. watermelonIdoAmount,
  85. nonce,
  86. startIdoTs,
  87. endDepositsTs,
  88. endIdoTs,
  89. {
  90. accounts: {
  91. poolAccount: poolAccount.publicKey,
  92. poolSigner,
  93. distributionAuthority: provider.wallet.publicKey,
  94. creatorWatermelon,
  95. redeemableMint,
  96. usdcMint,
  97. poolWatermelon,
  98. poolUsdc,
  99. tokenProgram: TOKEN_PROGRAM_ID,
  100. rent: anchor.web3.SYSVAR_RENT_PUBKEY,
  101. },
  102. signers: [poolAccount],
  103. instructions: [
  104. await program.account.poolAccount.createInstruction(poolAccount),
  105. ],
  106. }
  107. );
  108. creators_watermelon_account = await getTokenAccount(
  109. provider,
  110. creatorWatermelon
  111. );
  112. assert.ok(creators_watermelon_account.amount.eq(new anchor.BN(0)));
  113. });
  114. // We're going to need to start using the associated program account for creating token accounts
  115. // if not in testing, then definitely in production.
  116. let userUsdc = null;
  117. let userRedeemable = null;
  118. // 10 usdc
  119. const firstDeposit = new anchor.BN(10_000_349);
  120. it("Exchanges user USDC for redeemable tokens", async () => {
  121. // Wait until the IDO has opened.
  122. if (Date.now() < startIdoTs.toNumber() * 1000) {
  123. await sleep(startIdoTs.toNumber() * 1000 - Date.now() + 1000);
  124. }
  125. userUsdc = await createTokenAccount(
  126. provider,
  127. usdcMint,
  128. provider.wallet.publicKey
  129. );
  130. await mintToAccount(
  131. provider,
  132. usdcMint,
  133. userUsdc,
  134. firstDeposit,
  135. provider.wallet.publicKey
  136. );
  137. userRedeemable = await createTokenAccount(
  138. provider,
  139. redeemableMint,
  140. provider.wallet.publicKey
  141. );
  142. try {
  143. const tx = await program.rpc.exchangeUsdcForRedeemable(firstDeposit, {
  144. accounts: {
  145. poolAccount: poolAccount.publicKey,
  146. poolSigner,
  147. redeemableMint,
  148. poolUsdc,
  149. userAuthority: provider.wallet.publicKey,
  150. userUsdc,
  151. userRedeemable,
  152. tokenProgram: TOKEN_PROGRAM_ID,
  153. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  154. },
  155. });
  156. } catch (err) {
  157. console.log("This is the error message", err.toString());
  158. }
  159. poolUsdcAccount = await getTokenAccount(provider, poolUsdc);
  160. assert.ok(poolUsdcAccount.amount.eq(firstDeposit));
  161. userRedeemableAccount = await getTokenAccount(provider, userRedeemable);
  162. assert.ok(userRedeemableAccount.amount.eq(firstDeposit));
  163. });
  164. // 23 usdc
  165. const secondDeposit = new anchor.BN(23_000_672);
  166. let totalPoolUsdc = null;
  167. it("Exchanges a second users USDC for redeemable tokens", async () => {
  168. secondUserUsdc = await createTokenAccount(
  169. provider,
  170. usdcMint,
  171. provider.wallet.publicKey
  172. );
  173. await mintToAccount(
  174. provider,
  175. usdcMint,
  176. secondUserUsdc,
  177. secondDeposit,
  178. provider.wallet.publicKey
  179. );
  180. secondUserRedeemable = await createTokenAccount(
  181. provider,
  182. redeemableMint,
  183. provider.wallet.publicKey
  184. );
  185. await program.rpc.exchangeUsdcForRedeemable(secondDeposit, {
  186. accounts: {
  187. poolAccount: poolAccount.publicKey,
  188. poolSigner,
  189. redeemableMint,
  190. poolUsdc,
  191. userAuthority: provider.wallet.publicKey,
  192. userUsdc: secondUserUsdc,
  193. userRedeemable: secondUserRedeemable,
  194. tokenProgram: TOKEN_PROGRAM_ID,
  195. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  196. },
  197. });
  198. totalPoolUsdc = firstDeposit.add(secondDeposit);
  199. poolUsdcAccount = await getTokenAccount(provider, poolUsdc);
  200. assert.ok(poolUsdcAccount.amount.eq(totalPoolUsdc));
  201. secondUserRedeemableAccount = await getTokenAccount(
  202. provider,
  203. secondUserRedeemable
  204. );
  205. assert.ok(secondUserRedeemableAccount.amount.eq(secondDeposit));
  206. });
  207. const firstWithdrawal = new anchor.BN(2_000_000);
  208. it("Exchanges user Redeemable tokens for USDC", async () => {
  209. await program.rpc.exchangeRedeemableForUsdc(firstWithdrawal, {
  210. accounts: {
  211. poolAccount: poolAccount.publicKey,
  212. poolSigner,
  213. redeemableMint,
  214. poolUsdc,
  215. userAuthority: provider.wallet.publicKey,
  216. userUsdc,
  217. userRedeemable,
  218. tokenProgram: TOKEN_PROGRAM_ID,
  219. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  220. },
  221. });
  222. totalPoolUsdc = totalPoolUsdc.sub(firstWithdrawal);
  223. poolUsdcAccount = await getTokenAccount(provider, poolUsdc);
  224. assert.ok(poolUsdcAccount.amount.eq(totalPoolUsdc));
  225. userUsdcAccount = await getTokenAccount(provider, userUsdc);
  226. assert.ok(userUsdcAccount.amount.eq(firstWithdrawal));
  227. });
  228. it("Exchanges user Redeemable tokens for watermelon", async () => {
  229. // Wait until the IDO has opened.
  230. if (Date.now() < endIdoTs.toNumber() * 1000) {
  231. await sleep(endIdoTs.toNumber() * 1000 - Date.now() + 2000);
  232. }
  233. let firstUserRedeemable = firstDeposit.sub(firstWithdrawal);
  234. userWatermelon = await createTokenAccount(
  235. provider,
  236. watermelonMint,
  237. provider.wallet.publicKey
  238. );
  239. await program.rpc.exchangeRedeemableForWatermelon(firstUserRedeemable, {
  240. accounts: {
  241. poolAccount: poolAccount.publicKey,
  242. poolSigner,
  243. redeemableMint,
  244. poolWatermelon,
  245. userAuthority: provider.wallet.publicKey,
  246. userWatermelon,
  247. userRedeemable,
  248. tokenProgram: TOKEN_PROGRAM_ID,
  249. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  250. },
  251. });
  252. poolWatermelonAccount = await getTokenAccount(provider, poolWatermelon);
  253. let redeemedWatermelon = firstUserRedeemable
  254. .mul(watermelonIdoAmount)
  255. .div(totalPoolUsdc);
  256. let remainingWatermelon = watermelonIdoAmount.sub(redeemedWatermelon);
  257. assert.ok(poolWatermelonAccount.amount.eq(remainingWatermelon));
  258. userWatermelonAccount = await getTokenAccount(provider, userWatermelon);
  259. assert.ok(userWatermelonAccount.amount.eq(redeemedWatermelon));
  260. });
  261. it("Exchanges second users Redeemable tokens for watermelon", async () => {
  262. secondUserWatermelon = await createTokenAccount(
  263. provider,
  264. watermelonMint,
  265. provider.wallet.publicKey
  266. );
  267. await program.rpc.exchangeRedeemableForWatermelon(secondDeposit, {
  268. accounts: {
  269. poolAccount: poolAccount.publicKey,
  270. poolSigner,
  271. redeemableMint,
  272. poolWatermelon,
  273. userAuthority: provider.wallet.publicKey,
  274. userWatermelon: secondUserWatermelon,
  275. userRedeemable: secondUserRedeemable,
  276. tokenProgram: TOKEN_PROGRAM_ID,
  277. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  278. },
  279. });
  280. poolWatermelonAccount = await getTokenAccount(provider, poolWatermelon);
  281. assert.ok(poolWatermelonAccount.amount.eq(new anchor.BN(0)));
  282. });
  283. it("Withdraws total USDC from pool account", async () => {
  284. await program.rpc.withdrawPoolUsdc({
  285. accounts: {
  286. poolAccount: poolAccount.publicKey,
  287. poolSigner,
  288. distributionAuthority: provider.wallet.publicKey,
  289. creatorUsdc,
  290. poolUsdc,
  291. tokenProgram: TOKEN_PROGRAM_ID,
  292. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  293. },
  294. });
  295. poolUsdcAccount = await getTokenAccount(provider, poolUsdc);
  296. assert.ok(poolUsdcAccount.amount.eq(new anchor.BN(0)));
  297. creatorUsdcAccount = await getTokenAccount(provider, creatorUsdc);
  298. assert.ok(creatorUsdcAccount.amount.eq(totalPoolUsdc));
  299. });
  300. });