-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
403 additions
and
127 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
contract PiggyBank { | ||
address public owner; // Owner of the piggy bank (could be the contract deployer) | ||
uint public lockUpPeriod; // Lock-up period in seconds | ||
|
||
// Struct to represent each user's deposit information | ||
struct Deposit { | ||
uint amount; // Amount of Ether the user has deposited | ||
uint depositTime; // Timestamp of when the deposit was made | ||
} | ||
|
||
mapping(address => Deposit) public deposits; // Mapping from user address to their deposit details | ||
|
||
event Deposited(address indexed user, uint amount); | ||
event Withdrawn(address indexed user, uint amount); | ||
|
||
// Modifier to ensure only the owner can withdraw or set the lock-up period | ||
modifier onlyOwner() { | ||
require(msg.sender == owner, "Not the contract owner"); | ||
_; | ||
} | ||
|
||
// Constructor to set the initial owner and the lock-up period | ||
constructor(uint _lockUpPeriod) { | ||
owner = msg.sender; | ||
lockUpPeriod = _lockUpPeriod; // Set the lock-up period (in seconds) | ||
} | ||
|
||
// Deposit Ether into the PiggyBank | ||
function deposit() external payable { | ||
require(msg.value > 0, "Must send Ether to deposit"); | ||
|
||
Deposit storage userDeposit = deposits[msg.sender]; | ||
|
||
// If the user already has a deposit, we just add to it | ||
userDeposit.amount += msg.value; | ||
|
||
// If this is the user's first deposit, we set the deposit time | ||
if (userDeposit.depositTime == 0) userDeposit.depositTime = block.timestamp; | ||
|
||
emit Deposited(msg.sender, msg.value); | ||
} | ||
|
||
// Withdraw funds after the lock-up period has passed | ||
function withdraw() external { | ||
Deposit storage userDeposit = deposits[msg.sender]; | ||
require(userDeposit.amount > 0, "No funds to withdraw"); | ||
require(block.timestamp >= userDeposit.depositTime + lockUpPeriod, "Funds are still locked"); | ||
|
||
uint amountToWithdraw = userDeposit.amount; | ||
userDeposit.amount = 0; // Reset the user's deposit | ||
|
||
// Transfer the funds back to the user | ||
payable(msg.sender).transfer(amountToWithdraw); | ||
|
||
emit Withdrawn(msg.sender, amountToWithdraw); | ||
} | ||
|
||
// View the balance of the user's deposit | ||
function getDepositBalance(address _user) external view returns (uint) { | ||
return deposits[_user].amount; | ||
} | ||
|
||
// View the time at which the user can withdraw their funds | ||
function getUnlockTime() external view returns (uint) { | ||
return deposits[msg.sender].depositTime + lockUpPeriod; | ||
} | ||
|
||
// Owner can update the lock-up period (if desired) | ||
function setLockUpPeriod(uint _newLockUpPeriod) external onlyOwner { | ||
lockUpPeriod = _newLockUpPeriod; | ||
} | ||
|
||
// Owner can withdraw all funds (for example, in case of emergencies) | ||
function ownerWithdraw(uint amount) external onlyOwner { | ||
require(amount <= address(this).balance, "Insufficient balance in the contract"); | ||
payable(owner).transfer(amount); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
import { expect } from "chai"; | ||
import { ethers } from "hardhat"; | ||
import { PiggyBank } from "../typechain-types"; | ||
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; | ||
|
||
describe("PiggyBank Contract", function () { | ||
let piggyBank: PiggyBank; | ||
let owner: HardhatEthersSigner; | ||
let user: HardhatEthersSigner; | ||
let lockUpPeriod: number; | ||
|
||
beforeEach(async () => { | ||
// Get the signers (owner and a user) | ||
[owner, user] = await ethers.getSigners(); | ||
|
||
// Deploy the PiggyBank contract with a lock-up period of 1 week | ||
lockUpPeriod = 7 * 24 * 60 * 60; // 7 days in seconds | ||
const PiggyBankFactory = await ethers.getContractFactory("PiggyBank"); | ||
piggyBank = await PiggyBankFactory.deploy(lockUpPeriod); | ||
await piggyBank.waitForDeployment(); | ||
}); | ||
|
||
describe("Deployment", function () { | ||
it("Should deploy with the correct owner and lock-up period", async function () { | ||
expect(await piggyBank.owner()).to.equal(owner.address); | ||
expect(await piggyBank.lockUpPeriod()).to.equal(lockUpPeriod); | ||
}); | ||
}); | ||
|
||
describe("Deposit", function () { | ||
it("Should allow a user to deposit Ether", async function () { | ||
const depositAmount = ethers.parseEther("1.0"); | ||
|
||
// Use the deposit function instead of direct transfer | ||
await piggyBank.connect(user).deposit({ value: depositAmount }); | ||
|
||
const depositBalance = await piggyBank.getDepositBalance(user.address); | ||
expect(depositBalance).to.equal(depositAmount); | ||
}); | ||
|
||
it("Should revert when trying to deposit 0 Ether", async function () { | ||
await expect(piggyBank.connect(user).deposit({ value: 0 })) | ||
.to.be.revertedWith("Must send Ether to deposit"); | ||
}); | ||
|
||
it("Should emit a Deposited event when Ether is deposited", async function () { | ||
const depositAmount = ethers.parseEther("0.5"); | ||
|
||
await expect(piggyBank.connect(user).deposit({ value: depositAmount })) | ||
.to.emit(piggyBank, "Deposited") | ||
.withArgs(user.address, depositAmount); | ||
}); | ||
|
||
it("Should update deposit time only on first deposit", async function () { | ||
const firstDeposit = ethers.parseEther("0.5"); | ||
const secondDeposit = ethers.parseEther("0.5"); | ||
|
||
// First deposit | ||
await piggyBank.connect(user).deposit({ value: firstDeposit }); | ||
const firstDepositTime = (await ethers.provider.getBlock('latest'))!.timestamp; | ||
|
||
// Second deposit | ||
await piggyBank.connect(user).deposit({ value: secondDeposit }); | ||
|
||
const deposit = await piggyBank.deposits(user.address); | ||
expect(deposit.depositTime).to.equal(firstDepositTime); | ||
expect(deposit.amount).to.equal(firstDeposit + secondDeposit); | ||
}); | ||
}); | ||
|
||
describe("Withdraw", function () { | ||
beforeEach(async function () { | ||
// Setup: User deposits some Ether | ||
await piggyBank.connect(user).deposit({ value: ethers.parseEther("1.0") }); | ||
}); | ||
|
||
it("Should allow withdrawal after lock-up period", async function () { | ||
// Fast-forward time | ||
await ethers.provider.send("evm_increaseTime", [lockUpPeriod]); | ||
await ethers.provider.send("evm_mine", []); | ||
|
||
await expect(piggyBank.connect(user).withdraw()) | ||
.to.changeEtherBalance(user, ethers.parseEther("1.0")); | ||
}); | ||
|
||
it("Should revert withdrawal before lock-up period", async function () { | ||
await expect(piggyBank.connect(user).withdraw()) | ||
.to.be.revertedWith("Funds are still locked"); | ||
}); | ||
|
||
it("Should revert withdrawal with no deposit", async function () { | ||
const newUser = (await ethers.getSigners())[2]; | ||
await expect(piggyBank.connect(newUser).withdraw()) | ||
.to.be.revertedWith("No funds to withdraw"); | ||
}); | ||
|
||
it("Should correctly report unlock time", async function () { | ||
const depositTime = (await ethers.provider.getBlock('latest'))!.timestamp; | ||
const expectedUnlockTime = depositTime + lockUpPeriod; | ||
expect(await piggyBank.connect(user).getUnlockTime()) | ||
.to.equal(expectedUnlockTime); | ||
}); | ||
}); | ||
|
||
describe("Owner Functions", function () { | ||
it("Should allow only owner to change lock-up period", async function () { | ||
const newPeriod = 30 * 24 * 60 * 60; // 30 days | ||
await expect(piggyBank.connect(user).setLockUpPeriod(newPeriod)) | ||
.to.be.revertedWith("Not the contract owner"); | ||
|
||
await piggyBank.connect(owner).setLockUpPeriod(newPeriod); | ||
expect(await piggyBank.lockUpPeriod()).to.equal(newPeriod); | ||
}); | ||
|
||
it("Should allow only owner to withdraw funds", async function () { | ||
const amount = ethers.parseEther("1.0"); | ||
await piggyBank.connect(user).deposit({ value: amount }); | ||
|
||
await expect(piggyBank.connect(user).ownerWithdraw(amount)) | ||
.to.be.revertedWith("Not the contract owner"); | ||
|
||
await expect(piggyBank.connect(owner).ownerWithdraw(amount)) | ||
.to.changeEtherBalance(owner, amount); | ||
}); | ||
|
||
it("Should revert owner withdrawal if insufficient balance", async function () { | ||
const amount = ethers.parseEther("1.0"); | ||
await expect(piggyBank.connect(owner).ownerWithdraw(amount)) | ||
.to.be.revertedWith("Insufficient balance in the contract"); | ||
}); | ||
}); | ||
}); |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.