컨트랙트 이벤트 인덱싱
인덱싱은 온체인 이벤트를 애플리케이션이 반응할 수 있는 데이터로 변환합니다: 잔액 업데이트, 거래 내역, UI 알림 등이 그 예입니다. 이 가이드에서는 ethers.js를 사용해 배포된 Stable 컨트랙트의 이벤트를 구독하는 방법과, 서비스가 오프라인 상태일 때 발생한 이벤트를 놓치지 않도록 과거 이벤트를 백필하는 방법을 설명합니다.
사전 준비
1. 설치 및 구성
npm install ethers// config.ts
import { ethers } from "ethers";
export const STABLE_TESTNET_RPC = "https://rpc.testnet.stable.xyz";
export const STABLE_TESTNET_WS = "wss://rpc.testnet.stable.xyz";
export const CONTRACT_ADDRESS = "0xDeployedContractAddress";
// Minimal ABI: only the events you want to index.
export const CONTRACT_ABI = [
"event NumberUpdated(address indexed caller, uint256 oldValue, uint256 newValue)",
];2. 실시간 이벤트 구독
WebSocket 프로바이더를 사용하면 검증자가 각 블록을 확정하는 즉시 이벤트를 받을 수 있습니다. WebSocket은 폴링 오버헤드를 피하고 알림 지연 시간을 블록 타임(Stable에서 약 0.7초)에 가깝게 유지합니다.
// watchLive.ts
import { ethers } from "ethers";
import { STABLE_TESTNET_WS, CONTRACT_ADDRESS, CONTRACT_ABI } from "./config";
const provider = new ethers.WebSocketProvider(STABLE_TESTNET_WS);
const contract = new ethers.Contract(CONTRACT_ADDRESS, CONTRACT_ABI, provider);
contract.on("NumberUpdated", (caller, oldValue, newValue, event) => {
console.log("NumberUpdated:");
console.log(" caller: ", caller);
console.log(" oldValue: ", oldValue.toString());
console.log(" newValue: ", newValue.toString());
console.log(" tx: ", event.log.transactionHash);
console.log(" block: ", event.log.blockNumber);
});
console.log("Listening for NumberUpdated events...");npx tsx watchLive.tsListening for NumberUpdated events...
NumberUpdated:
caller: 0x1234...abcd
oldValue: 0
newValue: 42
tx: 0x8f3a...2d41
block: 1284371호출자가 컨트랙트를 호출하면 이벤트가 실시간으로 도착합니다.
3. 과거 이벤트 백필
서비스가 시작될 때는 보통 오프라인 상태일 때 발생한 이벤트를 따라잡아야 합니다. 블록 범위와 함께 queryFilter를 사용하세요.
// backfill.ts
import { ethers } from "ethers";
import { STABLE_TESTNET_RPC, CONTRACT_ADDRESS, CONTRACT_ABI } from "./config";
const provider = new ethers.JsonRpcProvider(STABLE_TESTNET_RPC);
const contract = new ethers.Contract(CONTRACT_ADDRESS, CONTRACT_ABI, provider);
const latest = await provider.getBlockNumber();
const fromBlock = Math.max(0, latest - 10_000); // last ~10k blocks
const events = await contract.queryFilter(
contract.filters.NumberUpdated(),
fromBlock,
latest
);
for (const event of events) {
console.log(
`[block ${event.blockNumber}]`,
event.args.caller,
"set number to",
event.args.newValue.toString()
);
}
console.log(`Backfilled ${events.length} events from block ${fromBlock} to ${latest}`);npx tsx backfill.ts[block 1282351] 0x1234...abcd set number to 10
[block 1283092] 0xef01...2345 set number to 25
[block 1284371] 0x1234...abcd set number to 42
Backfilled 3 events from block 1282351 to 12843714. 인덱싱된 인수로 이벤트 필터링
indexed 매개변수가 있는 이벤트(위의 caller 등)는 서버 측에서 필터링할 수 있습니다. 모든 이벤트를 읽고 앱에서 필터링하는 대신 필터 값을 전달하세요.
// watchUser.ts
import { ethers } from "ethers";
import { STABLE_TESTNET_WS, CONTRACT_ADDRESS, CONTRACT_ABI } from "./config";
const provider = new ethers.WebSocketProvider(STABLE_TESTNET_WS);
const contract = new ethers.Contract(CONTRACT_ADDRESS, CONTRACT_ABI, provider);
const userAddress = "0x1234...abcd";
const filter = contract.filters.NumberUpdated(userAddress);
contract.on(filter, (caller, oldValue, newValue, event) => {
console.log(`${caller} set number to ${newValue.toString()}`);
});
console.log(`Watching NumberUpdated for ${userAddress}...`);npx tsx watchUser.tsWatching NumberUpdated for 0x1234...abcd...
0x1234...abcd set number to 42연결 끊김 처리
WebSocket 연결은 끊길 수 있습니다. 프로덕션 인덱서의 경우 이벤트를 놓치지 않도록 재연결 로직을 구현하세요.
// resilientWatch.ts
import { ethers } from "ethers";
import { STABLE_TESTNET_WS, CONTRACT_ADDRESS, CONTRACT_ABI } from "./config";
let reconnectAttempts = 0;
const MAX_RECONNECT = 5;
function setupWatcher() {
const provider = new ethers.WebSocketProvider(STABLE_TESTNET_WS);
const contract = new ethers.Contract(CONTRACT_ADDRESS, CONTRACT_ABI, provider);
contract.on("NumberUpdated", (caller, oldValue, newValue) => {
console.log(`${caller} set number to ${newValue.toString()}`);
});
provider.websocket.onerror = (err: any) => {
console.error("Provider error:", err);
if (reconnectAttempts < MAX_RECONNECT) {
reconnectAttempts++;
setTimeout(setupWatcher, 5000);
}
};
}
setupWatcher();다음 추천
- 언본딩 완료 추적 — 프로토콜에서 발생하는 시스템 트랜잭션 이벤트(언본딩 완료)를 인덱싱합니다.
- P2P 결제 앱 구축 — USDT0 Transfer 이벤트에 인덱싱을 적용하고 결제 내역 화면을 구축합니다.
- JSON-RPC 레퍼런스 — Stable이 지원하는
eth_getLogs및 관련 메서드를 확인합니다.

