Crypto Allowance for Children

What it does:
A smart contract that allows parents to set up a recurring crypto allowance for children with spending limits, schedules, and optional parental controls.

Why it matters:
It introduces children to money management in a safe, transparent, and programmable way while giving parents full control and visibility over funds.

How it works:

  • A parent deposits funds into an allowance vault.

  • An allowance amount and release interval are defined.

  • Funds unlock periodically (weekly / monthly).

  • The child can withdraw only the allowed amount.

  • Parents can pause, adjust, or terminate the allowance at any time.

      // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title CryptoAllowanceForChildren
 * @author Nam
 * @notice Programmable crypto allowance with parental controls
 */
contract CryptoAllowanceForChildren {

    // -------------------- STATE VARIABLES --------------------

    address public parent;
    address public child;

    uint256 public allowanceAmount;     // Amount per interval
    uint256 public interval;            // Time interval (e.g. 7 days)
    uint256 public lastWithdrawal;
    bool public paused;

    // -------------------- EVENTS --------------------

    event AllowanceWithdrawn(address indexed child, uint256 amount);
    event AllowanceUpdated(uint256 newAmount, uint256 newInterval);
    event Paused(bool status);
    event Funded(uint256 amount);

    // -------------------- MODIFIERS --------------------

    modifier onlyParent() {
        require(msg.sender == parent, "Not parent");
        _;
    }

    modifier onlyChild() {
        require(msg.sender == child, "Not child");
        _;
    }

    modifier notPaused() {
        require(!paused, "Allowance paused");
        _;
    }

    // -------------------- CONSTRUCTOR --------------------

    constructor(
        address _child,
        uint256 _allowanceAmount,
        uint256 _interval
    ) {
        require(_child != address(0), "Invalid child");
        require(_allowanceAmount > 0, "Invalid amount");
        require(_interval > 0, "Invalid interval");

        parent = msg.sender;
        child = _child;
        allowanceAmount = _allowanceAmount;
        interval = _interval;
        lastWithdrawal = block.timestamp;
    }

    // -------------------- FUNDING --------------------

    /**
     * @notice Parent funds the allowance vault
     */
    function fund() external payable onlyParent {
        require(msg.value > 0, "Zero funding");
        emit Funded(msg.value);
    }

    // -------------------- WITHDRAWAL --------------------

    /**
     * @notice Child withdraws allowance if interval passed
     */
    function withdraw() external onlyChild notPaused {
        require(
            block.timestamp >= lastWithdrawal + interval,
            "Too early"
        );

        require(
            address(this).balance >= allowanceAmount,
            "Insufficient balance"
        );

        lastWithdrawal = block.timestamp;
        payable(child).transfer(allowanceAmount);

        emit AllowanceWithdrawn(child, allowanceAmount);
    }

    // -------------------- PARENT CONTROLS --------------------

    /**
     * @notice Update allowance parameters
     */
    function updateAllowance(
        uint256 _newAmount,
        uint256 _newInterval
    ) external onlyParent {
        require(_newAmount > 0, "Invalid amount");
        require(_newInterval > 0, "Invalid interval");

        allowanceAmount = _newAmount;
        interval = _newInterval;

        emit AllowanceUpdated(_newAmount, _newInterval);
    }

    /**
     * @notice Pause or resume allowance
     */
    function setPaused(bool _paused) external onlyParent {
        paused = _paused;
        emit Paused(_paused);
    }

    /**
     * @notice Emergency withdrawal by parent
     */
    function emergencyWithdraw(uint256 _amount)
        external
        onlyParent
    {
        require(_amount = lastWithdrawal + interval) {
            return 0;
        }
        return (lastWithdrawal + interval) - block.timestamp;
    }
}