Decentralized Marketplace Escrow

What it does:
Holds payments in escrow for marketplace transactions and releases funds to sellers only when buyers confirm receipt or predefined conditions are met.

Why it matters:
Protects buyers from fraud, ensures sellers are paid upon fulfillment, reduces disputes, and provides transparent, auditable escrow transactions.

How it works:

  • Buyers deposit payment into the escrow contract when making a purchase

  • Sellers fulfill the order (digital or physical goods)

  • Buyer confirms receipt or smart contract verifies delivery via integration (e.g., Supply Chain Traceability)

  • Payment is released automatically to the seller

  • Partial payments, refunds, or dispute handling can be managed via multi-signature or arbitration

  • Integrates with on-chain marketplaces, logistics tracking, and reputation systems

  • Public dashboards allow monitoring of escrowed funds and transaction status

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

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

/**
 * @title MarketplaceEscrow
 * @author Nam
 * @notice Holds payments in escrow for marketplace transactions and releases upon fulfillment
 */
contract MarketplaceEscrow is Ownable {

    struct Escrow {
        address payable buyer;
        address payable seller;
        uint256 amount;
        bool delivered;
        bool released;
        bool refunded;
    }

    mapping(uint256 => Escrow) public escrows;
    uint256 public escrowCount;

    mapping(address => bool) public authorizedArbiters;

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

    event EscrowCreated(uint256 indexed escrowId, address buyer, address seller, uint256 amount);
    event DeliveryConfirmed(uint256 indexed escrowId);
    event PaymentReleased(uint256 indexed escrowId, uint256 amount);
    event RefundIssued(uint256 indexed escrowId, uint256 amount);
    event ArbiterApproved(address arbiter);

    // -------------------- ARBITER MANAGEMENT --------------------

    function approveArbiter(address _arbiter) external onlyOwner {
        authorizedArbiters[_arbiter] = true;
        emit ArbiterApproved(_arbiter);
    }

    function revokeArbiter(address _arbiter) external onlyOwner {
        authorizedArbiters[_arbiter] = false;
    }

    // -------------------- ESCROW CREATION --------------------

    function createEscrow(address payable _seller) external payable {
        require(msg.value > 0, "Payment required");

        escrowCount += 1;
        escrows[escrowCount] = Escrow({
            buyer: payable(msg.sender),
            seller: _seller,
            amount: msg.value,
            delivered: false,
            released: false,
            refunded: false
        });

        emit EscrowCreated(escrowCount, msg.sender, _seller, msg.value);
    }

    // -------------------- DELIVERY CONFIRMATION --------------------

    function confirmDelivery(uint256 _escrowId) external {
        Escrow storage e = escrows[_escrowId];
        require(msg.sender == e.buyer, "Only buyer can confirm delivery");
        require(!e.delivered, "Already confirmed");

        e.delivered = true;
        emit DeliveryConfirmed(_escrowId);
    }

    // -------------------- PAYMENT RELEASE --------------------

    function releasePayment(uint256 _escrowId) external {
        Escrow storage e = escrows[_escrowId];
        require(e.delivered, "Delivery not confirmed");
        require(!e.released, "Payment already released");

        e.released = true;
        e.seller.transfer(e.amount);
        emit PaymentReleased(_escrowId, e.amount);
    }

    // -------------------- REFUNDS / DISPUTES --------------------

    function issueRefund(uint256 _escrowId) external {
        require(authorizedArbiters[msg.sender], "Not authorized arbiter");

        Escrow storage e = escrows[_escrowId];
        require(!e.released, "Payment already released");
        require(!e.refunded, "Already refunded");

        e.refunded = true;
        e.buyer.transfer(e.amount);

        emit RefundIssued(_escrowId, e.amount);
    }
}