
HederaTube
Pay-per-second video on Hedera. Lock a few cents in USDC, watch, get back what you did not use.
Demo
About
Ads and subscriptions exist for one reason. Payments could never be small enough to charge for a few seconds of video. So platforms charge for a month and sell your attention to cover the rest. HederaTube is what a video site looks like when payments can be that small. A video costs a few thousandths of a dollar. You press play and that amount is deposited into an escrow contract on Hedera. As you watch, every 5 second chunk is paid with a signed voucher that never touches the chain. When you leave, whatever you did not watch is refunded in one transaction, and a receipt shows exactly what you paid and what came back. The creator gets everything they earned in a single settlement that can cover thousands of viewers. Three things had to be true for this to work, and none of them existed when we started. First, streaming payments on Hedera. x402 has a batch-settlement scheme for EVM chains, but nothing for Hedera. We wrote the Hedera binding, contracts, SDK, facilitator, spec and examples, and opened it as a PR against the x402 repo (x402-foundation/x402 #3466). It is deployed on testnet and verified on Sourcify. Second, no wallet prompts. A five minute video is about sixty signatures. No one will click approve sixty times. With Privy's embedded wallet we sign every voucher silently, so the viewer presses play once and the stream just pays. Third, real creators. A platform that pays anyone per second watched will be farmed with junk channels and self viewing loops. Creators verify once with a World ID Selfie Check. One human, one channel. Viewers stay anonymous and never verify. The result is a site where the price of a video is the price of a video, not the price of a month, and the creator gets paid for exactly what people watched.
How it's made
The stack is a Vite and React 19 frontend, an Express backend with Drizzle on Postgres, Redis for channel state and BullMQ queues, ffmpeg for transcoding into HLS, Garage as S3 storage, and a standalone x402 facilitator. Everything runs on Hedera testnet at hederatube.xyz. The payment layer is the x402 batch-settlement scheme, which did not exist for Hedera, so we built it and opened PR #3466 upstream. The EVM version verifies signatures with ecrecover and funds channels through Permit2. Neither works on Hedera. Permit2 is not deployed on mainnet, and Hedera accounts can have ED25519 keys that ecrecover cannot check. So the escrow contract, x402BatchSettlementHedera, sends every signature (vouchers, claim batches, refunds, deposit authorizations) through the Hedera Account Service system contract, HIP-632 isAuthorizedRaw, which verifies against the account's current key on the network itself. Deposits go through a new HederaAllowanceDepositCollector that pulls USDC with an HTS transferFrom under an allowance the viewer granted once, plus a per deposit signed authorization with nonce and deadline. The facilitator submits with the Hiero SDK and reads through the Mirror Node, simulating every call before sending. 32 Foundry tests with mocked system contracts, 337 unit tests in the TypeScript package, and a live testnet integration test that locks, pays three chunks, refunds, claims and settles. Pricing is integer math on USDC base units. Segments are 2.5 seconds, chunks are 5 seconds, the deposit is exactly the video price, and the cumulative charge after k chunks is floor(price × k / chunks), so watching to the end pays exactly the price and never more. A custom hls.js fragment loader routes only the first segment of each chunk through x402 with a fresh cumulative voucher. The second segment is free, but the server refuses it unless the first was paid, so a client cannot skip vouchers. Vouchers are verified locally against Redis, never through the facilitator, which is what makes playback feel instant. Privy is what makes any of this usable. External wallets are deliberately not offered because they cannot sign without a prompt. The embedded wallet runs with showWalletUIs off, Hedera registered as a custom EVM chain (296, Hashio RPC), and we sign the raw 32 byte EIP-712 digest through Privy's secp256k1_sign RPC instead of personal_sign, because the EIP-191 prefix would break on chain verification. Each signature is recovered and checked against the wallet address before it leaves the browser. Onboarding is a faucet drip of HBAR that creates the hollow account, then a USDC association, then one approve. Those are the only transactions a viewer ever pays gas for. World ID gates creation, not viewing. The backend signs a relying party context with the portal key, IDKit shows the QR, World App returns a Selfie Check proof (credential 11, World ID 3.0) with the wallet address as signal, and the server verifies it against World's endpoint. The nullifier is stored under a unique index, so a second wallet from the same face gets a 409, and the upload API returns 403 for any wallet without one. We treat Selfie Check as an abuse prevention signal rather than identity, and its 90 day validity doubles as a continuity check for silent channels. Our integration feedback for World is in the repo. Hacky parts worth mentioning. The Mirror Node lags consensus by several seconds, and the facilitator was polling it after every deposit, which made play take 8 seconds. Our server never needs that read, so we made the poll configurable and set it to zero. Play now starts in about 4 seconds, all of it Hedera consensus. Vite's dependency pre-bundling broke IDKit's WebAssembly load, so a small dev plugin serves the wasm from the right path. An approve on a Hedera account that has not associated USDC reverts silently and burns the faucet gas, which cost us an afternoon before we reordered onboarding. And the settlement batch used to run on a 60 second timer, so receipts sat on Settling for a minute. Now every close schedules a batch 4 seconds later and closes inside that window share one job.