Time-Capsule Message Contract

What it does:
Allows users to store encrypted messages or data on-chain that can only be accessed or revealed after a predefined unlock time.

Why it matters:
Preserves digital memories, secrets, or instructions securely, ensures immutability, and enables timed disclosure without intermediaries.

How it works:

  • Users submit messages or data hashes with an unlock timestamp

  • Smart contract stores the encrypted message securely on-chain

  • Messages cannot be accessed or modified until the unlock time is reached

  • Once the unlock time arrives, authorized recipients can reveal the message

  • Integrates with NFTs, personal archives, or inheritance systems

  • Dashboards show pending capsules, unlock countdowns, and history

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

import "@openzeppelin/contracts/access/Ownable.sol";

/**
 * @title TimeCapsuleMessage
 * @author Nam
 * @notice Stores and releases encrypted messages at a future time on-chain
 */
contract TimeCapsuleMessage is Ownable {

    struct Capsule {
        address creator;
        bytes32 messageHash; // hash of the encrypted message
        uint256 unlockTime;
        bool revealed;
    }

    mapping(uint256 => Capsule) public capsules;
    uint256 public capsuleCount;

    mapping(address => bool) public authorizedRecipients;

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

    event CapsuleCreated(uint256 indexed capsuleId, address creator, uint256 unlockTime);
    event CapsuleRevealed(uint256 indexed capsuleId, address recipient);
    event RecipientApproved(address recipient);
    event RecipientRevoked(address recipient);

    // -------------------- RECIPIENT MANAGEMENT --------------------

    function approveRecipient(address _recipient) external onlyOwner {
        authorizedRecipients[_recipient] = true;
        emit RecipientApproved(_recipient);
    }

    function revokeRecipient(address _recipient) external onlyOwner {
        authorizedRecipients[_recipient] = false;
        emit RecipientRevoked(_recipient);
    }

    modifier onlyRecipient() {
        require(authorizedRecipients[msg.sender], "Not authorized recipient");
        _;
    }

    // -------------------- CAPSULE MANAGEMENT --------------------

    function createCapsule(bytes32 _messageHash, uint256 _unlockTime) external {
        require(_unlockTime > block.timestamp, "Unlock time must be in the future");

        capsuleCount += 1;
        capsules[capsuleCount] = Capsule({
            creator: msg.sender,
            messageHash: _messageHash,
            unlockTime: _unlockTime,
            revealed: false
        });

        emit CapsuleCreated(capsuleCount, msg.sender, _unlockTime);
    }

    function revealCapsule(uint256 _capsuleId) external onlyRecipient view returns (bytes32) {
        Capsule storage c = capsules[_capsuleId];
        require(block.timestamp >= c.unlockTime, "Capsule is still locked");
        require(!c.revealed, "Capsule already revealed");

        // In practice, the recipient would decrypt off-chain using stored messageHash
        return c.messageHash;
    }

    function markAsRevealed(uint256 _capsuleId) external onlyRecipient {
        Capsule storage c = capsules[_capsuleId];
        require(block.timestamp >= c.unlockTime, "Capsule is still locked");
        require(!c.revealed, "Already revealed");

        c.revealed = true;
        emit CapsuleRevealed(_capsuleId, msg.sender);
    }

    // -------------------- VIEW FUNCTIONS --------------------

    function getCapsule(uint256 _capsuleId) external view returns (Capsule memory) {
        return capsules[_capsuleId];
    }
}