Relationship Agreement Smart Contract

What it does:
Enables individuals to define, store, and enforce personal agreements or relationship commitments on-chain, optionally including shared responsibilities or milestone-based triggers.

Why it matters:
Creates transparency and accountability in personal agreements, reduces disputes, ensures enforceability, and allows optional automation of shared responsibilities or assets.

How it works:

  • Participants define agreement terms, milestones, or conditions on-chain

  • Smart contract stores immutable records of the agreement and participants’ consent

  • Conditional triggers can automate asset transfers, shared responsibilities, or notifications

  • Milestones can include events, date-based commitments, or mutual approvals

  • Integrates with tokenized assets, escrow payments, or shared wallets

  • Dashboards show agreement status, upcoming milestones, and fulfillment history

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

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

/**
 * @title RelationshipAgreement
 * @author Nam
 * @notice Stores and enforces personal or relationship agreements on-chain
 */
contract RelationshipAgreement is Ownable {

    struct Milestone {
        string description;
        uint256 dueDate; // timestamp
        bool completed;
    }

    struct Agreement {
        address participant1;
        address participant2;
        string terms;
        Milestone[] milestones;
        bool active;
    }

    mapping(uint256 => Agreement) public agreements;
    uint256 public agreementCount;

    mapping(address => bool) public authorizedValidators;

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

    event ValidatorApproved(address validator);
    event ValidatorRevoked(address validator);
    event AgreementCreated(uint256 indexed agreementId, address participant1, address participant2);
    event MilestoneCompleted(uint256 indexed agreementId, uint256 milestoneIndex);

    // -------------------- VALIDATOR MANAGEMENT --------------------

    function approveValidator(address _validator) external onlyOwner {
        authorizedValidators[_validator] = true;
        emit ValidatorApproved(_validator);
    }

    function revokeValidator(address _validator) external onlyOwner {
        authorizedValidators[_validator] = false;
        emit ValidatorRevoked(_validator);
    }

    modifier onlyValidator() {
        require(authorizedValidators[msg.sender], "Not authorized validator");
        _;
    }

    // -------------------- AGREEMENT MANAGEMENT --------------------

    function createAgreement(address _participant2, string calldata _terms, string[] calldata _milestoneDescriptions, uint256[] calldata _dueDates) external {
        require(_participant2 != address(0), "Invalid participant");
        require(_milestoneDescriptions.length == _dueDates.length, "Mismatched milestones and dates");

        agreementCount += 1;
        Agreement storage a = agreements[agreementCount];
        a.participant1 = msg.sender;
        a.participant2 = _participant2;
        a.terms = _terms;
        a.active = true;

        for (uint256 i = 0; i < _milestoneDescriptions.length; i++) {
            a.milestones.push(Milestone({
                description: _milestoneDescriptions[i],
                dueDate: _dueDates[i],
                completed: false
            }));
        }

        emit AgreementCreated(agreementCount, msg.sender, _participant2);
    }

    // -------------------- MILESTONE MANAGEMENT --------------------

    function completeMilestone(uint256 _agreementId, uint256 _milestoneIndex) external onlyValidator {
        Agreement storage a = agreements[_agreementId];
        require(a.active, "Agreement not active");
        Milestone storage m = a.milestones[_milestoneIndex];
        require(!m.completed, "Milestone already completed");

        m.completed = true;
        emit MilestoneCompleted(_agreementId, _milestoneIndex);
    }

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

    function getMilestones(uint256 _agreementId) external view returns (Milestone[] memory) {
        return agreements[_agreementId].milestones;
    }

    function isAgreementActive(uint256 _agreementId) external view returns (bool) {
        return agreements[_agreementId].active;
    }
}