Real-time token streaming on Stellar. Send tokens that unlock continuously by the second — perfect for payroll, token vesting, and grants. Built on Soroban smart contracts.
Inspired by Streamflow on Solana.
Testnet deployment — connect a Freighter wallet set to Stellar testnet to try it.
Contract: CBNDCZTRFNTDAPQLPK2ESOKO4XFMSC4PX37QE75BBYFOYIEWIPMHAKFV (Testnet)
- Per-second unlocking — funds stream continuously, recipients withdraw anytime
- Cliff support — set a cliff date with an optional lump-sum unlock
- Cancel anytime — sender cancels and gets unstreamed tokens back, recipient keeps what unlocked
- Non-custodial — contract holds funds, no intermediary
- Multi-token — XLM, USDC, EURC (any SEP-41 token)
| Layer | Tech |
|---|---|
| Frontend | Next.js 16, React 19, Tailwind CSS 4, shadcn/ui |
| Smart Contract | Rust, Soroban SDK v26 |
| Blockchain | Stellar (Soroban) |
| Wallet | Freighter via @stellar/freighter-api |
| RPC | Stellar Soroban Testnet RPC |
├── app/ # Next.js app router pages
│ └── app/ # Protected app area
│ ├── page.tsx # Dashboard
│ ├── streams/ # All streams list
│ ├── create/ # Create stream form
│ └── stream/[id]/ # Stream detail + withdraw/cancel
├── components/
│ ├── landing/ # Marketing landing page
│ ├── layout/ # Navbar, wallet button, auth gate
│ ├── streams/ # Stream card, stats, empty state
│ └── ui/ # shadcn/ui primitives
├── contracts/
│ └── streaming/ # Soroban smart contract (Rust)
│ └── src/
│ ├── lib.rs # Contract logic
│ ├── test.rs # Unit tests (13 tests)
│ └── test_security.rs # Security tests (31 tests)
├── hooks/ # useStreams, useContract, useWallet, useNow
├── lib/
│ ├── contract.ts # Contract integration layer
│ ├── stellar.ts # Network config + RPC client
│ ├── stream-utils.ts # Unlock math, formatters
│ └── mock-data.ts # Dev mock store
└── types/stream.ts # StreamData, TokenInfo, CreateStreamInput
The Soroban contract is at contracts/streaming/. It handles:
| Function | Description |
|---|---|
create_stream |
Fund a new stream (requires prior approve on token) |
withdraw |
Recipient withdraws unlocked tokens |
cancel |
Sender cancels — recipient gets unlocked portion, sender gets remainder |
get_stream |
Read stream by ID |
get_withdrawable |
Current withdrawable amount |
get_sent_streams |
All stream IDs sent by an address |
get_received_streams |
All stream IDs received by an address |
unlocked = cliffAmount + (elapsed × amountPerSecond)
Capped at depositedAmount. The cliff blocks any unlock until cliffTime is reached.
cd contracts
cargo test44 tests pass covering the full lifecycle, authorization, overdraw protection, cliff edge cases, integer math, and self-streams.
- Node.js 18+
- Freighter browser extension (set to Testnet)
npm install
npm run devOpen http://localhost:3000.
# .env.local
NEXT_PUBLIC_STREAM_CONTRACT_ID=CBNDCZTRFNTDAPQLPK2ESOKO4XFMSC4PX37QE75BBYFOYIEWIPMHAKFVThe contract is already deployed to testnet. For mainnet, deploy your own and update this value.
# Install stellar-cli
cargo install stellar-cli --locked
# Generate a deployer key and fund it
stellar keys generate deployer --network testnet
stellar keys fund deployer --network testnet
# Build
cd contracts/streaming
stellar contract build
# Deploy
stellar contract deploy \
--wasm contracts/target/wasm32v1-none/release/flowstar_streaming.wasm \
--source deployer \
--network testnet- Install Freighter and switch to Testnet
- Fund your wallet at Stellar Laboratory Friendbot
- Open the app at
localhost:3000 - Connect Freighter → Create a stream → use this as the recipient address for testing:
GBWEDYWFGPNPAWCYOKWMCRPTR4IMV4SNZ7CVOZHPUXGHVXXPJSCFKVXQ
You'll sign two transactions: one approve on the token contract, then create_stream.
lib/contract.tsis the single integration boundary — swap mock ↔ real by changingUSE_MOCK- Stream unlock math runs client-side in
lib/stream-utils.tsfor live UI counters without polling - The contract uses
Persistentstorage with TTL extensions on every write (~30 days per stream) - All token amounts use
bigint(i128/u64) to match Soroban types exactly — no precision loss
MIT
