Decentralized content subscription platform using INTMAX for secure ETH payments.
ZKSub is a decentralized platform designed for content creators and subscribers, leveraging blockchain technology for secure, transparent payment processing. Built with a modern tech stack, it combines a React-based frontend with TypeScript, Vite, and Tailwind CSS for a responsive and visually appealing user interface, and an Express-based backend with TypeScript for robust server-side operations. The platform integrates with the INTMAX testnet to facilitate payments in ETH, using the intmax2-client-sdk for client-side wallet interactions and intmax2-server-sdk for backend transaction validation. The project enables creators to upload and monetize content (images, videos, audio, PDFs) and subscribers to pay for access to this content, with all transactions secured on the blockchain.
Key Features
Creator Dashboard:
Content Upload: Creators can upload content via a modal interface, specifying a name, description, file (image, video, audio, or PDF), and subscription price ($0.05, $0.25, or $1 per day). A disabled "+18 Content" button is included for future age-restricted content functionality.
Content Management: Creators can view their uploaded content in a grid layout and delete content, which removes both the file and its metadata.
Responsive Design: Styled with Tailwind CSS for a modern, clean, and user-friendly interface with cards, shadows, and vibrant colors.
Subscriber Dashboard:
Content Browsing: Displays available content in a responsive grid, showing name, description, and price (in USD and ETH, with 1 ETH = $2500).
Subscription Payments: Subscribers can pay for content access using Metamask, with payments processed in ETH on the INTMAX testnet.
Content Access: Subscribed content is displayed in an iframe, accessible for 24 hours after payment.
Error Handling: Displays error messages for failed transactions or validation issues.
Payment System:
Uses broadcastTransaction from intmax2-client-sdk to send ETH payments, converting USD prices to ETH (e.g., $0.25 = 0.0001 ETH).
Backend validates transactions using fetchTransactionHistory from intmax2-server-sdk, ensuring the transaction digest matches and the amount is correct.
Persistence:
Content metadata (ID, name, description, price, creator address, file path) is stored in server/contents.json for persistence across server restarts.
Uploaded files are saved in client/public and served via the backend's /public endpoint.
Security:
CORS configured to allow requests from http://localhost:5173.
File uploads use express-fileupload for secure handling.
Payments are validated on the blockchain, ensuring trustless transactions.
Technical Details
Frontend:
Built with React, TypeScript, and Vite for fast development and hot module replacement.
Uses Tailwind CSS for styling, providing a responsive grid layout, modals, and visually appealing components.
Integrates intmax2-client-sdk for wallet initialization, login, and transaction broadcasting.
Axios for HTTP requests to the backend.
Backend:
Built with Express and TypeScript, running on http://localhost:3000.
Uses express-fileupload for file uploads, cors for cross-origin requests, and uuid for generating unique content IDs.
Integrates intmax2-server-sdk for transaction validation.
Stores content metadata in contents.json and files in client/public.
Blockchain Integration:
Operates on the INTMAX testnet, requiring Metamask configured with an INTMAX testnet account and sufficient ETH.
Payments are made in ETH, with prices converted from USD (1 ETH = $2500).
Transactions are broadcast using broadcastTransaction and validated by checking the digest in fetchTransactionHistory.
User Flow
Initialization: Users initialize the INTMAX client and log in via Metamask.
Creators:
Click "Upload New Content" to open a modal, enter content details, and upload a file.
View uploaded content with "View My Contents" and delete content if needed.
Subscribers:
Browse content in the Subscriber Dashboard.
Click "Subscribe" to pay in ETH, triggering a Metamask transaction.
Access subscribed content in an iframe for 24 hours.
Persistence: Content and files persist across restarts via contents.json and client/public.
Limitations
Subscriptions are stored in memory and reset on server restart (consider a database for production).
The "+18 Content" feature is disabled and requires future implementation.
Assumes 1 ETH = $2500 for price conversion; real-time price feeds should be used in production.
File storage in client/public is not optimized for large-scale use; consider cloud storage for production.
Partner Technologies
INTMAX Testnet:
Benefit: Enables fast, low-cost ETH transactions, ideal for micro-payments ($0.05, $0.25, $1 converted to ETH).
Integration: intmax2-client-sdk handles wallet login and transaction broadcasting, while intmax2-server-sdk validates transactions, ensuring trustless payments.
Metamask:
Benefit: Simplifies user interaction with the INTMAX testnet, allowing seamless wallet connections.
Integration: Users log in via client.login(), and payments are signed in the browser.
Tailwind CSS:
Benefit: Rapid styling with utility classes, reducing CSS boilerplate and ensuring responsiveness.
Integration: Configured in tailwind.config.js, applied to components for consistent design.
Notable Hacks
ETH Price Conversion: Hardcoded 1 ETH = $2500 for simplicity, converting USD prices (e.g., $0.25 = 0.0001 ETH) in useIntMaxClient.ts and SubscriberDashboard.tsx. A production app would use a real-time price oracle.
Transaction Validation: The backend compares transferDigests[0] from broadcastTransaction with digest from fetchTransactionHistory, using a small tolerance (< 0.0000001) for ETH amount comparisons to handle floating-point precision.
File Persistence: Files are stored in client/public and metadata in contents.json. A custom endpoint (/content/:id) deletes both the file and JSON entry, ensuring consistency without a database.
Disabled +18 Button: Added a placeholder button in the upload modal for future age-restricted content, styled but disabled to meet requirements without implementing logic.
Challenges and Solutions
CORS Issues: Fixed by configuring cors middleware to allow requests from http://localhost:5173.
JSX Errors: Resolved by renaming .ts files to .tsx for React components with JSX.
Transaction Validation: Initially failed due to mismatched txHash and digest. Fixed by aligning frontend (transferDigests[0]) and backend (digest) fields.
Persistence: Implemented contents.json to persist content metadata, as in-memory storage was lost on restart.