Rise In Logo

Submit Your Homework 3

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.

Instructions

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 {
        depositAddresses.push(depositer);
        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{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{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!

Comments

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