UserStream is a production-ready Next.js app that tracks EVM and Solana wallet activity in real time using Quicknode Streams with intelligent, real-time filtering powered by Quicknode KV Store.
Add or remove wallet addresses through the UI (single or bulk import), and the Quicknode Streams filter updates instantly via KV Store lists. No stream restart required - start monitoring an address and see their transactions immediately.
Track both EVM (Ethereum, Base, Arbitrum, etc.) and Solana wallets with dedicated filters for each chain. Native transfers, ERC-20 tokens, and SPL tokens are all supported.
Filtering EVM and Solana blocks happens at the stream level using Quicknode Streams Filter and Quicknode KV lists, not in your application code. This means you only process the exact data you need - no wasted bandwidth or compute on irrelevant transactions.
Live activity feed with transaction details powered by Server-Sent Events (SSE) - see new transactions appear instantly without polling.
Pre-built scripts for Quicknode Streams setup and activation, plus Prisma migrations - get up and running in minutes.
Blockchain (EVM/Solana)
-> Quicknode Streams + KV
-> POST /api/webhook/streams (signature verified)
-> DB insert + SSE emit
-> Live UI activity feed
- Node.js 20+ and a package manager (pnpm/npm/yarn)
- Quicknode account with API key that has Streams + KV access
- Public webhook URL for local development (use ngrok or similar tunneling service)
- Git for cloning the repository
Optional but highly recommended (for enhanced metadata):
- Ethereum mainnet RPC URL from Quicknode (for ENS resolution and ERC-20 metadata)
- Solana RPC URL from Quicknode (for SPL token metadata)
| Variable | Required | Description | Where to Get |
|---|---|---|---|
QN_API_KEY |
Yes | Quicknode API key with Streams + KV access | Quicknode Dashboard |
APP_URL |
Yes | Public webhook URL (must be reachable by Quicknode) | Use ngrok for local dev (see setup below) |
QN_STREAM_SECURITY_TOKEN_EVM |
For EVM | EVM stream security token | Generated by pnpm run setup:streams |
QN_STREAM_SECURITY_TOKEN_SOL |
For Solana | Solana stream security token | Generated by pnpm run setup:streams chain=solana-mainnet |
DATABASE_URL |
Yes | SQLite database path | file:./dev.db (default) |
QN_EVM_ENDPOINT |
No | Ethereum RPC for ENS/ERC-20 metadata | Quicknode Endpoints |
QN_SOLANA_ENDPOINT |
No | Solana RPC for SPL token metadata | Quicknode Endpoints |
Important Notes:
- Never commit
.envto git - it contains sensitive credentials QN_API_KEYmust have both Streams and KV permissions enabledAPP_URLmust be set before runningsetup:streams(it becomes the webhook destination)
git clone https://github.com/quiknode-labs/qn-guide-examples.git
cd qn-guide-examples/Streams/userstream# Using pnpm
pnpm install
# OR using npm
npm install
# OR using yarn
yarn installcp .env.example .envIMPORTANT: Edit .env file NOW before proceeding. You must configure:
-
QN_API_KEY: Get from Quicknode Dashboard- Ensure your API key has Streams and KV permissions enabled
-
APP_URL: Your public webhook URL (required for stream setup)- This URL must be publicly accessible by Quicknode
- For local development, set up ngrok:
-
QN_EVM_ENDPOINT(optional but recommended): Ethereum RPC endpoint for ENS resolution and ERC-20 token metadata -
QN_SOLANA_ENDPOINT(optional but recommended): Solana RPC endpoint for SPL token metadata
Plan considerations and minimal setup (Free trial plan)
Some Quicknode plans limit the number of available Endpoints and active Streams (for example, the Free trial plan typically allows a single endpoint and a single active stream).
The setup instructions describe the full EVM + Solana workflow. If you encounter plan-limit errors during setup, use the minimal EVM-only path to run the app end-to-end without upgrading.
This path uses:
- One EVM endpoint (Ethereum Mainnet)
- One active EVM stream
- No Solana activation
Follow the same setup steps below, with these adjustments:
- Configure EVM only
- Skip Solana endpoint and stream setup
- Activate a single EVM stream
- Ensure
APP_URLis publicly reachable (e.g. via ngrok for local dev)
The UI will remain empty until at least one wallet is added for monitoring.
Running EVM + Solana simultaneously (multiple endpoints and/or active streams) requires a plan that supports higher limits (e.g. the Build plan).
See available plans for details.
For local development, set up ngrok:
# Install ngrok (macOS)
brew install ngrok
# OR download from https://ngrok.com/download
# Start ngrok tunnel pointing to port 3000
ngrok http 3000Copy the https:// URL from ngrok output (e.g., https://abc123.ngrok-free.app) and add it to your .env file:
APP_URL="https://abc123.ngrok-free.app"Tip: Keep ngrok running in a separate terminal. The URL remains valid as long as ngrok is running.
# Using pnpm
pnpm prisma migrate dev --name init
# OR using npm
npx prisma migrate dev --name init
# OR using yarn
yarn prisma migrate dev --name init# Using pnpm
pnpm run setup:streams
# OR using npm
npm run setup:streams
# OR using yarn
yarn run setup:streamsThis script will:
- Create KV list for address filtering (
userstream_monitored_users_evm) - Test the filter on a known block
- Create a paused stream with your
APP_URLas the destination - Print a
QN_STREAM_SECURITY_TOKEN_EVM
Copy the security token from the output and add it to your .env file:
QN_STREAM_SECURITY_TOKEN_EVM="qnsec_..."If you want Solana, run the command again with mainnet or devnet selected:
pnpm run setup:streams chain=solana-mainnetThis script will:
- Create KV list for address filtering (
userstream_monitored_users_sol) - Test the filter on a known block
- Create a paused stream with your
APP_URLas the destination - Print a
QN_STREAM_SECURITY_TOKEN_SOL
Copy the security token from the output and add it to your .env file:
QN_STREAM_SECURITY_TOKEN_SOL="qnsec_..."Start the Next.js development server in a new terminal: (At this point, ngrok should still be running)
# Using pnpm
pnpm dev
# OR using npm
npm run dev
# OR using yarn
yarn dev# Activate the most recently created stream
pnpm run activate:streams
# OR using npm
npm run activate:streams
# OR using yarn
yarn run activate:streamsTo activate a specific chain's stream:
pnpm run activate:streams chain=ethereum-mainnet
pnpm run activate:streams chain=solana-mainnet- Open http://localhost:3000
- Add wallet addresses using the "Add Monitored Address" form, or use bulk import
- Activity from monitored addresses will start flowing to your webhook in real-time
When you add an address:
- The address is immediately added to the KV Store list
- The Quicknode Stream filter picks it up instantly - no restart needed
- Transactions from this address will start flowing to your webhook in real-time
Sample Ethereum addresses to try:
0xE592427A0AEce92De3Edee1F18E0157C05861564, Uniswap V3 Router
Sample Solana addresses to try:
3CgvbiM3op4vjrrjH2zcrQUwsqh5veNVRjFCB9N6sRoD, Jupiter Aggregator Authority 4
Once configured, transactions matching your filters will stream in real-time!
Note: The default setup uses Ethereum mainnet. The same EVM filter works for other EVM chains (Base, Arbitrum, Polygon, etc.) - just specify the chain and an appropriate test block number when running
setup:streamsand set up yourQN_EVM_ENDPOINTaccordingly.
Learn more about Quicknode Streams and KV Store in the official documentation.
The filters are provided in the filters/ directory:
filters/evm-filter.js- EVM filter for native ETH and ERC-20 transfersfilters/solana-filter.js- Solana filter for native SOL and SPL token transfers
When you run pnpm run setup:streams, it will:
- Create KV list:
userstream_monitored_users_evmoruserstream_monitored_users_sol - Test the filter on a known block
- Create a stream (paused) and print the security token
You can override options with key=value arguments:
pnpm run setup:streams chain=ethereum-mainnet name="UserStream EVM Monitor" test_block_number=24223192
pnpm run setup:streams chain=solana-mainnet name="UserStream Solana Monitor" test_block_number=393612994Activate the most recently created stream:
pnpm run activate:streamsFor more details, see the Quicknode Streams Documentation.
SQLite is used by default. Prisma schema lives in prisma/schema.prisma.
Common commands:
pnpm prisma migrate dev --name init
pnpm prisma studio
pnpm db:resetWebhook endpoint: POST /api/webhook/streams
Required headers:
x-qn-noncex-qn-timestampx-qn-signature
The request can be gzip-compressed. The handler auto-detects content-encoding: gzip.
Signature verification uses QN_STREAM_SECURITY_TOKEN_EVM or QN_STREAM_SECURITY_TOKEN_SOL depending on the chain.
GET /api/health- health checkGET /api/users- list monitored addressesPOST /api/users- add address (walletAddress, optionalname)PATCH /api/users?id=...- update address name/displayNameDELETE /api/users?id=...- remove addressPOST /api/users/bulk- bulk add (newline-separated, supports "address, label")POST /api/webhook/streams- Quicknode Streams webhookPOST /api/webhook/test- local-only webhook testGET /api/sse- Server-Sent Events stream of activity events
filters/
evm-filter.js # EVM Streams filter
solana-filter.js # Solana Streams filter
scripts/
setup-streams.ts # Creates KV lists + stream
activate-streams.ts # Activates stream by id
reset-kv.ts # Deletes KV lists
prisma/
schema.prisma # SQLite schema
src/
app/ # App Router routes + pages
components/ # UI components
lib/ # Quicknode, webhook, SSE helpers
types/ # Shared types
pnpm dev # Start development server
pnpm build # Build for production
pnpm start # Start production server
pnpm lint # Run ESLint
pnpm typecheck # Run TypeScript type checking
pnpm test # Run tests
pnpm run setup:streams # Create Quicknode Stream + KV lists
pnpm run activate:streams # Activate the stream
pnpm db:reset # Reset database
pnpm reset:kv # Delete KV listsUnit tests use Vitest. Webhook and SSE routes have dedicated tests under src/app/api/__tests__.
pnpm testMissing webhook events:
- Ensure
APP_URLis publicly reachable and the stream is active - Check that ngrok is still running (URLs expire when ngrok stops)
- Verify stream status in Quicknode Dashboard
- Check logs:
pnpm run activate:streamsshould show stream as "running"
401 Unauthorized from webhook:
- Verify
QN_STREAM_SECURITY_TOKEN_EVMorQN_STREAM_SECURITY_TOKEN_SOLmatches the token from setup output - Check Quicknode signature headers are present (
x-qn-signature,x-qn-timestamp,x-qn-nonce) - Ensure you haven't modified the webhook URL after stream creation
KV list errors:
- Confirm
QN_API_KEYhas both Streams and KV access enabled - Check your Quicknode Dashboard API Keys for permissions
- Verify KV lists exist:
userstream_monitored_users_evmoruserstream_monitored_users_sol
ENS resolution issues:
- Confirm
QN_EVM_ENDPOINTpoints to Ethereum mainnet - Get an Ethereum endpoint from Quicknode
- ENS resolution is optional - addresses will work without it
SPL token metadata missing:
- Confirm
QN_SOLANA_ENDPOINTpoints to a Solana RPC endpoint - Get a Solana endpoint from Quicknode
- SPL metadata is optional - transfers will still be tracked
Address not being tracked:
- For EVM: addresses are normalized to lowercase - this is handled automatically
- For Solana: addresses are case-sensitive (Base58 format) - ensure exact match
- Verify the address was added to the KV list via the UI
- Quicknode Streams Guides
- Quicknode KV Store Docs
- Quicknode Support
- GitHub Issues for this repository
MIT
