Creator Subscription Wallet
What it does:
Manages on-chain subscription payments that allow fans to support creators with recurring deposits while giving creators programmable control over access, payouts, and cancellations.
Why it matters:
Eliminates platform fees, removes payment censorship risk, enables global subscriptions without banks, and gives creators full custody and transparency over recurring revenue.
How it works:
-
Creators deploy or register a subscription wallet with pricing and billing interval
-
Fans subscribe by depositing funds into the contract
-
Subscription validity is tracked on-chain by time and balance
-
Creators can withdraw earned subscription revenue at any time
-
Subscribers can cancel to stop future charges without trust assumptions
-
Expired or underfunded subscriptions automatically lose access
-
Integrates with gated content, communities, APIs, newsletters, or live streams
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title CreatorSubscriptionWallet
* @author Nam
* @notice On-chain recurring subscription wallet for creators
*/
contract CreatorSubscriptionWallet {
address public creator;
uint256 public pricePerInterval; // wei
uint256 public billingInterval; // seconds
uint256 public subscriberCount;
constructor(uint256 _pricePerInterval, uint256 _billingInterval) {
require(_pricePerInterval > 0, "Invalid price");
require(_billingInterval > 0, "Invalid interval");
creator = msg.sender;
pricePerInterval = _pricePerInterval;
billingInterval = _billingInterval;
}
// -------------------- STRUCTS --------------------
struct Subscription {
uint256 balance;
uint256 lastCharged;
bool active;
}
mapping(address => Subscription) public subscriptions;
// -------------------- EVENTS --------------------
event Subscribed(address indexed subscriber, uint256 amount);
event Charged(address indexed subscriber, uint256 amount, uint256 nextChargeTime);
event Cancelled(address indexed subscriber);
event CreatorWithdraw(uint256 amount);
// -------------------- SUBSCRIPTION MANAGEMENT --------------------
function subscribe() external payable {
require(msg.value >= pricePerInterval, "Insufficient deposit");
Subscription storage s = subscriptions[msg.sender];
if (!s.active) {
subscriberCount += 1;
s.lastCharged = block.timestamp;
s.active = true;
}
s.balance += msg.value;
emit Subscribed(msg.sender, msg.value);
}
function cancelSubscription() external {
Subscription storage s = subscriptions[msg.sender];
require(s.active, "Not active");
s.active = false;
emit Cancelled(msg.sender);
}
// -------------------- BILLING LOGIC --------------------
function charge(address _subscriber) public {
Subscription storage s = subscriptions[_subscriber];
require(s.active, "Inactive subscription");
uint256 elapsed = block.timestamp - s.lastCharged;
uint256 intervals = elapsed / billingInterval;
require(intervals > 0, "Nothing to charge");
uint256 amount = intervals * pricePerInterval;
require(s.balance >= amount, "Insufficient balance");
s.balance -= amount;
s.lastCharged += intervals * billingInterval;
emit Charged(
_subscriber,
amount,
s.lastCharged + billingInterval
);
}
function batchCharge(address[] calldata _subscribers) external {
for (uint256 i = 0; i < _subscribers.length; i++) {
if (subscriptions[_subscribers[i]].active) {
charge(_subscribers[i]);
}
}
}
// -------------------- CREATOR WITHDRAW --------------------
function withdraw(uint256 _amount) external {
require(msg.sender == creator, "Not creator");
require(_amount = pricePerInterval;
}
function nextChargeTime(address _subscriber) external view returns (uint256) {
return subscriptions[_subscriber].lastCharged + billingInterval;
}
}