anchor/examples/ido-pool/tests/ido-pool.js

335 lines
10 KiB
JavaScript

const anchor = require("@project-serum/anchor");
const assert = require("assert");
const {
TOKEN_PROGRAM_ID,
sleep,
getTokenAccount,
createMint,
createTokenAccount,
mintToAccount,
} = require("./utils");
describe("ido-pool", () => {
const provider = anchor.Provider.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 usdcMint = null;
let watermelonMint = null;
let creatorUsdc = null;
let creatorWatermelon = null;
it("Initializes the state-of-the-world", async () => {
usdcMint = await createMint(provider);
watermelonMint = await createMint(provider);
creatorUsdc = await createTokenAccount(
provider,
usdcMint,
provider.wallet.publicKey
);
creatorWatermelon = await createTokenAccount(
provider,
watermelonMint,
provider.wallet.publicKey
);
// Mint Watermelon tokens the will be distributed from the IDO pool.
await mintToAccount(
provider,
watermelonMint,
creatorWatermelon,
watermelonIdoAmount,
provider.wallet.publicKey
);
creator_watermelon_account = await getTokenAccount(
provider,
creatorWatermelon
);
assert.ok(creator_watermelon_account.amount.eq(watermelonIdoAmount));
});
// These are all variables the client will have to create to initialize the
// IDO pool
let poolSigner = null;
let redeemableMint = null;
let poolWatermelon = null;
let poolUsdc = null;
let poolAccount = null;
let startIdoTs = null;
let endDepositsTs = null;
let endIdoTs = null;
it("Initializes the IDO pool", async () => {
// We use the watermelon mint address as the seed, could use something else though.
const [_poolSigner, nonce] = await anchor.web3.PublicKey.findProgramAddress(
[watermelonMint.toBuffer()],
program.programId
);
poolSigner = _poolSigner;
// Pool doesn't need a Redeemable SPL token account because it only
// burns and mints redeemable tokens, it never stores them.
redeemableMint = await createMint(provider, poolSigner);
poolWatermelon = await createTokenAccount(
provider,
watermelonMint,
poolSigner
);
poolUsdc = await createTokenAccount(provider, usdcMint, poolSigner);
poolAccount = anchor.web3.Keypair.generate();
const nowBn = new anchor.BN(Date.now() / 1000);
startIdoTs = nowBn.add(new anchor.BN(5));
endDepositsTs = nowBn.add(new anchor.BN(10));
endIdoTs = nowBn.add(new anchor.BN(15));
// Atomically create the new account and initialize it with the program.
await program.rpc.initializePool(
watermelonIdoAmount,
nonce,
startIdoTs,
endDepositsTs,
endIdoTs,
{
accounts: {
poolAccount: poolAccount.publicKey,
poolSigner,
distributionAuthority: provider.wallet.publicKey,
creatorWatermelon,
redeemableMint,
usdcMint,
poolWatermelon,
poolUsdc,
tokenProgram: TOKEN_PROGRAM_ID,
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
},
signers: [poolAccount],
instructions: [
await program.account.poolAccount.createInstruction(poolAccount),
],
}
);
creators_watermelon_account = await getTokenAccount(
provider,
creatorWatermelon
);
assert.ok(creators_watermelon_account.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;
let userRedeemable = 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() < startIdoTs.toNumber() * 1000) {
await sleep(startIdoTs.toNumber() * 1000 - Date.now() + 1000);
}
userUsdc = await createTokenAccount(
provider,
usdcMint,
provider.wallet.publicKey
);
await mintToAccount(
provider,
usdcMint,
userUsdc,
firstDeposit,
provider.wallet.publicKey
);
userRedeemable = await createTokenAccount(
provider,
redeemableMint,
provider.wallet.publicKey
);
try {
const tx = await program.rpc.exchangeUsdcForRedeemable(firstDeposit, {
accounts: {
poolAccount: poolAccount.publicKey,
poolSigner,
redeemableMint,
poolUsdc,
userAuthority: provider.wallet.publicKey,
userUsdc,
userRedeemable,
tokenProgram: TOKEN_PROGRAM_ID,
clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
},
});
} catch (err) {
console.log("This is the error message", err.toString());
}
poolUsdcAccount = await getTokenAccount(provider, poolUsdc);
assert.ok(poolUsdcAccount.amount.eq(firstDeposit));
userRedeemableAccount = await getTokenAccount(provider, userRedeemable);
assert.ok(userRedeemableAccount.amount.eq(firstDeposit));
});
// 23 usdc
const secondDeposit = new anchor.BN(23_000_672);
let totalPoolUsdc = null;
it("Exchanges a second users USDC for redeemable tokens", async () => {
secondUserUsdc = await createTokenAccount(
provider,
usdcMint,
provider.wallet.publicKey
);
await mintToAccount(
provider,
usdcMint,
secondUserUsdc,
secondDeposit,
provider.wallet.publicKey
);
secondUserRedeemable = await createTokenAccount(
provider,
redeemableMint,
provider.wallet.publicKey
);
await program.rpc.exchangeUsdcForRedeemable(secondDeposit, {
accounts: {
poolAccount: poolAccount.publicKey,
poolSigner,
redeemableMint,
poolUsdc,
userAuthority: provider.wallet.publicKey,
userUsdc: secondUserUsdc,
userRedeemable: secondUserRedeemable,
tokenProgram: TOKEN_PROGRAM_ID,
clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
},
});
totalPoolUsdc = firstDeposit.add(secondDeposit);
poolUsdcAccount = await getTokenAccount(provider, poolUsdc);
assert.ok(poolUsdcAccount.amount.eq(totalPoolUsdc));
secondUserRedeemableAccount = await getTokenAccount(
provider,
secondUserRedeemable
);
assert.ok(secondUserRedeemableAccount.amount.eq(secondDeposit));
});
const firstWithdrawal = new anchor.BN(2_000_000);
it("Exchanges user Redeemable tokens for USDC", async () => {
await program.rpc.exchangeRedeemableForUsdc(firstWithdrawal, {
accounts: {
poolAccount: poolAccount.publicKey,
poolSigner,
redeemableMint,
poolUsdc,
userAuthority: provider.wallet.publicKey,
userUsdc,
userRedeemable,
tokenProgram: TOKEN_PROGRAM_ID,
clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
},
});
totalPoolUsdc = totalPoolUsdc.sub(firstWithdrawal);
poolUsdcAccount = await getTokenAccount(provider, poolUsdc);
assert.ok(poolUsdcAccount.amount.eq(totalPoolUsdc));
userUsdcAccount = await getTokenAccount(provider, userUsdc);
assert.ok(userUsdcAccount.amount.eq(firstWithdrawal));
});
it("Exchanges user Redeemable tokens for watermelon", async () => {
// Wait until the IDO has opened.
if (Date.now() < endIdoTs.toNumber() * 1000) {
await sleep(endIdoTs.toNumber() * 1000 - Date.now() + 2000);
}
let firstUserRedeemable = firstDeposit.sub(firstWithdrawal);
userWatermelon = await createTokenAccount(
provider,
watermelonMint,
provider.wallet.publicKey
);
await program.rpc.exchangeRedeemableForWatermelon(firstUserRedeemable, {
accounts: {
poolAccount: poolAccount.publicKey,
poolSigner,
redeemableMint,
poolWatermelon,
userAuthority: provider.wallet.publicKey,
userWatermelon,
userRedeemable,
tokenProgram: TOKEN_PROGRAM_ID,
clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
},
});
poolWatermelonAccount = await getTokenAccount(provider, poolWatermelon);
let redeemedWatermelon = firstUserRedeemable
.mul(watermelonIdoAmount)
.div(totalPoolUsdc);
let remainingWatermelon = watermelonIdoAmount.sub(redeemedWatermelon);
assert.ok(poolWatermelonAccount.amount.eq(remainingWatermelon));
userWatermelonAccount = await getTokenAccount(provider, userWatermelon);
assert.ok(userWatermelonAccount.amount.eq(redeemedWatermelon));
});
it("Exchanges second users Redeemable tokens for watermelon", async () => {
secondUserWatermelon = await createTokenAccount(
provider,
watermelonMint,
provider.wallet.publicKey
);
await program.rpc.exchangeRedeemableForWatermelon(secondDeposit, {
accounts: {
poolAccount: poolAccount.publicKey,
poolSigner,
redeemableMint,
poolWatermelon,
userAuthority: provider.wallet.publicKey,
userWatermelon: secondUserWatermelon,
userRedeemable: secondUserRedeemable,
tokenProgram: TOKEN_PROGRAM_ID,
clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
},
});
poolWatermelonAccount = await getTokenAccount(provider, poolWatermelon);
assert.ok(poolWatermelonAccount.amount.eq(new anchor.BN(0)));
});
it("Withdraws total USDC from pool account", async () => {
await program.rpc.withdrawPoolUsdc({
accounts: {
poolAccount: poolAccount.publicKey,
poolSigner,
distributionAuthority: provider.wallet.publicKey,
creatorUsdc,
poolUsdc,
tokenProgram: TOKEN_PROGRAM_ID,
clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
},
});
poolUsdcAccount = await getTokenAccount(provider, poolUsdc);
assert.ok(poolUsdcAccount.amount.eq(new anchor.BN(0)));
creatorUsdcAccount = await getTokenAccount(provider, creatorUsdc);
assert.ok(creatorUsdcAccount.amount.eq(totalPoolUsdc));
});
});