Vacation Property Booking Escrow

What it does:
Holds booking funds in escrow for vacation rentals and automatically releases or refunds payments based on check-in, stay completion, or cancellation rules.

Why it matters:
Protects both guests and hosts from scams, no-shows, and disputes by ensuring funds move only when pre-agreed booking conditions are met.

How it works:

  • A booking is created with property details, price, and stay dates

  • Guest deposits the booking amount into the escrow contract

  • Funds remain locked until the check-in date

  • Host confirms guest check-in on-chain

  • Funds are released to the host after successful stay

  • If cancellation or dispute occurs, funds are refunded per rules

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

/**
 * @title VacationPropertyBookingEscrow
 * @author Nam
 * @notice Escrow contract for short-term vacation property bookings
 */
contract VacationPropertyBookingEscrow {

    // -------------------- ROLES --------------------

    address public host;
    address public guest;

    // -------------------- BOOKING TERMS --------------------

    uint256 public bookingAmount;
    uint256 public checkInDate;
    uint256 public checkOutDate;
    uint256 public cancellationDeadline;

    // -------------------- STATE --------------------

    bool public deposited;
    bool public checkedIn;
    bool public completed;
    bool public cancelled;

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

    event FundsDeposited(address indexed guest, uint256 amount);
    event CheckInConfirmed();
    event BookingCompleted();
    event BookingCancelled();
    event FundsReleased(address indexed host, uint256 amount);
    event FundsRefunded(address indexed guest, uint256 amount);

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

    modifier onlyHost() {
        require(msg.sender == host, "Not host");
        _;
    }

    modifier onlyGuest() {
        require(msg.sender == guest, "Not guest");
        _;
    }

    modifier bookingActive() {
        require(!completed && !cancelled, "Booking inactive");
        _;
    }

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

    constructor(
        address _guest,
        uint256 _checkInDate,
        uint256 _checkOutDate,
        uint256 _cancellationDeadline
    ) {
        require(_guest != address(0), "Invalid guest");
        require(_checkOutDate > _checkInDate, "Invalid stay dates");

        host = msg.sender;
        guest = _guest;

        checkInDate = _checkInDate;
        checkOutDate = _checkOutDate;
        cancellationDeadline = _cancellationDeadline;
    }

    // -------------------- ESCROW LOGIC --------------------

    /**
     * @notice Guest deposits booking funds
     */
    function deposit() external payable onlyGuest bookingActive {
        require(!deposited, "Already deposited");
        require(msg.value > 0, "Invalid amount");

        bookingAmount = msg.value;
        deposited = true;

        emit FundsDeposited(msg.sender, msg.value);
    }

    /**
     * @notice Host confirms guest check-in
     */
    function confirmCheckIn() external onlyHost bookingActive {
        require(deposited, "No funds deposited");
        require(block.timestamp >= checkInDate, "Too early to check in");

        checkedIn = true;
        emit CheckInConfirmed();
    }

    /**
     * @notice Complete stay and release funds to host
     */
    function completeStay() external onlyHost bookingActive {
        require(checkedIn, "Guest not checked in");
        require(block.timestamp >= checkOutDate, "Stay not completed");

        completed = true;
        payable(host).transfer(bookingAmount);

        emit BookingCompleted();
        emit FundsReleased(host, bookingAmount);
    }

    // -------------------- CANCELLATION & REFUND --------------------

    /**
     * @notice Guest cancels booking before deadline
     */
    function cancelBooking() external onlyGuest bookingActive {
        require(deposited, "No funds deposited");
        require(block.timestamp <= cancellationDeadline, "Cancellation period passed");

        cancelled = true;
        payable(guest).transfer(bookingAmount);

        emit BookingCancelled();
        emit FundsRefunded(guest, bookingAmount);
    }
}