Streaming Payment Per Second

What it does:
Enables continuous, real-time payments that stream value per second from a payer to a recipient, calculated and enforced entirely on-chain.

Why it matters:
Replaces delayed, batch-based payments with instant value flow, enables new business models like pay-per-use and time-based access, and aligns payment precisely with actual consumption.

How it works:

  • A payer opens a payment stream specifying recipient, rate per second, and deposit amount

  • Smart contract tracks elapsed time and calculates accrued payments continuously

  • Funds are deducted proportionally based on time passed, not fixed intervals

  • Recipients can withdraw earned funds at any moment without waiting for stream closure

  • Payers can pause or cancel streams to stop future payments

  • Unused funds are refunded automatically when a stream ends

  • Supports subscriptions, live content, APIs, compute usage, and creator monetization

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

/**
 * @title StreamingPaymentPerSecond
 * @author Nam
 * @notice Real-time streaming payments calculated per second on-chain
 */
contract StreamingPaymentPerSecond {

    uint256 public streamCount;

    // -------------------- STRUCTS --------------------

    struct Stream {
        address payer;
        address recipient;
        uint256 ratePerSecond;   // wei per second
        uint256 deposit;
        uint256 startTime;
        uint256 lastWithdrawTime;
        bool active;
    }

    mapping(uint256 => Stream) public streams;

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

    event StreamOpened(
        uint256 indexed streamId,
        address indexed payer,
        address indexed recipient,
        uint256 ratePerSecond,
        uint256 deposit
    );

    event StreamWithdrawn(
        uint256 indexed streamId,
        address indexed recipient,
        uint256 amount
    );

    event StreamCancelled(
        uint256 indexed streamId,
        uint256 refundedAmount
    );

    // -------------------- STREAM MANAGEMENT --------------------

    function openStream(
        address _recipient,
        uint256 _ratePerSecond
    ) external payable returns (uint256) {
        require(_recipient != address(0), "Invalid recipient");
        require(_ratePerSecond > 0, "Invalid rate");
        require(msg.value > 0, "Deposit required");

        streamCount += 1;

        streams[streamCount] = Stream({
            payer: msg.sender,
            recipient: _recipient,
            ratePerSecond: _ratePerSecond,
            deposit: msg.value,
            startTime: block.timestamp,
            lastWithdrawTime: block.timestamp,
            active: true
        });

        emit StreamOpened(
            streamCount,
            msg.sender,
            _recipient,
            _ratePerSecond,
            msg.value
        );

        return streamCount;
    }

    // -------------------- PAYMENT CALCULATION --------------------

    function withdraw(uint256 _streamId) external {
        Stream storage s = streams[_streamId];
        require(s.active, "Stream inactive");
        require(msg.sender == s.recipient, "Not recipient");

        uint256 elapsed = block.timestamp - s.lastWithdrawTime;
        uint256 amount = elapsed * s.ratePerSecond;
        require(amount > 0, "Nothing to withdraw");

        if (amount > s.deposit) {
            amount = s.deposit;
        }

        s.deposit -= amount;
        s.lastWithdrawTime = block.timestamp;

        if (s.deposit == 0) {
            s.active = false;
        }

        payable(s.recipient).transfer(amount);
        emit StreamWithdrawn(_streamId, s.recipient, amount);
    }

    // -------------------- STREAM CANCELLATION --------------------

    function cancelStream(uint256 _streamId) external {
        Stream storage s = streams[_streamId];
        require(s.active, "Already inactive");
        require(msg.sender == s.payer, "Not payer");

        uint256 elapsed = block.timestamp - s.lastWithdrawTime;
        uint256 owed = elapsed * s.ratePerSecond;

        if (owed > s.deposit) {
            owed = s.deposit;
        }

        uint256 refund = s.deposit - owed;
        s.deposit = 0;
        s.active = false;

        if (owed > 0) {
            payable(s.recipient).transfer(owed);
        }

        if (refund > 0) {
            payable(s.payer).transfer(refund);
        }

        emit StreamCancelled(_streamId, refund);
    }

    // -------------------- VIEW HELPERS --------------------

    function pendingAmount(uint256 _streamId) external view returns (uint256) {
        Stream memory s = streams[_streamId];
        if (!s.active) return 0;

        uint256 elapsed = block.timestamp - s.lastWithdrawTime;
        uint256 amount = elapsed * s.ratePerSecond;

        if (amount > s.deposit) {
            return s.deposit;
        }
        return amount;
    }
}