검증인 데이터 인덱싱
검증인 데이터는 온체인에 있으며 표준 EVM JSON-RPC를 통해 읽을 수 있습니다. 스테이킹, 슬래싱 및 거버넌스 프리컴파일러를 통해 현재 상태를 쿼리하고, 해당 이벤트 로그로부터 기록을 재구성합니다. 이는 인덱서 또는 분석 플랫폼이 노드의 stabled CLI 또는 Cosmos REST에 액세스하지 않고 eth_call 및 eth_getLogs를 통해 필요한 모든 것을 읽는다는 의미입니다.
각 데이터 포인트의 출처
| 데이터 포인트 | 출처 | 읽는 방법 |
|---|---|---|
| 검증인 이름, ID, 웹사이트 | 스테이킹 프리컴파일러 validators() | description.moniker 및 관련 필드 |
| 스테이크 (본딩된 토큰) | 스테이킹 프리컴파일러 validators() | tokens 필드 |
| 수수료 | 스테이킹 프리컴파일러 validators() | commission 필드 |
| 시간 경과에 따른 스테이크 변경 | 스테이킹 프리컴파일러 이벤트 | Delegate, Unbond, Redelegate 로그 |
| 가입 날짜 | 스테이킹 프리컴파일러 이벤트 | CreateValidator 로그 → 블록 타임스탬프 |
| 가동 시간 | 슬래싱 프리컴파일러 getSigningInfos() | (signedBlocksWindow − missedBlocksCounter) / signedBlocksWindow |
| 투표 기록(집계) | 거버넌스 프리컴파일러 getTallyResult() | 제안별 집계 |
| 투표 기록(검증인별) | 거버넌스 프리컴파일러 이벤트 | Vote, VoteWeighted 로그, 투표자 = 운영자 주소 |
프리컴파일러 주소
| 모듈 | 주소 | 용도 |
|---|---|---|
| 스테이킹 | 0x0000000000000000000000000000000000000800 | 검증인 세트, 스테이크, 수수료, 위임 이벤트 |
| 분배 | 0x0000000000000000000000000000000000000801 | 보상 및 수수료 인출 |
| 거버넌스 | 0x0000000000000000000000000000000000000805 | 제안, 집계 및 투표 로그 |
| 슬래싱 | 0x0000000000000000000000000000000000000806 | 서명 정보 및 가동 시간 |
https://rpc.stable.xyz에서 메인넷(체인 ID 988)에 연결하세요. 엔드포인트 및 제한 사항은 메인넷 정보를 참조하세요.
검증인 이름, 스테이크 및 수수료
스테이킹 프리컴파일러에서 validators()를 호출하여 현재 검증인 세트를 읽습니다. 필터링할 본드 상태를 전달합니다(예: BOND_STATUS_BONDED). 각 항목은 검증인의 description(moniker 포함), tokens(본딩된 스테이크) 및 commission을 노출합니다.
// validators.ts
import { createPublicClient, http } from "viem";
const STAKING_PRECOMPILE = "0x0000000000000000000000000000000000000800";
const client = createPublicClient({
transport: http("https://rpc.stable.xyz"),
});
// `validators()` ABI 및 structs에 대한 자세한 내용은 스테이킹 프리컴파일러 참조를 확인하세요.
const validators = await client.readContract({
address: STAKING_PRECOMPILE,
abi: stakingAbi,
functionName: "validators",
args: ["BOND_STATUS_BONDED", { key: "0x", offset: 0n, limit: 100n, countTotal: true, reverse: false }],
});
for (const v of validators[0]) {
console.log(v.description.moniker, v.tokens.toString(), v.commission.toString());
}StableNode-01 4500000000000000000000000 50000000000000000
StableNode-02 3900000000000000000000000 100000000000000000tokens 및 commission 값은 18자리까지 스케일링됩니다. commission을 1e18로 나누어 비율을 분수(예: 5%의 경우 0.05)로 얻습니다. 완전한 Validator 구조체 및 BOND_STATUS_* 값에 대해서는 스테이킹 프리컴파일러 참조를 참조하세요.
시간 경과에 따른 스테이크 변경
validators()는 스냅샷을 반환합니다. 스테이크가 어떻게 이동했는지 추적하려면 스테이킹 프리컴파일러의 위임 이벤트를 인덱싱합니다. Delegate, Unbond, Redelegate는 인덱싱된 validatorAddr과 amount를 포함하므로 모든 스테이크 변경을 검증인 및 블록에 귀속시킬 수 있습니다.
// stakeChanges.ts
import { parseAbiItem } from "viem";
const logs = await client.getLogs({
address: STAKING_PRECOMPILE,
event: parseAbiItem(
"event Delegate(address indexed delegatorAddr, string indexed validatorAddr, uint256 amount, uint256 newShares)"
),
fromBlock: 0n,
toBlock: "latest",
});
console.log(`${logs.length} delegations indexed`);1842 delegations indexedUnbond 및 Redelegate도 동일한 형태를 따르며 추가로 completionTime을 포함합니다. 정확한 서명은 스테이킹 참조의 이벤트 섹션을 참조하세요.
가입 날짜
검증인의 가입 날짜는 CreateValidator 이벤트의 블록 타임스탬프입니다. 이벤트는 검증인 주소로 인덱싱되므로 단일 검증인을 필터링하거나 전체 세트를 스캔한 다음 각 로그의 blockNumber를 eth_getBlockByNumber를 사용하여 타임스탬프로 변환합니다.
// joinDate.ts
import { parseAbiItem } from "viem";
const logs = await client.getLogs({
address: STAKING_PRECOMPILE,
event: parseAbiItem("event CreateValidator(address indexed valiAddr, uint256 value)"),
fromBlock: 0n,
toBlock: "latest",
});
for (const log of logs) {
const block = await client.getBlock({ blockNumber: log.blockNumber });
console.log(log.args.valiAddr, new Date(Number(block.timestamp) * 1000).toISOString());
}0xAbc...123 2025-11-04T09:12:44.000Z
0xDef...456 2025-12-18T17:03:01.000Z가동 시간
슬래싱 프리컴파일러(0x...806)에서 getSigningInfos()를 사용하여 서명 정보를 읽습니다. 각 레코드는 signedBlocksWindow(슬라이딩 윈도우 크기) 및 missedBlocksCounter(그 안에 누락된 블록)를 보고합니다. 가동 시간은 다음과 같이 계산합니다.
가동 시간 = (signedBlocksWindow - missedBlocksCounter) / signedBlocksWindowsignedBlocksWindow가 10000이고 missedBlocksCounter가 25인 검증기는 윈도우 기간 동안 99.75%의 가동 시간을 갖습니다. 이는 평생 가동 시간이 아니라 롤링 수치입니다. 가동 시간 기록을 추적하려면 고정된 간격으로 카운터를 스냅샷하고 각 읽기를 저장합니다.
투표 기록
거버넌스 데이터는 두 가지 계층으로 구성됩니다. 제안의 총 결과를 얻으려면 거버넌스 프리컴파일러(0x...805)에서 getTallyResult()를 호출합니다. 누가 무엇에 투표했는지 확인하려면 Vote 및 VoteWeighted 이벤트 로그를 인덱싱합니다. 이 로그의 투표자 주소는 검증인의 운영자 주소이므로 투표를 검증인에게 직접 연결할 수 있습니다.
// votes.ts
import { parseAbiItem } from "viem";
const GOV_PRECOMPILE = "0x0000000000000000000000000000000000000805";
const logs = await client.getLogs({
address: GOV_PRECOMPILE,
event: parseAbiItem(
"event Vote(uint64 indexed proposalId, address indexed voter, uint8 option, uint256 weight)"
),
fromBlock: 0n,
toBlock: "latest",
});
console.log(`${logs.length} votes indexed across all proposals`);38 votes indexed across all proposals라이브 투표 로그는 현재까지의 모든 제안(제안 #1 ~ #7)에 대해 확인되었습니다. 제안별 최종 개수만 필요할 때는 getTallyResult()를 사용하고, 검증인별 기록이 필요할 때는 이벤트 로그를 사용하세요.
다음 권장 사항
- 스테이킹 프리컴파일러 참조: 전체
validators(), 위임 메서드 및 이벤트 서명을 찾아봅니다. - 검증인 생성: 동기화된 노드를 검증인으로 등록하여 위 데이터에 나타나도록 합니다.
- 인덱서 및 분석: 이미 정규화된 Stable 데이터를 제공하는 인덱싱 제공업체를 찾아봅니다.
- 메인넷 정보: 인덱싱을 시작하기 전에 체인 ID, RPC 엔드포인트 및 속도 제한을 확인합니다.

