LaunchpadOwner.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. import { expect } from "chai"
  2. import hre, { ethers } from "hardhat"
  3. import { StandardMerkleTree } from "@openzeppelin/merkle-tree"
  4. import { time } from "@nomicfoundation/hardhat-toolbox/network-helpers"
  5. describe("Launchpad", () => {
  6. async function deployLaunchpadFixture() {
  7. const signers = await hre.ethers.getSigners()
  8. const owner = signers[0].address
  9. const Launchpad = await hre.ethers.getContractFactory("Launchpad")
  10. const Erc20Token = await hre.ethers.getContractFactory("BasicERC20")
  11. const launchpadContract = await Launchpad.deploy({
  12. from: owner,
  13. })
  14. const saleTokenContract = await Erc20Token.deploy("TestToken", "TT", owner, {
  15. from: owner,
  16. })
  17. const buyTokenContract = await Erc20Token.deploy("Usdt", "USDT", owner, {
  18. from: owner,
  19. })
  20. const launchpadAddress = await launchpadContract.getAddress()
  21. await saleTokenContract.mint(signers[0].address, ethers.parseEther("1000000"))
  22. await saleTokenContract.approve(launchpadAddress, ethers.MaxUint256)
  23. await buyTokenContract.mint(signers[1].address, ethers.parseEther("10000"))
  24. return {
  25. launchpadContract,
  26. saleTokenContract,
  27. buyTokenContract,
  28. owner,
  29. }
  30. }
  31. async function createNativeSaleFixture() {
  32. const { launchpadContract, saleTokenContract, owner } = await deployLaunchpadFixture()
  33. const balanceBefore = await saleTokenContract.balanceOf(owner)
  34. const saleTokenAddress = await saleTokenContract.getAddress()
  35. const startTime = Math.floor(Date.now() / 1000) + 60 // 1 minute from now
  36. const endTime = Math.floor(Date.now() / 1000) + 60 * 60 // 1 hour from now
  37. const paymentTokens = ethers.ZeroAddress
  38. const paymentTokenPrice = ethers.parseEther("1")
  39. await launchpadContract.createSale(
  40. saleTokenAddress,
  41. balanceBefore,
  42. startTime,
  43. endTime,
  44. paymentTokens,
  45. paymentTokenPrice
  46. )
  47. return {
  48. launchpadContract,
  49. saleTokenContract,
  50. owner,
  51. }
  52. }
  53. describe("init function and setters", () => {
  54. it("Should Allow Owner to create a sale use native token", async () => {
  55. const { launchpadContract, saleTokenContract, owner } = await deployLaunchpadFixture()
  56. const balanceBefore = await saleTokenContract.balanceOf(owner)
  57. const saleTokenAddress = await saleTokenContract.getAddress()
  58. const startTime = Math.floor(Date.now() / 1000) + 60 // 1 minute from now
  59. const endTime = Math.floor(Date.now() / 1000) + 60 * 60 // 1 hour from now
  60. const paymentTokens = ethers.ZeroAddress
  61. const paymentTokenPrice = ethers.parseEther("1") / 2500n
  62. await launchpadContract.createSale(
  63. saleTokenAddress,
  64. balanceBefore,
  65. startTime,
  66. endTime,
  67. paymentTokens,
  68. paymentTokenPrice
  69. )
  70. expect(await saleTokenContract.balanceOf(owner)).to.equal(0)
  71. expect(await launchpadContract.getSaleToken()).to.equal(saleTokenAddress)
  72. expect(await launchpadContract.getTotalTokens()).to.equal(balanceBefore)
  73. expect(await launchpadContract.getStartTime()).to.equal(startTime)
  74. expect(await launchpadContract.getEndTime()).to.equal(endTime)
  75. expect(await launchpadContract.getPaymentTokens()).to.equal(ethers.ZeroAddress)
  76. expect(await launchpadContract.getPaymentTokenPrice()).to.equal(paymentTokenPrice)
  77. expect(await launchpadContract.getTokensSold()).to.equal(0n)
  78. })
  79. it("should Allow Owner reset payment info", async () => {
  80. const { launchpadContract, saleTokenContract, owner } = await createNativeSaleFixture()
  81. const paymentTokenBefore = await launchpadContract.getPaymentTokens()
  82. const paymentTokenPriceBefore = await launchpadContract.getPaymentTokenPrice()
  83. expect(await launchpadContract.getPaymentTokens()).to.equal(paymentTokenBefore)
  84. expect(await launchpadContract.getPaymentTokenPrice()).to.equal(paymentTokenPriceBefore)
  85. const newPaymentToken = await saleTokenContract.getAddress()
  86. const newPaymentTokenPrice = ethers.parseEther("1")
  87. await launchpadContract.setSalePayment(newPaymentToken, newPaymentTokenPrice, {
  88. from: owner,
  89. })
  90. expect(await launchpadContract.getPaymentTokens()).to.equal(newPaymentToken)
  91. expect(await launchpadContract.getPaymentTokenPrice()).to.equal(newPaymentTokenPrice)
  92. })
  93. it("should Allow Owner set MerkleRoot", async () => {
  94. const { launchpadContract, owner } = await createNativeSaleFixture()
  95. const signers = await hre.ethers.getSigners()
  96. const values = [
  97. [signers[0].address, ethers.parseEther("50")],
  98. [signers[1].address, ethers.parseEther("150")],
  99. [signers[2].address, ethers.parseEther("100")],
  100. ]
  101. const tree = StandardMerkleTree.of(values, ["address", "uint256"])
  102. const root = tree.root
  103. await launchpadContract.setMerkleRoot(root, { from: owner })
  104. expect(await launchpadContract.merkleRoot()).to.equal(tree.root)
  105. })
  106. it("should Allow Owner transfer Owner", async () => {
  107. const { launchpadContract, owner } = await createNativeSaleFixture()
  108. const signers = await hre.ethers.getSigners()
  109. const newOwner = signers[1].address
  110. await launchpadContract.setOwner(newOwner, { from: owner })
  111. expect(await launchpadContract.owner()).to.equal(newOwner)
  112. })
  113. })
  114. describe("after init buy function", () => {
  115. it("should Allow user use native token buy token", async () => {
  116. const { launchpadContract, owner } = await createNativeSaleFixture()
  117. const signers = await hre.ethers.getSigners()
  118. const values = [
  119. [signers[0].address, ethers.parseEther("50")],
  120. [signers[1].address, ethers.parseEther("10")],
  121. [signers[2].address, ethers.parseEther("100")],
  122. [signers[3].address, ethers.parseEther("200")],
  123. [signers[4].address, ethers.parseEther("300")],
  124. ]
  125. const tree = StandardMerkleTree.of(values, ["address", "uint256"])
  126. const root = tree.root
  127. const buyAmount = ethers.parseEther("1")
  128. const buyer = signers[1]
  129. let maxBuyAmount = 0n
  130. let proof = [] as string[]
  131. let wrongProof = [] as string[]
  132. for (const [i, v] of tree.entries()) {
  133. if (v[0] === buyer.address) {
  134. proof = tree.getProof(i)
  135. if (typeof v[1] === "bigint") {
  136. maxBuyAmount = v[1]
  137. }
  138. }
  139. if (v[0] === signers[2].address) {
  140. wrongProof = tree.getProof(i)
  141. }
  142. }
  143. await expect(
  144. launchpadContract.connect(buyer).contributeETH(buyAmount, maxBuyAmount, proof)
  145. ).to.be.revertedWith("MerkleRoot not initialized")
  146. await launchpadContract.setMerkleRoot(root, { from: owner })
  147. await expect(
  148. launchpadContract.connect(buyer).contributeETH(buyAmount, maxBuyAmount, proof)
  149. ).to.be.revertedWith("Sale is not active")
  150. await time.increase(100)
  151. await expect(
  152. launchpadContract.connect(buyer).contributeETH(buyAmount + maxBuyAmount, maxBuyAmount, proof, {
  153. value: ethers.parseEther("0.5"),
  154. })
  155. ).to.be.revertedWith("Buy amount exceeds max buy amount")
  156. await expect(
  157. launchpadContract.connect(buyer).contributeETH(buyAmount, maxBuyAmount, wrongProof, {
  158. value: ethers.parseEther("0.5"),
  159. })
  160. ).to.be.revertedWith("Invalid proof")
  161. await expect(
  162. launchpadContract.connect(buyer).contributeETH(buyAmount, maxBuyAmount, proof, {
  163. value: ethers.parseEther("0.5"),
  164. })
  165. ).to.be.revertedWith("Not enough ETH sent")
  166. await expect(
  167. launchpadContract.connect(buyer).contributeETH(ethers.parseEther("50"), maxBuyAmount, proof, {
  168. value: ethers.parseEther("50"),
  169. })
  170. ).to.revertedWith("Buy amount exceeds max buy amount")
  171. //success once
  172. await expect(
  173. launchpadContract.connect(buyer).contributeETH(buyAmount, maxBuyAmount, proof, {
  174. value: ethers.parseEther("1"),
  175. })
  176. ).to.emit(launchpadContract, "Contributed")
  177. //make it success twice
  178. await expect(
  179. launchpadContract.connect(buyer).contributeETH(ethers.parseEther("4"), maxBuyAmount, proof, {
  180. value: ethers.parseEther("4"),
  181. })
  182. ).to.emit(launchpadContract, "Contributed")
  183. expect(await launchpadContract.getClaimableTokens(buyer.address)).to.equal(
  184. buyAmount + ethers.parseEther("4")
  185. )
  186. //failed by exceeding max buy amount
  187. await expect(
  188. launchpadContract.connect(buyer).contributeETH(ethers.parseEther("10"), maxBuyAmount, proof, {
  189. value: ethers.parseEther("10"),
  190. })
  191. ).to.be.revertedWith("Buy amount exceeds max buy amount")
  192. })
  193. it("should Allow user claim token after finished sale", async () => {
  194. const { launchpadContract, saleTokenContract, owner } = await createNativeSaleFixture()
  195. const signers = await hre.ethers.getSigners()
  196. const values = [
  197. [signers[0].address, ethers.parseEther("50")],
  198. [signers[1].address, ethers.parseEther("10")],
  199. [signers[2].address, ethers.parseEther("100")],
  200. [signers[3].address, ethers.parseEther("200")],
  201. [signers[4].address, ethers.parseEther("300")],
  202. ]
  203. const tree = StandardMerkleTree.of(values, ["address", "uint256"])
  204. const root = tree.root
  205. const buyAmount = ethers.parseEther("1")
  206. const buyer = signers[1]
  207. let maxBuyAmount = 0n
  208. let proof = [] as string[]
  209. for (const [i, v] of tree.entries()) {
  210. if (v[0] === buyer.address) {
  211. proof = tree.getProof(i)
  212. if (typeof v[1] === "bigint") {
  213. maxBuyAmount = v[1]
  214. }
  215. }
  216. }
  217. await launchpadContract.setMerkleRoot(root, { from: owner })
  218. await time.increase(100)
  219. await expect(
  220. launchpadContract.connect(buyer).contributeETH(buyAmount, maxBuyAmount, proof, {
  221. value: ethers.parseEther("1"),
  222. })
  223. ).to.emit(launchpadContract, "Contributed")
  224. expect(await launchpadContract.getClaimableTokens(buyer.address)).to.equal(buyAmount)
  225. await expect(launchpadContract.connect(buyer).claimTokens()).to.be.revertedWith(
  226. "Claiming tokens is not enabled"
  227. )
  228. expect(await launchpadContract.enableClaimTokens(Math.floor(Date.now() / 1000) + 60 * 60)).to.emit(
  229. launchpadContract,
  230. "EnableClaimToken"
  231. )
  232. await expect(launchpadContract.connect(buyer).claimTokens()).to.be.revertedWith(
  233. "Claiming tokens not started yet"
  234. )
  235. await time.increase(60 * 60 + 100) // increase time to finish sale
  236. await expect(launchpadContract.connect(signers[2]).claimTokens()).to.be.revertedWith("No tokens to claim")
  237. //Transfer token
  238. await expect(launchpadContract.connect(buyer).claimTokens()).to.emit(saleTokenContract, "Transfer")
  239. //Balance check
  240. expect(await saleTokenContract.balanceOf(buyer.address)).to.equal(buyAmount)
  241. })
  242. it("should Allow owner claim contribution token after finished sale", async () => {
  243. const { launchpadContract, saleTokenContract, owner } = await createNativeSaleFixture()
  244. const signers = await hre.ethers.getSigners()
  245. const values = [
  246. [signers[0].address, ethers.parseEther("50")],
  247. [signers[1].address, ethers.parseEther("10")],
  248. [signers[2].address, ethers.parseEther("100")],
  249. [signers[3].address, ethers.parseEther("200")],
  250. [signers[4].address, ethers.parseEther("300")],
  251. ]
  252. const tree = StandardMerkleTree.of(values, ["address", "uint256"])
  253. const root = tree.root
  254. const buyAmount = ethers.parseEther("1")
  255. const buyer = signers[1]
  256. let maxBuyAmount = 0n
  257. let proof = [] as string[]
  258. for (const [i, v] of tree.entries()) {
  259. if (v[0] === buyer.address) {
  260. proof = tree.getProof(i)
  261. if (typeof v[1] === "bigint") {
  262. maxBuyAmount = v[1]
  263. }
  264. }
  265. }
  266. await launchpadContract.setMerkleRoot(root, { from: owner })
  267. await time.increase(100)
  268. const launchpadAddress = await launchpadContract.getAddress()
  269. await expect(
  270. launchpadContract.connect(buyer).contributeETH(buyAmount, maxBuyAmount, proof, {
  271. value: ethers.parseEther("1"),
  272. })
  273. ).to.emit(launchpadContract, "Contributed")
  274. await expect(launchpadContract.withdrawPayments()).to.be.revertedWith("Sale is still active")
  275. await launchpadContract.enableClaimTokens(Math.floor(Date.now() / 1000) + 60 * 60)
  276. await time.increase(60 * 60 + 100) // increase time to finish sale
  277. await launchpadContract.connect(buyer).claimTokens()
  278. //todo 写不动了 接下来测试 非 owner withdraw token/钱 owner 的 withdraw token/钱
  279. await expect(launchpadContract.connect(signers[2]).withdrawPayments()).to.revertedWith(
  280. "Only owner can call this function"
  281. )
  282. await expect(launchpadContract.withdrawPayments()).to.emit(launchpadContract, "WithdrawPayments")
  283. expect(await ethers.provider.getBalance(launchpadAddress)).to.equal(0n)
  284. await expect(launchpadContract.connect(signers[2]).withdrawRemainingTokens()).to.revertedWith(
  285. "Only owner can call this function"
  286. )
  287. await expect(launchpadContract.withdrawRemainingTokens()).to.emit(
  288. launchpadContract,
  289. "WithdrawRemainingTokens"
  290. )
  291. expect(await saleTokenContract.balanceOf(launchpadAddress)).to.equal(0)
  292. })
  293. })
  294. })