Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ProValidator Staking API

Vercel Functions 기반 스테이킹 스탯 API. 기존 provalidator_info_api.php 대체용.

왜 JSON 파일 저장을 안 쓰는가

Vercel 서버리스 함수의 파일시스템은 읽기 전용이고, /tmp 는 인스턴스마다 따로 존재하다 사라집니다. 크론이 A 인스턴스에 JSON 을 써도 사용자 요청은 B 인스턴스로 갑니다.

대신 CDN 캐시 + KV 폴백 2단 구조를 씁니다:

요청 → Vercel Edge CDN
        ├─ 캐시 hit (60초 이내)      → 즉시 응답, 함수 실행 0회
        └─ 캐시 stale/miss           → 함수 실행
                                        ├─ Cosmos LCD 병렬 수집 → Upstash KV 저장 → 응답
                                        └─ 수집 실패 → KV 의 마지막 성공값 → 없으면 static 폴백

stale-while-revalidate 덕분에, 캐시가 만료돼도 사용자는 옛날 값을 즉시 받고 갱신은 백그라운드에서 일어납니다. 체인 RPC 가 느리거나 죽어도 응답 속도와 가용성이 유지됩니다. 크론 불필요.

엔드포인트

베이스: /api/stats (/provalidator_info_api.php 로도 접근 가능 — vercel.json rewrite)

요청 설명
?endpoint=chains 신규. 전체 체인 + 글로벌 스탯을 한 번에
?endpoint=chain_stats&token=ATOM 체인 1개 (chain_id / chain 도 동일하게 동작)
?endpoint=global_stats 합산 스탯 (하드코딩 아님 — 체인 데이터에서 계산)
?endpoint=health 진단용. KV 연결 상태를 왕복 테스트로 확인 (캐시 안 함)

Framer 가 체인마다 호출하고 있다면 endpoint=chains 한 번으로 바꾸는 걸 권장합니다.

PHP 버전과 달라진 점

  • 모든 수치가 문자열이 아니라 number
  • aprapr_percent둘 다 백분율, 소수점 2자리(14.5 = 14.5%). 프론트가 apr 을 그대로 % 로 쓰기 때문입니다
  • global_stats 는 체인 데이터에서 합산 계산
  • 응답에 source / price_source 필드 추가: live | cached | static
  • chain_idtoken 으로 이름 정리 (구 파라미터도 계속 동작)

응답 예시

{
  "message": "Success",
  "data": {
    "project": {
      "chain_id": "cosmos",
      "project_title": "Cosmos Hub",
      "token": "ATOM",
      "type": "validator",
      "logo": "https://coin-images.coingecko.com/coins/images/1481/large/cosmos_hub.png",
      "fees": 5.0,
      "apr": 14.21,
      "apr_percent": 14.21,
      "token_price": 8.45,
      "staked_amount": 1234567.89,
      "staked_amount_usd": 10432098.67,
      "delegators": 5231,
      "market_cap": 727053354.99,
      "source": "live",
      "price_source": "live",
      "timestamp": 1754800000
    }
  }
}

현재 데이터 소스

응답의 type 필드로 두 종류가 구분됩니다.

type: "validator" — 프로발리데이터가 밸리데이터를 운영하는 7개 체인

체인 체인 데이터 가격 / 시총
Cosmos Hub, Osmosis, Axelar, Agoric, AtomOne live (커미션, 위임량, 위임자 수, 순 APR) live (CoinGecko)
Aptos live (커미션, 위임량, 순 APR) live (CoinGecko)
Monad live (커미션, 위임량) live (CoinGecko)

type: "asset" — 밸리데이터를 운영하지 않고 추적만 하는 12개 자산

ZETA · XPRT · NIL · NOBL · SSV · BTC · ETH · SOL · USDC · HYPE · DATA · DYDX

