Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

컨트랙트 이벤트 인덱싱

인덱싱은 온체인 이벤트를 애플리케이션이 반응할 수 있는 데이터(잔액 업데이트, 거래 내역, UI 알림)로 변환합니다. 이 가이드는 ethers.js를 사용하여 배포된 Stable 컨트랙트에서 이벤트를 구독하는 방법과 서비스가 오프라인일 때 발생한 이벤트를 놓치지 않도록 기록 이벤트를 백필하는 방법을 보여줍니다.

전제 조건

  • Stable 테스트넷 또는 메인넷에 배포된 컨트랙트. 필요한 경우 배포확인을 참조하십시오.
  • Node.js 20 이상.
  • 컨트랙트 주소 및 인덱싱하려는 이벤트의 ABI.

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("NumberUpdated 이벤트 청취 중...");
npx tsx watchLive.ts
NumberUpdated 이벤트 청취 중...
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); // 지난 ~1만 블록
 
const events = await contract.queryFilter(
  contract.filters.NumberUpdated(),
  fromBlock,
  latest
);
 
for (const event of events) {
  console.log(
    `[block ${event.blockNumber}]`,
    event.args.caller,
    "숫자를 다음으로 설정:",
    event.args.newValue.toString()
  );
}
 
console.log(`${fromBlock}에서 ${latest}까지 ${events.length}개의 이벤트를 백필했습니다.`);
npx tsx backfill.ts
[block 1282351] 0x1234...abcd 숫자를 10으로 설정
[block 1283092] 0xef01...2345 숫자를 25로 설정
[block 1284371] 0x1234...abcd 숫자를 42로 설정
1282351에서 1284371까지 3개의 이벤트를 백필했습니다.

4. 인덱싱된 인수에 따라 이벤트 필터링

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}가 숫자를 ${newValue.toString()}로 설정했습니다.`);
});
 
console.log(`${userAddress}의 NumberUpdated를 지켜보고 있습니다...`);
npx tsx watchUser.ts
0x1234...abcd의 NumberUpdated를 지켜보고 있습니다...
0x1234...abcd가 숫자를 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}가 숫자를 ${newValue.toString()}로 설정했습니다.`);
  });
 
  provider.websocket.onerror = (err: any) => {
    console.error("공급자 오류:", err);
    if (reconnectAttempts < MAX_RECONNECT) {
      reconnectAttempts++;
      setTimeout(setupWatcher, 5000);
    }
  };
}
 
setupWatcher();

다음 권장 사항

  • 언본딩 완료 추적: 프로토콜에서 발행한 시스템 트랜잭션 이벤트(언본딩 완료)를 인덱싱합니다.
  • P2P 결제 앱 구축: USDT0 전송 이벤트에 인덱싱을 적용하고 결제 내역 보기를 구축합니다.
  • JSON-RPC 참조: Stable이 지원하는 eth_getLogs 및 관련 메서드를 확인하십시오.