Realtime: SSE & WebSocket
Two real-time channels: Server-Sent Events from the BaniPool API for account events, and the network RPC WebSocket (WSS) for live prices & blocks straight from the blockchain.
Server-Sent Events (SSE)
GET /v1/events/stream streams events for your session/key (finalized/failed transactions, positions out of range, strategy executions) plus public data.degraded. Each event carries an id cursor; reconnect with Last-Event-ID (automatic in EventSource) or ?after=. The server closes the connection after ~100 seconds for browsers and 15 minutes for API keys — just reconnect.
// Browser (sesi same-origin; jangan menaruh API key di URL)
const es = new EventSource('/api/v1/events/stream');
es.addEventListener('transaction.finalized', (e) => console.log(e.lastEventId, JSON.parse(e.data)));
es.addEventListener('position.out_of_range', (e) => console.log(JSON.parse(e.data)));
es.addEventListener('reset', () => { /* cursor kedaluwarsa: ambil snapshot baru */ });// Node.js / server dengan API key (header, bukan query string)
const res = await fetch('https://banipool.com/api/v1/events/stream', {
headers: { 'x-api-key': process.env.BANIPOOL_API_KEY!, accept: 'text/event-stream' },
});
const reader = res.body!.pipeThrough(new TextDecoderStream()).getReader();
let buf = '';
for (;;) {
const { value, done } = await reader.read();
if (done) break; // sambung ulang dengan header Last-Event-ID
buf += value;
let i;
while ((i = buf.indexOf('\n\n')) >= 0) {
const frame = buf.slice(0, i); buf = buf.slice(i + 2);
const field = (k: string) => frame.split('\n').find((l) => l.startsWith(k + ': '))?.slice(k.length + 2);
if (field('event')) console.log(field('id'), field('event'), field('data'));
}
}| Events | When |
|---|---|
transaction.finalized | A transaction you made through BaniPool is final on the blockchain. |
transaction.failed | The transaction reverted/failed. |
position.out_of_range | A position under a rule went out of range. |
strategy.executed | A paper/notify rule triggered. |
data.degraded | A network public data source degraded (public). |
reset / revoked | Cursor past retention / key revoked or session ended. |
WebSocket RPC (WSS)
The BaniPool API does not open its own WebSocket; real-time on-chain data (new blocks, pool swap events) is read directly from the network WSS RPC. The site uses it for live Zap quotes, and you can set your own WSS at /settings. For your app, use an RPC provider WSS (Alchemy, QuickNode, Infura, dRPC, or the network official RPC).
import { createPublicClient, webSocket, parseAbiItem } from 'viem';
import { base } from 'viem/chains';
const client = createPublicClient({
chain: base,
transport: webSocket(process.env.BASE_WSS_URL), // wss://base-mainnet.g.alchemy.com/v2/<KEY>
});
// Blok baru
client.watchBlockNumber({ onBlockNumber: (n) => console.log('block', n) });
// Event Swap pool Uniswap v3 (alamat pool dari GET /v1/pools)
client.watchEvent({
address: '0xd0b53D9277642d899DF5C87A3966A349A798F224', // WETH/USDC 0,05% Base
event: parseAbiItem('event Swap(address indexed sender, address indexed recipient, int256 amount0, int256 amount1, uint160 sqrtPriceX96, uint128 liquidity, int24 tick)'),
onLogs: (logs) => logs.forEach((l) => console.log('tick', l.args.tick)),
});// Tanpa library: JSON-RPC eth_subscribe langsung
const ws = new WebSocket(process.env.BASE_WSS_URL);
ws.onopen = () => ws.send(JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_subscribe', params: ['newHeads'] }));
ws.onmessage = (m) => {
const msg = JSON.parse(m.data);
if (msg.method === 'eth_subscription') console.log('block', parseInt(msg.params.result.number, 16));
};Provider WSS URLs usually contain an account key (e.g. Alchemy). Store them as secrets on your server; in the browser, use the site WSS setting, which stores it encrypted locally.