Rise In Logo

Build on Scroll

In this homework, you'll extend the capabilities of our PayPool smart contract. You'll practice using structs to organize data, enums to represent states, and leverage block timestamps for time-dependent logic.

Key Concepts

  • Structs: Custom data types to group related variables.
  • Enums: Sets of named constants to improve code readability.
  • Block Timestamps: Access the current block's timestamp (block.timestamp) for time-based actions.


1.Struct for Deposit Records:

  • Create a struct named DepositRecord:
  • depositor (address)
  • amount (uint256)
  • timestamp (uint256)

2.Enum for Deposit Statuses:

  • Create an enum named DepositStatus:
  • Pending
  • Approved
  • Rejected

3. Modify the PayPool Contract:

3.1.Store Deposit Records:

  • Inside the deposit function:
  • Get the current timestamp (block.timestamp).
  • Create a DepositRecord with the depositor's address, deposited amount, and timestamp.
  • Add this record to an array called depositHistory.

3.2.Manage Statuses:

  • Add a status property (type DepositStatus) to the DepositRecord struct.
  • Initialize new deposits as Pending.
  • Create owner-only functions:
  • approveDeposit(uint256 index) to set a deposit's status to Approved.
  • rejectDeposit(uint256 index) to set a deposit's status to Rejected.

3.3.Retrieve Deposit Data:

  • Create a function: getDepositHistory() public view returns (DepositRecord[] memory) to return the depositHistory array.

Paypool Contract

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.25;

// Imports

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract PayPool is ReentrancyGuard {

    // Data

    uint public totalBalance;

    address public owner;

    address\[\] public depositAddresses;

    mapping(address => uint256) public allowances;

    // Events

    event Deposit(address indexed depositer, uint256 amount);

    event AddressAdded(address indexed depositer);

    event AddressRemoved(address indexed depositer);

    event AllowanceGranted(address indexed user, uint amount);

    event AllowanceRemoved(address indexed user);

    event FundsRetrieved(address indexed recepient, uint amount);

    modifier isOwner() {

        require(msg.sender == owner, "Not owner!");



    modifier gotAllowance(address user) {

        require(hasAllowance(user), "This address has no allowance");



    modifier canDepositTokens(address depositer) {

        require(canDeposit(depositer), "This address is not allowed to deposit tokens");



    constructor() payable {

        totalBalance = msg.value;

        owner = msg.sender;


    // Internal functions

    function hasAllowance(address user) internal view returns(bool) {

        return allowances\[user\] > 0;


    function canDeposit(address depositer) internal view returns(bool) {

        for (uint i = 0; i < depositAddresses.length; i++) {

            if (depositAddresses\[i\] == depositer) {

                return true;



        return false;


    // Execute Functions

    function addDepositAddress(address depositer) external isOwner {


        emit AddressAdded(depositer);


    function removeDepositAddress(uint index) external isOwner canDepositTokens(depositAddresses\[index\]) {

        depositAddresses\[index\] = address(0);

        emit AddressRemoved(depositAddresses\[index\]);


    function deposit() external canDepositTokens(msg.sender) payable {

        totalBalance += msg.value;

        emit Deposit(msg.sender, msg.value);


    function retrieveBalance() external isOwner nonReentrant {

        uint balance = totalBalance;

        (bool success, ) = [owner.call](http://owner.call){value: balance}("");

        require(success, "Transfer failed");

        totalBalance = 0;

        emit FundsRetrieved(owner, balance);


    function giveAllowance(uint amount, address user) external isOwner {

        require(totalBalance >= amount, "There are no enough tokens inside the pool to give allowance");

        allowances\[user\] = amount;

        unchecked {

            totalBalance -= amount;


        emit AllowanceGranted(user, amount);


    function removeAllowance(address user) external isOwner gotAllowance(user) {

        allowances\[user\] = 0;

        emit AllowanceRemoved(user);


    function allowRetrieval() external gotAllowance(msg.sender) nonReentrant {

        uint amount = allowances\[msg.sender\];

        (bool success, ) = [msg.sender.call](http://msg.sender.call){value: amount}("");

        require(success, "Retrieval failed");

        allowances\[msg.sender\] = 0;

        emit FundsRetrieved(msg.sender, amount);



Homework Checklist

  • [ ] I created a DepositRecord struct.
  • [ ] I added a DepositStatus enum.
  • [ ] I modified the deposit function to store deposit records with timestamps.
  • [ ] I created functions to let the owner approve or reject deposits.
  • [ ] I created a getDepositHistory function to retrieve deposit data.
  • [ ] I rigorously tested my changes in Remix IDE.

After completing the contract, deploy it on Scroll Sepolia testnet and share your github repo below.

Congratulations, now you are a Solidity Developer!




You need to enroll in the course to be able to comment!

Stay in the know

Never miss updates on new programs and opportunities.

Rise In Logo

Rise together in web3!