staked_amount delegators fees 는 전부 null 이고 total_assets_usd_value / total_delegators / total_chains 합산에도 들어가지 않습니다. 즉 글로벌 스탯은 항상 "우리가 실제로 운영하는 밸리데이터"만 집계합니다.

apr 의 의미는 type 에 따라 다릅니다:

type apr 의미
validator 프로발리데이터에게 위임했을 때의 순 APR (커미션 차감 후)
asset 그 네트워크의 기준 APR (커미션 차감 전)

네트워크 APR 산출 (lib/networks.ts)

체인마다 보상 구조가 전혀 달라서 소스별로 따로 계산합니다. 결과는 KV 에 10분 캐시합니다.

자산 방식 실측값
SOL getInflationRate × 총 발행량 ÷ 총 활성 스테이크 (Solana RPC) 5.38%
ETH 64 × 연간 에포크 수 ÷ √(총 유효잔고) — 컨센서스 스펙 공식 2.57%
ZETA x/emissions 의 블록 보상 × validator_emission_percentage × 연간 블록 수 ÷ 본딩량 8.52%
XPRT 표준 x/mint (annual_provisions × (1-community_tax) ÷ bonded) 23.42%
HYPE 공식 문서 앵커(400M 스테이크 = 2.37%)와 1/√(총 스테이크) 비례 관계 2.27%
  • ETH 는 발행(issuance) 기준이며 MEV·팁은 제외입니다. 자체 계산값이 ultrasound.money 가 보고하는 issuance APR 과 소수점 3자리까지 일치하는 것을 확인했습니다.
  • ZETA 는 블록 시간이 고정값이 아니라서 최근 블록 2,000개 간격으로 실측해 환산합니다.
  • HYPE 만 온체인 파라미터가 아니라 문서에 명시된 기준값에서 환산한 값입니다. Hyperliquid 는 보상률 공식을 공개하지 않고 앵커 하나만 제시합니다.

APR 을 낼 수 없는 자산apr: null 로 나갑니다.

자산 이유
DYDX 보상이 인플레이션이 아니라 거래 수수료(USDC) 분배
NOBL 퍼미션드 밸리데이터 셋, 공개 스테이킹 없음 (bonded 8 토큰)
DATA 공개 Cosmos REST 엔드포인트가 없음 (EVM RPC 만 동작)
BTC · USDC 스테이킹 개념 자체가 없음
NIL 공식 엔드포인트(nilchain-api.nillion.network)에 접근 불가 — 아래 참고
SSV 스테이킹은 존재하나 조회 가능한 공개 API 가 없음 — 아래 참고

NIL (Nillion)

⚠️ nillion-api.polkachu.com 은 Nillion 이 아닙니다. node_infoapp_nameallorad (Allora) 로 나오고 unil 공급량이 0 입니다. 다른 체인 노드가 붙어 있습니다.

공식 엔드포인트는 https://nilchain-api.nillion.network 인데 현재 개발 환경에서 DNS 조차 해석되지 않아 검증하지 못했습니다. 네트워크 제약일 수도 있으니, 아래가 응답하면 lib/chains.ts 의 nillion 항목에 networkApr: 'cosmos-mint'rest 를 넣으면 바로 동작합니다.

curl "https://nilchain-api.nillion.network/cosmos/mint/v1beta1/annual_provisions"

SSV

SSV 는 컨센서스 스테이킹이 아닙니다. SSV 를 예치하면 cSSV 를 받고 보상이 ETH 로 지급되는 구조이고, 수익률이 네트워크 수수료와 총 예치 비율에 따라 변동합니다. 온체인 파라미터 하나로 환산되지 않고 공개 조회 API 도 확인되지 않아 null 로 둡니다.

Story Protocol 은 Data Network 로 리브랜딩되어 심볼이 IPDATA 로 바뀌었습니다. 기존 사이트가 아직 IP 로 조회하므로 별칭으로 계속 받아줍니다 (?chain=IP 동작).

