A decentralized subscription management platform built on LUKSO that enables content creators to offer subscription-based services with NFT membership tokens.
| ℹ️ | This platform allows seamless recurring payments with NFT proof of membership, referral rewards, and full on-chain subscription management. |
|---|
- Overview
- Architecture
- Key Features
- Subscription Lifecycle
- User Perspective
- Content Creator Perspective
- Protocol Perspective
- Referral System
- Contract Deployment
- Technical Implementation
- Code Examples
- Diagrams
Substream is a subscription management platform built on LUKSO that enables the following:
- For Content Creators: Create subscription tiers with different prices and payment frequencies
- For Users: Subscribe to content creators with LSP7 tokens, receiving NFT proof of membership
- For Referrers: Earn commissions for referring subscribers
- For Protocol: Maintain the system with a sustainable fee structure
The platform is built using two main smart contracts:
Substream.sol: Core subscription logic, payments, and tier managementSubscriptionNFT.sol: LSP8-compatible NFT representing active subscriptions
┌───────────────────┐ ┌─────────────────────┐
│ │ │ │
│ Substream.sol │◄────►│ SubscriptionNFT │
│ (Main Contract) │ │ (LSP8 NFT Contract)│
│ │ │ │
└─────────┬─────────┘ └─────────────────────┘
│
│ Interacts with
▼
┌─────────────────────┐
│ │
│ LSP7 Digital Asset │
│ (Payment Tokens) │
│ │
└─────────────────────┘
The Substream contract manages all subscription logic including payments, tier creation, and renewals. When a user subscribes or renews, the contract mints an NFT via the SubscriptionNFT contract that represents proof of their active subscription.
- Multi-tier Subscriptions: Content creators can offer multiple subscription tiers with different benefits
- Multi-token Support: Accept multiple LSP7 tokens as payment with customizable prices per token
- NFT Membership Proof: Each subscription period is represented by an NFT
- Automated Renewals: Support for manual or third-party renewal of subscriptions
- Referral System: Built-in referral system with configurable commission rates
- Revenue Distribution: Automatic splitting of fees between creators, protocol, and referrers
- Withdrawals: Creators, referrers, and protocol can withdraw accumulated tokens
- Creation: Content creator creates subscription tier(s)
- Discovery: Users browse available subscription tiers
- Subscription: User subscribes by paying with an accepted LSP7 token
- Membership: User receives NFT representing their subscription
- Usage: Content creator can verify active subscriptions on-chain
- Renewal: Subscription can be renewed manually or by a third party
- Expiration/Cancellation: Subscription expires or gets cancelled
- Find a content creator's subscription tier that interests you
- Ensure you have sufficient LSP7 tokens for payment
- Call the
subscribefunction with:tierId: The ID of the subscription tierpaymentToken: Address of the LSP7 token you want to usereferrer: Address of who referred you (optional)
// Subscribe to a tier
await substreamContract.subscribe(
tierId, // e.g., 1
paymentTokenAddr, // e.g., "0x123..."
referrerAddr // e.g., "0x456..." or address(0) for no referrer
);- Upon successful subscription, you'll receive an NFT representing your active subscription
- View Subscriptions: Call
getUserSubscriptions(yourAddress)to get all your subscription IDs - Check Status: Call
isSubscriptionActive(subscriptionId)to check if a subscription is active - Renew: Call
renewSubscription(subscriptionId)before or after expiration - Cancel: Call
cancelSubscription(subscriptionId)to cancel an active subscription
- Decide on tier details: name, description, accepted tokens, prices, and frequency
- Call the
createTierfunction:
// Create a new subscription tier
await substreamContract.createTier(
"Premium Tier", // Name
"Exclusive content access", // Description
[lsp7TokenAddr1, lsp7TokenAddr2],// Accepted tokens
[tokenPrice1, tokenPrice2], // Prices for each token
2592000 // Frequency in seconds (30 days)
);- The contract returns a
tierIdthat users can subscribe to
- Get Your Tiers: Call
getPublisherTiers(yourAddress)to see all your tiers - Update Status: Call
updateTierStatus(tierId, isActive)to activate/deactivate a tier - Check Subscriptions: Use events to monitor new subscribers
Content creators can withdraw accumulated tokens:
// Withdraw tokens from the contract
await substreamContract.withdrawTokens(
tokenAddr, // Address of the token to withdraw
amount // Amount to withdraw
);- Protocol Fee: Default 3% (300 basis points) of subscription payments
- Maximum Limit: Capped at 10% to protect users and creators
- Fee Management: Only contract owner can modify fee percentages
- Revenue Collection: Fees accumulate in the contract for owner withdrawal
- Pausable: Contract can be paused in emergency situations
- Non-custodial: Users always control their NFTs
- Upgradable: Using UUPS proxy pattern for future improvements
- Reentrancy Protection: All payment functions have reentrancy guards
Substream includes a powerful referral system to encourage growth:
- Referral Link: Referrers share their address as the referral code
- User Subscribes: New subscriber includes referrer's address during subscription
- Commission: Referrer earns a percentage of the protocol fee
- Default: 25% of the protocol fee (effectively 0.75% of subscription price)
- Lifetime Value: Referrer continues earning from renewals
Referrers withdraw earnings the same way creators do:
// Check your balance for a specific token
const balance = await substreamContract.getTokenBalance(
referrerAddr, // Your address
tokenAddr // Token address
);
// Withdraw tokens
await substreamContract.withdrawTokens(
tokenAddr, // Address of the token to withdraw
amount // Amount to withdraw
);- Node.js and npm installed
- Private key for deployment
- LUKSO network connection
- Clone the repository and install dependencies:
git clone https://github.com/yourusername/substream-contract.git
cd substream-contract
npm install- Create a
.envfile with your private key:
PRIVATE_KEY=your_private_key- Deploy the contract:
npx hardhat run scripts/deploy.js --network luksoThe deployment process:
- Deploys the SubscriptionNFT contract first
- Deploys the Substream contract with the SubscriptionNFT address
- Sets permissions on the SubscriptionNFT for the Substream contract
Key components of the Substream contract:
- SubscriptionTier Struct: Stores tier details including accepted tokens and prices
- Subscription Struct: Tracks subscription status, expiration, and payment details
- Mappings: Track subscriptions, tiers, and token balances
- Withdrawal System: Allow stakeholders to withdraw accumulated tokens
- Access Control: Ensure only authorized users can perform certain actions
The NFT contract implements the LSP8 standard with specific features:
- Token Metadata: Stores subscription details (tier, dates) in token metadata
- Forced Transfers: Allows the main contract to mint tokens directly to subscribers
- Verification: Enables verification of subscription status via token ownership
// Function to check if a user has an active subscription
async function hasActiveSubscription(userAddress, publisherAddress) {
// Get user's subscriptions
const subscriptionIds = await substreamContract.getUserSubscriptions(userAddress);
// Check each subscription
for (const id of subscriptionIds) {
// Check if subscription is active
const isActive = await substreamContract.isSubscriptionActive(id);
if (!isActive) continue;
// Get subscription details
const subscription = await substreamContract.subscriptions(id);
// Get tier details
const tierId = subscription.tierId;
const tier = await substreamContract.tiers(tierId);
// Check if this tier belongs to the publisher
if (tier.publisher === publisherAddress) {
return true;
}
}
return false;
}async function createMultiTokenTier(
name,
description,
tokenAddresses,
tokenPrices,
frequencyInDays
) {
// Convert days to seconds
const frequencyInSeconds = frequencyInDays * 24 * 60 * 60;
// Create tier transaction
const tx = await substreamContract.createTier(
name,
description,
tokenAddresses,
tokenPrices,
frequencyInSeconds
);
// Wait for confirmation
const receipt = await tx.wait();
// Get tier ID from event
const event = receipt.events.find(e => e.event === 'TierCreated');
const tierId = event.args.tierId;
console.log(`Created tier ${tierId}: ${name}`);
return tierId;
}┌─────────┐ ┌─────────────┐ ┌──────────────┐ ┌───────────┐
│ User │ │ Substream │ │SubscriptionNFT│ │ Publisher │
└────┬────┘ └──────┬──────┘ └──────┬───────┘ └─────┬─────┘
│ │ │ │
│ subscribe() │ │ │
│─────────────────> │ │
│ │ │ │
│ │ mint() │ │
│ │───────────────────> │
│ │ │ │
│ │ NFT transferred │ │
│ │ <─ ─ ─ ─ ─ ─ ─ ─ ─│ │
│ │ │ │
│ NFT received │ │ │
│ <─ ─ ─ ─ ─ ─ ─ ─ │ │
│ │ │ │
│ │ Fee distributed │ │
│ │ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─>│
│ │ │ │
Subscription Payment (100%)
│
├─► Protocol Fee (3%)
│ │
│ ├─► Protocol Owner (75% of fee = 2.25% of payment)
│ │
│ └─► Referrer (25% of fee = 0.75% of payment)
│
└─► Content Creator (97% of payment)
User
│
│ owns
▼
LSP7 Tokens ──────► Substream Contract ◄────────┐
│ │
│ mints │
▼ │
SubscriptionNFT can verify
│ │
│ owned by │
▼ │
User ◄─────────────────────┘
We welcome contributions to Substream! Check out our contribution guidelines and join our community to help build the future of Web3 subscriptions.