diff --git a/contracts/UnitapPassBatchSale.sol b/contracts/UnitapPassBatchSale.sol index 43280a8..9283c38 100644 --- a/contracts/UnitapPassBatchSale.sol +++ b/contracts/UnitapPassBatchSale.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; +import "@openzeppelin/contracts/access/Ownable.sol"; import "./interfaces/IUnitapPass.sol"; struct Batch { @@ -9,13 +10,13 @@ struct Batch { uint256 price; } -contract UnitapPassBatchSale { +contract UnitapPassBatchSale is Ownable { uint32 public constant MAX_SALE_COUNT = 2000; address unitapPass; uint32 public totalSoldCount; Batch[] public batches; - constructor(address unitapPass_) { + constructor(address unitapPass_) Ownable() { unitapPass = unitapPass_; } @@ -27,7 +28,7 @@ contract UnitapPassBatchSale { error CurrentBatchSoldOut(); error InsufficientFunds(); - function startBatch(uint32 batchSize, uint256 price) public { + function startBatch(uint32 batchSize, uint256 price) public onlyOwner { if (totalSoldCount + batchSize > MAX_SALE_COUNT) { revert InvalidBatchSize(); } diff --git a/scripts/deployers.ts b/scripts/deployers.ts index c4e1060..d6d65b5 100644 --- a/scripts/deployers.ts +++ b/scripts/deployers.ts @@ -8,3 +8,18 @@ export async function deployUnitapPass(admin: SignerWithAddress) { return unitapPass; } + +export async function deployUnitapBatchSale( + admin: SignerWithAddress, + unitapPassAddress: string +) { + const UnitapPassBatchSaleFactory = await ethers.getContractFactory( + "UnitapPassBatchSale" + ); + const unitapPassBatchSale = await UnitapPassBatchSaleFactory.connect( + admin + ).deploy(unitapPassAddress); + await unitapPassBatchSale.deployed(); + + return unitapPassBatchSale; +} diff --git a/test/batchSale.ts b/test/batchSale.ts index e69de29..4bf960e 100644 --- a/test/batchSale.ts +++ b/test/batchSale.ts @@ -0,0 +1,94 @@ +import { UnitapPass, UnitapPassBatchSale } from "../typechain-types"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import { ethers } from "hardhat"; +import { deployUnitapBatchSale, deployUnitapPass } from "../scripts/deployers"; +import { expect } from "chai"; + +describe("Batch Sale", async () => { + let unitapPassBatchSale: UnitapPassBatchSale; + let unitapPass: UnitapPass; + + let adminUnitapPass: SignerWithAddress; + let adminUnitapPassBatchSale: SignerWithAddress; + let user1: SignerWithAddress; + let user2: SignerWithAddress; + + before(async () => { + [adminUnitapPass, adminUnitapPassBatchSale, user1, user2] = + await ethers.getSigners(); + unitapPass = await deployUnitapPass(adminUnitapPass); + unitapPassBatchSale = await deployUnitapBatchSale( + adminUnitapPassBatchSale, + unitapPass.address + ); + }); + + it("should grant minter role to batch sale contract", async () => { + await unitapPass + .connect(adminUnitapPass) + .grantRole(unitapPass.MINTER_ROLE(), unitapPassBatchSale.address); + }); + + it("should start a batch with size 100", async () => { + await unitapPassBatchSale + .connect(adminUnitapPassBatchSale) + .startBatch(100, ethers.utils.parseEther("0.01")); + }); + + it("should mint 90 tokens for user 1", async () => { + // call multiMint function on batch sale contract and send 90*0.01 ether to it + await unitapPassBatchSale + .connect(user1) + .multiMint(90, user1.address, { value: ethers.utils.parseEther("0.9") }); + + // check that user 1 has 90 tokens + expect(await unitapPass.balanceOf(user1.address)).to.equal(90); + }); + + it("should not be able to start new batch while one is active", async () => { + await expect( + unitapPassBatchSale + .connect(adminUnitapPassBatchSale) + .startBatch(100, ethers.utils.parseEther("0.01")) + ).to.be.revertedWithCustomError( + unitapPassBatchSale, + "CurrentBatchNotSoldOut" + ); + }); + + it("should mint 10 tokens for user 2", async () => { + // call multiMint function on batch sale contract and send 10*0.01 ether to it + await unitapPassBatchSale + .connect(user2) + .multiMint(10, user2.address, { value: ethers.utils.parseEther("0.1") }); + + // check that user 2 has 10 tokens + expect(await unitapPass.balanceOf(user2.address)).to.equal(10); + }); + + it("should not be able to mint more tokens", async () => { + // call multiMint function on batch sale contract and send 1 ether to it + await expect( + unitapPassBatchSale + .connect(user1) + .multiMint(1, user1.address, { value: ethers.utils.parseEther("1") }) + ).to.be.revertedWithCustomError(unitapPassBatchSale, "CurrentBatchSoldOut"); + }); + + it("should be able to start new batch", async () => { + await unitapPassBatchSale + .connect(adminUnitapPassBatchSale) + .startBatch(100, ethers.utils.parseEther("0.01")); + }); + + it("should be able to mint 100 tokens for user 1", async () => { + // call multiMint function on batch sale contract and send 100*0.01 ether to it + await unitapPassBatchSale + .connect(user1) + .multiMint(100, user1.address, { value: ethers.utils.parseEther("1") }); + + // check that user 1 has 100 tokens + expect(await unitapPass.balanceOf(user1.address)).to.equal(190); + expect(await unitapPassBatchSale.totalSoldCount()).to.equal(200); + }); +});