实时:SSE 与 WebSocket
两种实时数据通道:来自 BaniPool API 的服务器发送事件(SSE),用于账户事件;以及网络 RPC WebSocket(WSS),用于直接从区块链获取实时价格和区块。
服务器发送事件(SSE)
GET /v1/events/stream 推送属于您会话/密钥的事件(交易最终确认/失败、仓位超出区间、策略执行)以及公开的 data.degraded。每个事件带有作为游标的 id;可通过 Last-Event-ID(EventSource 会自动处理)或 ?after= 重新连接。服务器会在约 100 秒(浏览器)或 15 分钟(API 密钥)后关闭连接——直接重连即可。
ts
// 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 */ });ts
// 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'));
}
}| 事件 | 触发时机 |
|---|---|
transaction.finalized | 您通过 BaniPool 发起的交易在区块链上已最终确认。 |
transaction.failed | 交易回滚/失败。 |
position.out_of_range | 规则下的仓位超出价格区间。 |
strategy.executed | paper/notify 规则被触发。 |
data.degraded | 某网络的公共数据源性能下降(公开)。 |
reset / revoked | 游标超出保留期 / 密钥被撤销或会话结束。 |
WebSocket RPC(WSS)
BaniPool API 本身不提供 WebSocket;实时链上数据(新区块、资金池兑换事件)直接从网络的 WSS RPC 读取。网站将其用于 Zap 实时报价,您也可以在 /settings 中设置自己的 WSS。在您的应用中,请使用 RPC 服务商的 WSS(Alchemy、QuickNode、Infura、dRPC 或网络官方 RPC)。
ts
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)),
});js
// 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));
};服务商的 WSS URL 通常包含账户密钥(例如 Alchemy)。请在服务器上将其作为机密保存;在浏览器中,请使用网站的 WSS 设置,它会在本地加密保存。