NOBL 은 가격도 null 입니다 — CoinGecko 미등재라서요 (검색 결과가 브릿지된 USDC 뿐). 등재되면 lib/chains.tscoingeckoId 만 채우면 됩니다.

로고

응답의 logo 필드에 CoinGecko 가 호스팅하는 토큰 로고 URL 이 들어갑니다. 가격 조회를 simple/price 에서 coins/markets 로 바꿔 요청 추가 없이 함께 받아옵니다. 프론트에서 아이콘을 따로 관리하지 않아도 되고, 리브랜딩되면 자동으로 반영됩니다.

체인 데이터와 가격은 서로 독립적으로 폴백합니다. CoinGecko 만 죽어도 체인 수치는 라이브로 나가고, 반대도 마찬가지입니다. 응답의 source / price_source 필드로 각각 어디서 왔는지 확인할 수 있습니다.

가격은 CoinGecko simple/price 를 요청 1회로 전 체인 조회합니다. 키 없이 동작하지만 429 가 보이면 COINGECKO_API_KEY 에 무료 demo 키를 넣으세요.

위임자 수(delegators)에는 static 폴백이 없습니다. 실제로 셀 수 없으면 null 을 내보냅니다. 하드코딩된 숫자를 대신 채우면 total_delegators 가 조용히 부풀려지기 때문입니다. Aptos 와 Monad 는 구조상 이 값을 못 세므로 항상 null 입니다 (아래 참고).

Aptos

0x1::delegation_pool 이 아니라 0x1::staking_contract 모델입니다 — 공개 위임 풀이 아니라 staker ↔ operator 1:1 계약이라 공개 위임자라는 개념이 없습니다 (delegators: null).

operator 주소로 인덱서를 조회해 스테이크 풀들을 찾고, 각 풀의 0x1::stake::StakePool 에서 active + pending_active 를 합산합니다 (pending_inactive 는 언본딩 중이라 제외). 커미션은 풀이 아니라 staker 계정의 0x1::staking_contract::Store 에 operator 별로 들어 있습니다.

APR 은 StakingRewardsConfig.rewards_rate(FixedPoint64) × 연간 에포크 수로 계산합니다. StakingConfig.rewards_rate 는 거버넌스로 갱신되지 않는 레거시 필드라 값이 다릅니다 (레거시 기준 7.0%, 실제 2.60%). 현재 rewards_rate == min_rewards_rate 로 하한에 도달한 상태입니다.

Monad

스테이킹이 컨트랙트가 아니라 프리컴파일(0x…1000)이고, getValidator(uint64 validatorId) 하나로 조회됩니다. 프리컴파일은 STATICCALL 을 거부하지만 eth_call 은 CALL 이라 정상 동작합니다.

⚠️ validatorId 는 주소로 역추적할 수 없습니다. 익스플로러에 쓰이는 주소 (0x279FC7…)와 온체인 authAddress(0x3673f7e6…)가 다릅니다. 밸리데이터 221개를 전수 조회해도 매칭되지 않으므로 lib/chains.ts 에 id 를 직접 넣어야 합니다.

APR 은 보상률 파라미터가 아니라 실제 지급액에서 역산합니다. Monad 는 보상률을 온체인에 노출하지 않지만, getValidator 가 돌려주는 accRewardPerToken 이 스테이크 1 단위당 누적 지급액이라 두 시점의 차이를 연율로 환산하면 실측 APR 이 나옵니다. 이 누적값은 위임자에게 실제로 꽂히는 금액이므로 커미션이 이미 차감된 순 APR 입니다.

  • 스케일은 1e36 입니다 (스테이크 1e18 당 보상 1e18). 다른 라운드 스케일을 대입하면 APR 이 1e9 배 이상으로 튀어 실측상 배제됩니다.
  • 측정 구간은 12,000 블록(약 1시간)입니다. 구간을 길게 잡으면 밸리데이터가 액티브 셋에서 빠져 있던 기간이 섞여 값이 낮아집니다 — 실측 1시간 12.5% / 10시간 7.5% / 25시간 7.2%. 0.5시간과 1시간이 12.7% / 12.5% 로 거의 같아 1시간을 씁니다. 결과는 KV 에 10분 캐시합니다.
  • consensusStake == 0 (= 액티브 셋에 없음)이면 보상이 실제로 0 이므로 측정 없이 apr: 0.
  • 과거 블록 조회가 필요합니다. 공개 RPC 는 약 50만 블록까지만 보관하므로 측정 구간을 그보다 길게 잡으면 조회가 실패합니다.

