123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603 |
- const anchor = require("@coral-xyz/anchor");
- const { assert } = require("chai");
- const {
- ASSOCIATED_TOKEN_PROGRAM_ID,
- TOKEN_PROGRAM_ID,
- Token,
- } = require("@solana/spl-token");
- const {
- sleep,
- getTokenAccount,
- createMint,
- createTokenAccount,
- } = require("./utils");
- const { token } = require("@coral-xyz/anchor/dist/cjs/utils");
- describe("ido-pool", () => {
- const provider = anchor.AnchorProvider.local();
- // Configure the client to use the local cluster.
- anchor.setProvider(provider);
- const program = anchor.workspace.IdoPool;
- // All mints default to 6 decimal places.
- const watermelonIdoAmount = new anchor.BN(5000000);
- // These are all of the variables we assume exist in the world already and
- // are available to the client.
- let usdcMintAccount = null;
- let usdcMint = null;
- let watermelonMintAccount = null;
- let watermelonMint = null;
- let idoAuthorityUsdc = null;
- let idoAuthorityWatermelon = null;
- it("Initializes the state-of-the-world", async () => {
- usdcMintAccount = await createMint(provider);
- watermelonMintAccount = await createMint(provider);
- usdcMint = usdcMintAccount.publicKey;
- watermelonMint = watermelonMintAccount.publicKey;
- idoAuthorityUsdc = await createTokenAccount(
- provider,
- usdcMint,
- provider.wallet.publicKey
- );
- idoAuthorityWatermelon = await createTokenAccount(
- provider,
- watermelonMint,
- provider.wallet.publicKey
- );
- // Mint Watermelon tokens that will be distributed from the IDO pool.
- await watermelonMintAccount.mintTo(
- idoAuthorityWatermelon,
- provider.wallet.publicKey,
- [],
- watermelonIdoAmount.toString()
- );
- idoAuthority_watermelon_account = await getTokenAccount(
- provider,
- idoAuthorityWatermelon
- );
- assert.isTrue(
- idoAuthority_watermelon_account.amount.eq(watermelonIdoAmount)
- );
- });
- // These are all variables the client will need to create in order to
- // initialize the IDO pool
- let idoTimes;
- let idoName = "test_ido";
- it("Initializes the IDO pool", async () => {
- let bumps = new PoolBumps();
- const [idoAccount, idoAccountBump] =
- await anchor.web3.PublicKey.findProgramAddress(
- [Buffer.from(idoName)],
- program.programId
- );
- bumps.idoAccount = idoAccountBump;
- const [redeemableMint, redeemableMintBump] =
- await anchor.web3.PublicKey.findProgramAddress(
- [Buffer.from(idoName), Buffer.from("redeemable_mint")],
- program.programId
- );
- bumps.redeemableMint = redeemableMintBump;
- const [poolWatermelon, poolWatermelonBump] =
- await anchor.web3.PublicKey.findProgramAddress(
- [Buffer.from(idoName), Buffer.from("pool_watermelon")],
- program.programId
- );
- bumps.poolWatermelon = poolWatermelonBump;
- const [poolUsdc, poolUsdcBump] =
- await anchor.web3.PublicKey.findProgramAddress(
- [Buffer.from(idoName), Buffer.from("pool_usdc")],
- program.programId
- );
- bumps.poolUsdc = poolUsdcBump;
- idoTimes = new IdoTimes();
- const nowBn = new anchor.BN(Date.now() / 1000);
- idoTimes.startIdo = nowBn.add(new anchor.BN(5));
- idoTimes.endDeposits = nowBn.add(new anchor.BN(10));
- idoTimes.endIdo = nowBn.add(new anchor.BN(15));
- idoTimes.endEscrow = nowBn.add(new anchor.BN(16));
- await program.rpc.initializePool(
- idoName,
- bumps,
- watermelonIdoAmount,
- idoTimes,
- {
- accounts: {
- idoAuthority: provider.wallet.publicKey,
- idoAuthorityWatermelon,
- idoAccount,
- watermelonMint,
- usdcMint,
- redeemableMint,
- poolWatermelon,
- poolUsdc,
- systemProgram: anchor.web3.SystemProgram.programId,
- tokenProgram: TOKEN_PROGRAM_ID,
- },
- }
- );
- idoAuthorityWatermelonAccount = await getTokenAccount(
- provider,
- idoAuthorityWatermelon
- );
- assert.isTrue(idoAuthorityWatermelonAccount.amount.eq(new anchor.BN(0)));
- });
- // We're going to need to start using the associated program account for creating token accounts
- // if not in testing, then definitely in production.
- let userUsdc = null;
- // 10 usdc
- const firstDeposit = new anchor.BN(10_000_349);
- it("Exchanges user USDC for redeemable tokens", async () => {
- // Wait until the IDO has opened.
- if (Date.now() < idoTimes.startIdo.toNumber() * 1000) {
- await sleep(idoTimes.startIdo.toNumber() * 1000 - Date.now() + 2000);
- }
- const [idoAccount] = await anchor.web3.PublicKey.findProgramAddress(
- [Buffer.from(idoName)],
- program.programId
- );
- const [redeemableMint] = await anchor.web3.PublicKey.findProgramAddress(
- [Buffer.from(idoName), Buffer.from("redeemable_mint")],
- program.programId
- );
- const [poolUsdc] = await anchor.web3.PublicKey.findProgramAddress(
- [Buffer.from(idoName), Buffer.from("pool_usdc")],
- program.programId
- );
- userUsdc = await Token.getAssociatedTokenAddress(
- ASSOCIATED_TOKEN_PROGRAM_ID,
- TOKEN_PROGRAM_ID,
- usdcMint,
- program.provider.wallet.publicKey
- );
- // Get the instructions to add to the RPC call
- let createUserUsdcInstr = Token.createAssociatedTokenAccountInstruction(
- ASSOCIATED_TOKEN_PROGRAM_ID,
- TOKEN_PROGRAM_ID,
- usdcMint,
- userUsdc,
- program.provider.wallet.publicKey,
- program.provider.wallet.publicKey
- );
- let createUserUsdcTrns = new anchor.web3.Transaction().add(
- createUserUsdcInstr
- );
- await provider.sendAndConfirm(createUserUsdcTrns);
- await usdcMintAccount.mintTo(
- userUsdc,
- provider.wallet.publicKey,
- [],
- firstDeposit.toString()
- );
- // Check if we inited correctly
- userUsdcAccount = await getTokenAccount(provider, userUsdc);
- assert.isTrue(userUsdcAccount.amount.eq(firstDeposit));
- const [userRedeemable] = await anchor.web3.PublicKey.findProgramAddress(
- [
- provider.wallet.publicKey.toBuffer(),
- Buffer.from(idoName),
- Buffer.from("user_redeemable"),
- ],
- program.programId
- );
- try {
- const tx = await program.rpc.exchangeUsdcForRedeemable(firstDeposit, {
- accounts: {
- userAuthority: provider.wallet.publicKey,
- userUsdc,
- userRedeemable,
- idoAccount,
- usdcMint,
- redeemableMint,
- watermelonMint,
- poolUsdc,
- tokenProgram: TOKEN_PROGRAM_ID,
- },
- instructions: [
- program.instruction.initUserRedeemable({
- accounts: {
- userAuthority: provider.wallet.publicKey,
- userRedeemable,
- idoAccount,
- redeemableMint,
- systemProgram: anchor.web3.SystemProgram.programId,
- tokenProgram: TOKEN_PROGRAM_ID,
- },
- }),
- ],
- });
- } catch (err) {
- console.log("This is the error message", err.toString());
- }
- poolUsdcAccount = await getTokenAccount(provider, poolUsdc);
- assert.isTrue(poolUsdcAccount.amount.eq(firstDeposit));
- userRedeemableAccount = await getTokenAccount(provider, userRedeemable);
- assert.isTrue(userRedeemableAccount.amount.eq(firstDeposit));
- });
- // 23 usdc
- const secondDeposit = new anchor.BN(23_000_672);
- let totalPoolUsdc, secondUserKeypair, secondUserUsdc;
- it("Exchanges a second users USDC for redeemable tokens", async () => {
- const [idoAccount] = await anchor.web3.PublicKey.findProgramAddress(
- [Buffer.from(idoName)],
- program.programId
- );
- const [redeemableMint] = await anchor.web3.PublicKey.findProgramAddress(
- [Buffer.from(idoName), Buffer.from("redeemable_mint")],
- program.programId
- );
- const [poolUsdc] = await anchor.web3.PublicKey.findProgramAddress(
- [Buffer.from(idoName), Buffer.from("pool_usdc")],
- program.programId
- );
- secondUserKeypair = anchor.web3.Keypair.generate();
- transferSolInstr = anchor.web3.SystemProgram.transfer({
- fromPubkey: provider.wallet.publicKey,
- lamports: 100_000_000_000, // 100 sol
- toPubkey: secondUserKeypair.publicKey,
- });
- secondUserUsdc = await Token.getAssociatedTokenAddress(
- ASSOCIATED_TOKEN_PROGRAM_ID,
- TOKEN_PROGRAM_ID,
- usdcMint,
- secondUserKeypair.publicKey
- );
- createSecondUserUsdcInstr = Token.createAssociatedTokenAccountInstruction(
- ASSOCIATED_TOKEN_PROGRAM_ID,
- TOKEN_PROGRAM_ID,
- usdcMint,
- secondUserUsdc,
- secondUserKeypair.publicKey,
- provider.wallet.publicKey
- );
- let createSecondUserUsdcTrns = new anchor.web3.Transaction();
- createSecondUserUsdcTrns.add(transferSolInstr);
- createSecondUserUsdcTrns.add(createSecondUserUsdcInstr);
- await provider.sendAndConfirm(createSecondUserUsdcTrns);
- await usdcMintAccount.mintTo(
- secondUserUsdc,
- provider.wallet.publicKey,
- [],
- secondDeposit.toString()
- );
- // Checking the transfer went through
- secondUserUsdcAccount = await getTokenAccount(provider, secondUserUsdc);
- assert.isTrue(secondUserUsdcAccount.amount.eq(secondDeposit));
- const [secondUserRedeemable] =
- await anchor.web3.PublicKey.findProgramAddress(
- [
- secondUserKeypair.publicKey.toBuffer(),
- Buffer.from(idoName),
- Buffer.from("user_redeemable"),
- ],
- program.programId
- );
- await program.rpc.exchangeUsdcForRedeemable(secondDeposit, {
- accounts: {
- userAuthority: secondUserKeypair.publicKey,
- userUsdc: secondUserUsdc,
- userRedeemable: secondUserRedeemable,
- idoAccount,
- usdcMint,
- redeemableMint,
- watermelonMint,
- poolUsdc,
- tokenProgram: TOKEN_PROGRAM_ID,
- },
- instructions: [
- program.instruction.initUserRedeemable({
- accounts: {
- userAuthority: secondUserKeypair.publicKey,
- userRedeemable: secondUserRedeemable,
- idoAccount,
- redeemableMint,
- systemProgram: anchor.web3.SystemProgram.programId,
- tokenProgram: TOKEN_PROGRAM_ID,
- },
- }),
- ],
- signers: [secondUserKeypair],
- });
- secondUserRedeemableAccount = await getTokenAccount(
- provider,
- secondUserRedeemable
- );
- assert.isTrue(secondUserRedeemableAccount.amount.eq(secondDeposit));
- totalPoolUsdc = firstDeposit.add(secondDeposit);
- poolUsdcAccount = await getTokenAccount(provider, poolUsdc);
- assert.isTrue(poolUsdcAccount.amount.eq(totalPoolUsdc));
- });
- const firstWithdrawal = new anchor.BN(2_000_000);
- it("Exchanges user Redeemable tokens for USDC", async () => {
- const [idoAccount] = await anchor.web3.PublicKey.findProgramAddress(
- [Buffer.from(idoName)],
- program.programId
- );
- const [redeemableMint] = await anchor.web3.PublicKey.findProgramAddress(
- [Buffer.from(idoName), Buffer.from("redeemable_mint")],
- program.programId
- );
- const [poolUsdc] = await anchor.web3.PublicKey.findProgramAddress(
- [Buffer.from(idoName), Buffer.from("pool_usdc")],
- program.programId
- );
- const [userRedeemable] = await anchor.web3.PublicKey.findProgramAddress(
- [
- provider.wallet.publicKey.toBuffer(),
- Buffer.from(idoName),
- Buffer.from("user_redeemable"),
- ],
- program.programId
- );
- const [escrowUsdc] = await anchor.web3.PublicKey.findProgramAddress(
- [
- provider.wallet.publicKey.toBuffer(),
- Buffer.from(idoName),
- Buffer.from("escrow_usdc"),
- ],
- program.programId
- );
- await program.rpc.exchangeRedeemableForUsdc(firstWithdrawal, {
- accounts: {
- userAuthority: provider.wallet.publicKey,
- escrowUsdc,
- userRedeemable,
- idoAccount,
- usdcMint,
- redeemableMint,
- watermelonMint,
- poolUsdc,
- tokenProgram: TOKEN_PROGRAM_ID,
- },
- instructions: [
- program.instruction.initEscrowUsdc({
- accounts: {
- userAuthority: provider.wallet.publicKey,
- escrowUsdc,
- idoAccount,
- usdcMint,
- systemProgram: anchor.web3.SystemProgram.programId,
- tokenProgram: TOKEN_PROGRAM_ID,
- },
- }),
- ],
- });
- totalPoolUsdc = totalPoolUsdc.sub(firstWithdrawal);
- poolUsdcAccount = await getTokenAccount(provider, poolUsdc);
- assert.isTrue(poolUsdcAccount.amount.eq(totalPoolUsdc));
- escrowUsdcAccount = await getTokenAccount(provider, escrowUsdc);
- assert.isTrue(escrowUsdcAccount.amount.eq(firstWithdrawal));
- });
- it("Exchanges user Redeemable tokens for watermelon", async () => {
- // Wait until the IDO has ended.
- if (Date.now() < idoTimes.endIdo.toNumber() * 1000) {
- await sleep(idoTimes.endIdo.toNumber() * 1000 - Date.now() + 3000);
- }
- const [idoAccount] = await anchor.web3.PublicKey.findProgramAddress(
- [Buffer.from(idoName)],
- program.programId
- );
- const [poolWatermelon] = await anchor.web3.PublicKey.findProgramAddress(
- [Buffer.from(idoName), Buffer.from("pool_watermelon")],
- program.programId
- );
- const [redeemableMint] = await anchor.web3.PublicKey.findProgramAddress(
- [Buffer.from(idoName), Buffer.from("redeemable_mint")],
- program.programId
- );
- const [userRedeemable] = await anchor.web3.PublicKey.findProgramAddress(
- [
- provider.wallet.publicKey.toBuffer(),
- Buffer.from(idoName),
- Buffer.from("user_redeemable"),
- ],
- program.programId
- );
- let firstUserRedeemable = firstDeposit.sub(firstWithdrawal);
- // TODO we've been lazy here and not used an ATA as we did with USDC
- userWatermelon = await createTokenAccount(
- provider,
- watermelonMint,
- provider.wallet.publicKey
- );
- await program.rpc.exchangeRedeemableForWatermelon(firstUserRedeemable, {
- accounts: {
- payer: provider.wallet.publicKey,
- userAuthority: provider.wallet.publicKey,
- userWatermelon,
- userRedeemable,
- idoAccount,
- watermelonMint,
- redeemableMint,
- poolWatermelon,
- tokenProgram: TOKEN_PROGRAM_ID,
- },
- });
- poolWatermelonAccount = await getTokenAccount(provider, poolWatermelon);
- let redeemedWatermelon = firstUserRedeemable
- .mul(watermelonIdoAmount)
- .div(totalPoolUsdc);
- let remainingWatermelon = watermelonIdoAmount.sub(redeemedWatermelon);
- assert.isTrue(poolWatermelonAccount.amount.eq(remainingWatermelon));
- userWatermelonAccount = await getTokenAccount(provider, userWatermelon);
- assert.isTrue(userWatermelonAccount.amount.eq(redeemedWatermelon));
- });
- it("Exchanges second user's Redeemable tokens for watermelon", async () => {
- const [idoAccount] = await anchor.web3.PublicKey.findProgramAddress(
- [Buffer.from(idoName)],
- program.programId
- );
- const [redeemableMint] = await anchor.web3.PublicKey.findProgramAddress(
- [Buffer.from(idoName), Buffer.from("redeemable_mint")],
- program.programId
- );
- const [secondUserRedeemable] =
- await anchor.web3.PublicKey.findProgramAddress(
- [
- secondUserKeypair.publicKey.toBuffer(),
- Buffer.from(idoName),
- Buffer.from("user_redeemable"),
- ],
- program.programId
- );
- const [poolWatermelon] = await anchor.web3.PublicKey.findProgramAddress(
- [Buffer.from(idoName), Buffer.from("pool_watermelon")],
- program.programId
- );
- secondUserWatermelon = await createTokenAccount(
- provider,
- watermelonMint,
- secondUserKeypair.publicKey
- );
- await program.rpc.exchangeRedeemableForWatermelon(secondDeposit, {
- accounts: {
- payer: provider.wallet.publicKey,
- userAuthority: secondUserKeypair.publicKey,
- userWatermelon: secondUserWatermelon,
- userRedeemable: secondUserRedeemable,
- idoAccount,
- watermelonMint,
- redeemableMint,
- poolWatermelon,
- tokenProgram: TOKEN_PROGRAM_ID,
- },
- });
- poolWatermelonAccount = await getTokenAccount(provider, poolWatermelon);
- assert.isTrue(poolWatermelonAccount.amount.eq(new anchor.BN(0)));
- });
- it("Withdraws total USDC from pool account", async () => {
- const [idoAccount] = await anchor.web3.PublicKey.findProgramAddress(
- [Buffer.from(idoName)],
- program.programId
- );
- const [poolUsdc] = await anchor.web3.PublicKey.findProgramAddress(
- [Buffer.from(idoName), Buffer.from("pool_usdc")],
- program.programId
- );
- await program.rpc.withdrawPoolUsdc({
- accounts: {
- idoAuthority: provider.wallet.publicKey,
- idoAuthorityUsdc,
- idoAccount,
- usdcMint,
- watermelonMint,
- poolUsdc,
- tokenProgram: TOKEN_PROGRAM_ID,
- },
- });
- poolUsdcAccount = await getTokenAccount(provider, poolUsdc);
- assert.isTrue(poolUsdcAccount.amount.eq(new anchor.BN(0)));
- idoAuthorityUsdcAccount = await getTokenAccount(provider, idoAuthorityUsdc);
- assert.isTrue(idoAuthorityUsdcAccount.amount.eq(totalPoolUsdc));
- });
- it("Withdraws USDC from the escrow account after waiting period is over", async () => {
- // Wait until the escrow period is over.
- if (Date.now() < idoTimes.endEscrow.toNumber() * 1000 + 1000) {
- await sleep(idoTimes.endEscrow.toNumber() * 1000 - Date.now() + 4000);
- }
- const [idoAccount] = await anchor.web3.PublicKey.findProgramAddress(
- [Buffer.from(idoName)],
- program.programId
- );
- const [escrowUsdc] = await anchor.web3.PublicKey.findProgramAddress(
- [
- provider.wallet.publicKey.toBuffer(),
- Buffer.from(idoName),
- Buffer.from("escrow_usdc"),
- ],
- program.programId
- );
- await program.rpc.withdrawFromEscrow(firstWithdrawal, {
- accounts: {
- payer: provider.wallet.publicKey,
- userAuthority: provider.wallet.publicKey,
- userUsdc,
- escrowUsdc,
- idoAccount,
- usdcMint,
- tokenProgram: TOKEN_PROGRAM_ID,
- },
- });
- userUsdcAccount = await getTokenAccount(provider, userUsdc);
- assert.isTrue(userUsdcAccount.amount.eq(firstWithdrawal));
- });
- function PoolBumps() {
- this.idoAccount;
- this.redeemableMint;
- this.poolWatermelon;
- this.poolUsdc;
- }
- function IdoTimes() {
- this.startIdo;
- this.endDeposts;
- this.endIdo;
- this.endEscrow;
- }
- });
|