From 095e539542646bd40bdb9f56a2182a32ce0ad580 Mon Sep 17 00:00:00 2001 From: Dimitar Manov <99065541+dimitarmanov@users.noreply.github.com> Date: Wed, 11 Jun 2025 13:33:38 +0300 Subject: [PATCH] Add geth compatible tests --- hardhat/test/Lock.ts | 154 ++++++++++++++++++++++++++----------------- 1 file changed, 94 insertions(+), 60 deletions(-) diff --git a/hardhat/test/Lock.ts b/hardhat/test/Lock.ts index 2437356d19..05ef38cfb8 100644 --- a/hardhat/test/Lock.ts +++ b/hardhat/test/Lock.ts @@ -5,76 +5,110 @@ describe("Lock", function () { const ONE_YEAR_IN_SECS = 365 * 24 * 60 * 60; const ONE_GWEI = 1_000_000_000; - let lock: any; - let unlockTime: number; - let lockedAmount: number; - let owner: any; - let otherAccount: any; - - beforeEach(async function () { - // Use block.timestamp + ONE_YEAR_IN_SECS - const latestBlock = await hre.ethers.provider.getBlock("latest"); - unlockTime = latestBlock.timestamp + ONE_YEAR_IN_SECS; - lockedAmount = ONE_GWEI; - - [owner, otherAccount] = await hre.ethers.getSigners(); + async function deployLockWithFutureUnlockTime() { + const [owner, otherAccount] = await hre.ethers.getSigners(); + const block = await hre.ethers.provider.getBlock("latest"); + const unlockTime = block.timestamp + ONE_YEAR_IN_SECS; const Lock = await hre.ethers.getContractFactory("Lock"); - lock = await Lock.connect(owner).deploy(unlockTime, { - value: lockedAmount, - }); + const lock = await Lock.deploy(unlockTime, { value: ONE_GWEI }); await lock.waitForDeployment(); - }); - it("Should set the right unlockTime", async function () { - expect(await lock.unlockTime()).to.equal(unlockTime); - }); + return { lock, unlockTime, owner, otherAccount }; + } - it("Should set the right owner", async function () { - expect(await lock.owner()).to.equal(owner.address); - }); - - it("Should receive and store the funds to lock", async function () { - const balance = await hre.ethers.provider.getBalance(lock.target); - expect(balance).to.equal(lockedAmount); - }); - - it("Should fail if the unlockTime is not in the future", async function () { - const latestBlock = await hre.ethers.provider.getBlock("latest"); - const Lock = await hre.ethers.getContractFactory("Lock"); - await expect( - Lock.deploy(latestBlock.timestamp, { value: 1 }) - ).to.be.revertedWith("Unlock time should be in the future"); - }); - - it("Should revert with the right error if called too soon", async function () { - await expect(lock.withdraw()).to.be.revertedWith("You can't withdraw yet"); - }); - - it("Should revert if called from another account", async function () { - // simulate time passing (skip in real Geth, or deploy with unlockTime already in past) - // For now, assume unlockTime is satisfied - const fakeUnlockTime = unlockTime - ONE_YEAR_IN_SECS - 1; - const Lock = await hre.ethers.getContractFactory("Lock"); - const lockNow = await Lock.connect(owner).deploy(fakeUnlockTime, { - value: lockedAmount, + describe("Deployment", function () { + it("Should set the right unlockTime", async function () { + const { lock, unlockTime } = await deployLockWithFutureUnlockTime(); + expect(await lock.unlockTime()).to.equal(unlockTime); }); - await lockNow.waitForDeployment(); - await expect(lockNow.connect(otherAccount).withdraw()).to.be.revertedWith( - "You aren't the owner" - ); + it("Should set the right owner", async function () { + const { lock, owner } = await deployLockWithFutureUnlockTime(); + expect(await lock.owner()).to.equal(owner.address); + }); + + it("Should receive and store the funds to lock", async function () { + const { lock } = await deployLockWithFutureUnlockTime(); + const balance = await hre.ethers.provider.getBalance(await lock.getAddress()); + expect(balance).to.equal(ONE_GWEI); + }); + + it("Should fail if the unlockTime is not in the future", async function () { + const block = await hre.ethers.provider.getBlock("latest"); + const currentTimestamp = block.timestamp; + + const Lock = await hre.ethers.getContractFactory("Lock"); + await expect( + Lock.deploy(currentTimestamp, { value: ONE_GWEI }) + ).to.be.revertedWith("Unlock time should be in the future"); + }); }); - it("Should not fail if unlockTime has passed and owner calls it", async function () { - // simulate a contract deployed with past unlock time - const pastTime = unlockTime - ONE_YEAR_IN_SECS - 1; - const Lock = await hre.ethers.getContractFactory("Lock"); - const lockNow = await Lock.connect(owner).deploy(pastTime, { - value: lockedAmount, + describe("Withdrawals", function () { + it("Should revert with the right error if called too soon", async function () { + const { lock } = await deployLockWithFutureUnlockTime(); + await expect(lock.withdraw()).to.be.revertedWith("You can't withdraw yet"); }); - await lockNow.waitForDeployment(); - await expect(lockNow.withdraw()).not.to.be.reverted; + it("Should revert with the right error if called from another account", async function () { + const { unlockTime, otherAccount } = await deployLockWithFutureUnlockTime(); + + const Lock = await hre.ethers.getContractFactory("Lock"); + const lock = await Lock.deploy(unlockTime, { value: ONE_GWEI }); + await lock.waitForDeployment(); + + // Simulate time passing + await new Promise((r) => setTimeout(r, 2000)); + + await expect(lock.connect(otherAccount).withdraw()).to.be.revertedWith( + "You aren't the owner" + ); + }); + + it("Should not fail if unlockTime has passed and owner calls it", async function () { + const [owner] = await hre.ethers.getSigners(); + const block = await hre.ethers.provider.getBlock("latest"); + const unlockTime = block.timestamp + 1; + + const Lock = await hre.ethers.getContractFactory("Lock"); + const lock = await Lock.connect(owner).deploy(unlockTime, { + value: ONE_GWEI, + }); + await lock.waitForDeployment(); + + // Wait 2 seconds to pass unlockTime + await new Promise((r) => setTimeout(r, 2000)); + + await expect(lock.withdraw()).not.to.be.reverted; + }); + + it("Should emit an event on withdrawals", async function () { + const { lock, unlockTime } = await deployLockWithFutureUnlockTime(); + + await new Promise((r) => setTimeout(r, 2000)); + + await expect(lock.withdraw()) + .to.emit(lock, "Withdrawal") + .withArgs(ONE_GWEI, anyValue); // `anyValue` works without Hardhat helpers + }); + + it("Should transfer the funds to the owner", async function () { + const { lock, owner, unlockTime } = await deployLockWithFutureUnlockTime(); + + const ownerBalanceBefore = await hre.ethers.provider.getBalance(owner.address); + await new Promise((r) => setTimeout(r, 2000)); + + const tx = await lock.withdraw(); + const receipt = await tx.wait(); + const gasUsed = receipt.gasUsed * tx.gasPrice; + + const ownerBalanceAfter = await hre.ethers.provider.getBalance(owner.address); + + expect(ownerBalanceAfter).to.be.closeTo( + ownerBalanceBefore.add(ONE_GWEI).sub(gasUsed), + ONE_GWEI // small margin + ); + }); }); });