APR 계산식

체인 APR      = 연간 신규발행량 × (1 - community_tax) / bonded_tokens
밸리데이터 APR = 체인 APR × (1 - 커미션)

Osmosis 는 epoch 기반 mint 모듈이라 별도 경로를 씁니다 (epoch_provisions × 365 × staking 비율).

Axelar 는 x/mint 를 쓰지 않습니다 (annual_provisions 가 항상 0). 보상이 x/reward 모듈에서 나오고, 인플레이션이 밸리데이터가 유지하는 EVM 체인 수에 비례합니다:

inflation = base + base × key_mgmt_relative_rate
                 + external_chain_voting_rate × 유지 중인 EVM 체인 수

즉 같은 Axelar 라도 밸리데이터마다 APR 이 다릅니다. 현재 프로발리데이터는 EVM 체인 20개를 전부 유지 중이고 external_chain_voting_inflation_rate 가 0.002 이므로 인플레이션은 4% 입니다. 체인 수 집계는 체인마다 maintainer 목록을 받아야 해서 요청이 20회쯤 발생하는데, 거의 안 바뀌는 값이라 KV 에 6시간 캐시합니다 (KV 가 없으면 매 스냅샷마다 조회합니다).

알려진 한계 (실측 확인됨)

  • Osmosis — mint 기반 계산은 약 1.8% 로 나옵니다. Osmosis 는 taker fee 도 스테이커에게 분배하는데 이 공식에는 잡히지 않아 과소 추정입니다. 실 수치가 중요하면 별도 보정이 필요합니다.
  • AtomOne — 약 47% 로 나옵니다 (인플레 20% + 낮은 본딩 비율). distribution 파라미터에 nakamoto_bonus 라는 커스텀 항목이 있어 표준 공식은 근사치입니다.
  • 위임자 수 — publicnode 는 pagination.count_total 쿼리를 503 으로 막습니다. 그래서 polkachu 계열을 1순위 엔드포인트로 두었습니다 (실패 시 자동 failover).

로컬 실행

npm install

체인 수집 결과만 표로 확인 (서버 없이):

npm run probe

HTTP 레이어까지 포함해 로컬 서버 구동:

npm run serve
curl "http://localhost:3000/api/stats?endpoint=chains"

Vercel 런타임을 그대로 재현하려면 npx vercel dev 를 쓰세요.

배포

npx vercel --prod

환경변수는 전부 선택 사항입니다 (.env.example 참고). 아무것도 없어도 공개 노드로 동작합니다.

프로덕션에서는 두 가지를 권장합니다:

  1. Vercel 대시보드에서 Upstash Redis 연결 → KV 폴백 활성화 (업스트림 장애 시 무중단)
  2. REST_* 환경변수로 자체 노드 지정 → 공개 노드 rate limit 회피

다음 작업

  • CoinGecko 가격/시총 연동 (lib/prices.ts)
  • Aptos 어댑터 (lib/aptos.ts)
  • Monad 어댑터 (lib/monad.ts)
  • Axelar x/reward 기반 APR
  • Osmosis taker fee 반영한 APR 보정
  • Monad APR — 온체인 소스가 없어 현재 static 폴백

About

ProValidator staking stats API on Vercel Functions (live Cosmos LCD data, CDN-cached)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages