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;
}
}