123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317 |
- import { expect } from "chai"
- import hre, { ethers } from "hardhat"
- import { StandardMerkleTree } from "@openzeppelin/merkle-tree"
- import { time } from "@nomicfoundation/hardhat-toolbox/network-helpers"
- describe("Launchpad", () => {
- async function deployLaunchpadFixture() {
- const signers = await hre.ethers.getSigners()
- const owner = signers[0].address
- const Launchpad = await hre.ethers.getContractFactory("Launchpad")
- const Erc20Token = await hre.ethers.getContractFactory("BasicERC20")
- const launchpadContract = await Launchpad.deploy({
- from: owner,
- })
- const saleTokenContract = await Erc20Token.deploy("TestToken", "TT", owner, {
- from: owner,
- })
- const buyTokenContract = await Erc20Token.deploy("Usdt", "USDT", owner, {
- from: owner,
- })
- const launchpadAddress = await launchpadContract.getAddress()
- await saleTokenContract.mint(signers[0].address, ethers.parseEther("1000000"))
- await saleTokenContract.approve(launchpadAddress, ethers.MaxUint256)
- await buyTokenContract.mint(signers[1].address, ethers.parseEther("10000"))
- return {
- launchpadContract,
- saleTokenContract,
- buyTokenContract,
- owner,
- }
- }
- async function createNativeSaleFixture() {
- const { launchpadContract, saleTokenContract, owner } = await deployLaunchpadFixture()
- const balanceBefore = await saleTokenContract.balanceOf(owner)
- const saleTokenAddress = await saleTokenContract.getAddress()
- const startTime = Math.floor(Date.now() / 1000) + 60 // 1 minute from now
- const endTime = Math.floor(Date.now() / 1000) + 60 * 60 // 1 hour from now
- const paymentTokens = ethers.ZeroAddress
- const paymentTokenPrice = ethers.parseEther("1")
- await launchpadContract.createSale(
- saleTokenAddress,
- balanceBefore,
- startTime,
- endTime,
- paymentTokens,
- paymentTokenPrice
- )
- return {
- launchpadContract,
- saleTokenContract,
- owner,
- }
- }
- describe("init function and setters", () => {
- it("Should Allow Owner to create a sale use native token", async () => {
- const { launchpadContract, saleTokenContract, owner } = await deployLaunchpadFixture()
- const balanceBefore = await saleTokenContract.balanceOf(owner)
- const saleTokenAddress = await saleTokenContract.getAddress()
- const startTime = Math.floor(Date.now() / 1000) + 60 // 1 minute from now
- const endTime = Math.floor(Date.now() / 1000) + 60 * 60 // 1 hour from now
- const paymentTokens = ethers.ZeroAddress
- const paymentTokenPrice = ethers.parseEther("1") / 2500n
- await launchpadContract.createSale(
- saleTokenAddress,
- balanceBefore,
- startTime,
- endTime,
- paymentTokens,
- paymentTokenPrice
- )
- expect(await saleTokenContract.balanceOf(owner)).to.equal(0)
- expect(await launchpadContract.getSaleToken()).to.equal(saleTokenAddress)
- expect(await launchpadContract.getTotalTokens()).to.equal(balanceBefore)
- expect(await launchpadContract.getStartTime()).to.equal(startTime)
- expect(await launchpadContract.getEndTime()).to.equal(endTime)
- expect(await launchpadContract.getPaymentTokens()).to.equal(ethers.ZeroAddress)
- expect(await launchpadContract.getPaymentTokenPrice()).to.equal(paymentTokenPrice)
- expect(await launchpadContract.getTokensSold()).to.equal(0n)
- })
- it("should Allow Owner reset payment info", async () => {
- const { launchpadContract, saleTokenContract, owner } = await createNativeSaleFixture()
- const paymentTokenBefore = await launchpadContract.getPaymentTokens()
- const paymentTokenPriceBefore = await launchpadContract.getPaymentTokenPrice()
- expect(await launchpadContract.getPaymentTokens()).to.equal(paymentTokenBefore)
- expect(await launchpadContract.getPaymentTokenPrice()).to.equal(paymentTokenPriceBefore)
- const newPaymentToken = await saleTokenContract.getAddress()
- const newPaymentTokenPrice = ethers.parseEther("1")
- await launchpadContract.setSalePayment(newPaymentToken, newPaymentTokenPrice, {
- from: owner,
- })
- expect(await launchpadContract.getPaymentTokens()).to.equal(newPaymentToken)
- expect(await launchpadContract.getPaymentTokenPrice()).to.equal(newPaymentTokenPrice)
- })
- it("should Allow Owner set MerkleRoot", async () => {
- const { launchpadContract, owner } = await createNativeSaleFixture()
- const signers = await hre.ethers.getSigners()
- const values = [
- [signers[0].address, ethers.parseEther("50")],
- [signers[1].address, ethers.parseEther("150")],
- [signers[2].address, ethers.parseEther("100")],
- ]
- const tree = StandardMerkleTree.of(values, ["address", "uint256"])
- const root = tree.root
- await launchpadContract.setMerkleRoot(root, { from: owner })
- expect(await launchpadContract.merkleRoot()).to.equal(tree.root)
- })
- it("should Allow Owner transfer Owner", async () => {
- const { launchpadContract, owner } = await createNativeSaleFixture()
- const signers = await hre.ethers.getSigners()
- const newOwner = signers[1].address
- await launchpadContract.setOwner(newOwner, { from: owner })
- expect(await launchpadContract.owner()).to.equal(newOwner)
- })
- })
- describe("after init buy function", () => {
- it("should Allow user use native token buy token", async () => {
- const { launchpadContract, owner } = await createNativeSaleFixture()
- const signers = await hre.ethers.getSigners()
- const values = [
- [signers[0].address, ethers.parseEther("50")],
- [signers[1].address, ethers.parseEther("10")],
- [signers[2].address, ethers.parseEther("100")],
- [signers[3].address, ethers.parseEther("200")],
- [signers[4].address, ethers.parseEther("300")],
- ]
- const tree = StandardMerkleTree.of(values, ["address", "uint256"])
- const root = tree.root
- const buyAmount = ethers.parseEther("1")
- const buyer = signers[1]
- let maxBuyAmount = 0n
- let proof = [] as string[]
- let wrongProof = [] as string[]
- for (const [i, v] of tree.entries()) {
- if (v[0] === buyer.address) {
- proof = tree.getProof(i)
- if (typeof v[1] === "bigint") {
- maxBuyAmount = v[1]
- }
- }
- if (v[0] === signers[2].address) {
- wrongProof = tree.getProof(i)
- }
- }
- await expect(
- launchpadContract.connect(buyer).contributeETH(buyAmount, maxBuyAmount, proof)
- ).to.be.revertedWith("MerkleRoot not initialized")
- await launchpadContract.setMerkleRoot(root, { from: owner })
- await expect(
- launchpadContract.connect(buyer).contributeETH(buyAmount, maxBuyAmount, proof)
- ).to.be.revertedWith("Sale is not active")
- await time.increase(100)
- await expect(
- launchpadContract.connect(buyer).contributeETH(buyAmount + maxBuyAmount, maxBuyAmount, proof, {
- value: ethers.parseEther("0.5"),
- })
- ).to.be.revertedWith("Buy amount exceeds max buy amount")
- await expect(
- launchpadContract.connect(buyer).contributeETH(buyAmount, maxBuyAmount, wrongProof, {
- value: ethers.parseEther("0.5"),
- })
- ).to.be.revertedWith("Invalid proof")
- await expect(
- launchpadContract.connect(buyer).contributeETH(buyAmount, maxBuyAmount, proof, {
- value: ethers.parseEther("0.5"),
- })
- ).to.be.revertedWith("Not enough ETH sent")
- await expect(
- launchpadContract.connect(buyer).contributeETH(ethers.parseEther("50"), maxBuyAmount, proof, {
- value: ethers.parseEther("50"),
- })
- ).to.revertedWith("Buy amount exceeds max buy amount")
- //success once
- await expect(
- launchpadContract.connect(buyer).contributeETH(buyAmount, maxBuyAmount, proof, {
- value: ethers.parseEther("1"),
- })
- ).to.emit(launchpadContract, "Contributed")
- //make it success twice
- await expect(
- launchpadContract.connect(buyer).contributeETH(ethers.parseEther("4"), maxBuyAmount, proof, {
- value: ethers.parseEther("4"),
- })
- ).to.emit(launchpadContract, "Contributed")
- expect(await launchpadContract.getClaimableTokens(buyer.address)).to.equal(
- buyAmount + ethers.parseEther("4")
- )
- //failed by exceeding max buy amount
- await expect(
- launchpadContract.connect(buyer).contributeETH(ethers.parseEther("10"), maxBuyAmount, proof, {
- value: ethers.parseEther("10"),
- })
- ).to.be.revertedWith("Buy amount exceeds max buy amount")
- })
- it("should Allow user claim token after finished sale", async () => {
- const { launchpadContract, saleTokenContract, owner } = await createNativeSaleFixture()
- const signers = await hre.ethers.getSigners()
- const values = [
- [signers[0].address, ethers.parseEther("50")],
- [signers[1].address, ethers.parseEther("10")],
- [signers[2].address, ethers.parseEther("100")],
- [signers[3].address, ethers.parseEther("200")],
- [signers[4].address, ethers.parseEther("300")],
- ]
- const tree = StandardMerkleTree.of(values, ["address", "uint256"])
- const root = tree.root
- const buyAmount = ethers.parseEther("1")
- const buyer = signers[1]
- let maxBuyAmount = 0n
- let proof = [] as string[]
- for (const [i, v] of tree.entries()) {
- if (v[0] === buyer.address) {
- proof = tree.getProof(i)
- if (typeof v[1] === "bigint") {
- maxBuyAmount = v[1]
- }
- }
- }
- await launchpadContract.setMerkleRoot(root, { from: owner })
- await time.increase(100)
- await expect(
- launchpadContract.connect(buyer).contributeETH(buyAmount, maxBuyAmount, proof, {
- value: ethers.parseEther("1"),
- })
- ).to.emit(launchpadContract, "Contributed")
- expect(await launchpadContract.getClaimableTokens(buyer.address)).to.equal(buyAmount)
- await expect(launchpadContract.connect(buyer).claimTokens()).to.be.revertedWith(
- "Claiming tokens is not enabled"
- )
- expect(await launchpadContract.enableClaimTokens(Math.floor(Date.now() / 1000) + 60 * 60)).to.emit(
- launchpadContract,
- "EnableClaimToken"
- )
- await expect(launchpadContract.connect(buyer).claimTokens()).to.be.revertedWith(
- "Claiming tokens not started yet"
- )
- await time.increase(60 * 60 + 100) // increase time to finish sale
- await expect(launchpadContract.connect(signers[2]).claimTokens()).to.be.revertedWith("No tokens to claim")
- //Transfer token
- await expect(launchpadContract.connect(buyer).claimTokens()).to.emit(saleTokenContract, "Transfer")
- //Balance check
- expect(await saleTokenContract.balanceOf(buyer.address)).to.equal(buyAmount)
- })
- it("should Allow owner claim contribution token after finished sale", async () => {
- const { launchpadContract, saleTokenContract, owner } = await createNativeSaleFixture()
- const signers = await hre.ethers.getSigners()
- const values = [
- [signers[0].address, ethers.parseEther("50")],
- [signers[1].address, ethers.parseEther("10")],
- [signers[2].address, ethers.parseEther("100")],
- [signers[3].address, ethers.parseEther("200")],
- [signers[4].address, ethers.parseEther("300")],
- ]
- const tree = StandardMerkleTree.of(values, ["address", "uint256"])
- const root = tree.root
- const buyAmount = ethers.parseEther("1")
- const buyer = signers[1]
- let maxBuyAmount = 0n
- let proof = [] as string[]
- for (const [i, v] of tree.entries()) {
- if (v[0] === buyer.address) {
- proof = tree.getProof(i)
- if (typeof v[1] === "bigint") {
- maxBuyAmount = v[1]
- }
- }
- }
- await launchpadContract.setMerkleRoot(root, { from: owner })
- await time.increase(100)
- const launchpadAddress = await launchpadContract.getAddress()
- await expect(
- launchpadContract.connect(buyer).contributeETH(buyAmount, maxBuyAmount, proof, {
- value: ethers.parseEther("1"),
- })
- ).to.emit(launchpadContract, "Contributed")
- await expect(launchpadContract.withdrawPayments()).to.be.revertedWith("Sale is still active")
- await launchpadContract.enableClaimTokens(Math.floor(Date.now() / 1000) + 60 * 60)
- await time.increase(60 * 60 + 100) // increase time to finish sale
- await launchpadContract.connect(buyer).claimTokens()
- //todo 写不动了 接下来测试 非 owner withdraw token/钱 owner 的 withdraw token/钱
- await expect(launchpadContract.connect(signers[2]).withdrawPayments()).to.revertedWith(
- "Only owner can call this function"
- )
- await expect(launchpadContract.withdrawPayments()).to.emit(launchpadContract, "WithdrawPayments")
- expect(await ethers.provider.getBalance(launchpadAddress)).to.equal(0n)
- await expect(launchpadContract.connect(signers[2]).withdrawRemainingTokens()).to.revertedWith(
- "Only owner can call this function"
- )
- await expect(launchpadContract.withdrawRemainingTokens()).to.emit(
- launchpadContract,
- "WithdrawRemainingTokens"
- )
- expect(await saleTokenContract.balanceOf(launchpadAddress)).to.equal(0)
- })
- })
- })
|