Real-Time Balance Monitor
Monitor multiple addresses with live balance updates using shred state changes.
balance-monitor.ts
import { createPublicClient, webSocket, formatEther } from 'viem'
import { riseTestnet } from 'viem/chains'
import { watchShreds } from 'shreds/viem'
const wsClient = createPublicClient({
chain: riseTestnet,
transport: webSocket()
})
// Track balances for multiple addresses
class BalanceMonitor {
private balances = new Map<string, bigint>()
private monitoredAddresses = new Set<string>()
private unwatch?: () => void
async addAddress(address: string) {
// Get initial balance (only once during setup)
const balance = await wsClient.getBalance({
address: address as `0x${string}`
})
this.balances.set(address.toLowerCase(), balance)
this.monitoredAddresses.add(address.toLowerCase())
console.log(`Monitoring ${address}: ${formatEther(balance)} ETH`)
// Start watching if this is the first address
if (this.monitoredAddresses.size === 1) {
this.startWatching()
}
}
private startWatching() {
// Watch for shreds and use state changes for efficient updates
this.unwatch = watchShreds(wsClient, {
onShred: (shred) => {
// Process state changes from the shred
for (const stateChange of shred.stateChanges) {
const address = stateChange.address.toLowerCase()
// Check if this address is being monitored
if (this.monitoredAddresses.has(address)) {
const oldBalance = this.balances.get(address) || 0n
const newBalance = stateChange.balance
if (newBalance !== oldBalance) {
const change = newBalance - oldBalance
console.log(`[BALANCE] Balance change for ${address}:`)
console.log(` ${formatEther(oldBalance)} -> ${formatEther(newBalance)} ETH`)
console.log(` Change: ${change > 0 ? '+' : ''}${formatEther(change)} ETH`)
this.balances.set(address, newBalance)
}
}
}
},
onError: (error) => {
console.error('Shred watching error:', error)
}
})
}
getBalance(address: string): string {
const balance = this.balances.get(address.toLowerCase()) || 0n
return formatEther(balance)
}
stop() {
this.unwatch?.()
console.log('Stopped monitoring')
}
}
// Usage
const monitor = new BalanceMonitor()
await monitor.addAddress('0x742d35Cc6634C0532925a3b844Bc9e7595f6E98d')
await monitor.addAddress('0x70997970C51812dc3A010C7d01b50e0d17dc79C8')
// Stop monitoring after 60 seconds
setTimeout(() => monitor.stop(), 60000)
Key Features
- Efficient Updates: Uses shred state changes instead of RPC polling
- Multiple Addresses: Monitor any number of addresses simultaneously
- Real-Time: Balance updates arrive within milliseconds of transactions
- Resource Friendly: No RPC calls in the shred callback
How It Works
- Initial Setup: Fetches balance once when adding an address
- State Changes: Shreds include balance changes for affected addresses
- Instant Updates: Balance changes are detected and logged immediately
- No Polling: Uses WebSocket subscription for push-based updates
Best Practices
- Always use state changes from shreds for real-time data
- Fetch initial state once, then rely on updates
- Handle errors gracefully in the shred callback
- Clean up subscriptions when done
Next Steps
- Add persistence to track balance history
- Create a web interface with real-time charts
- Add notifications for large balance changes
- Export balance data for analysis