diff --git a/doc/api.md b/doc/api.md
index e26e81b..5ace3c6 100644
--- a/doc/api.md
+++ b/doc/api.md
@@ -4,6 +4,7 @@
* [1.2 获取validator最近20笔质押变化](#1.2)
* [1.3 获取validator的delegate记录](#1.3)
* [1.4 获取validator的undelegate记录](#1.4)
+* [1.5 获取validator最近10日vote变化](#1.5)
## [Contract](#2)
* [2.1 获取bound数量](#2.1)
@@ -343,8 +344,55 @@
}
```
+
1.5 获取validator最近10日vote变化
+* `GET /api/diff/vote`
+* 参数
+
+| 参数 | 类型 | 必传 | 说明 |
+|-----------|--------|----|-------------|
+| validator | string | N | validator地址 |
+| page | number | N | 页码,默认1 |
+| page_size | number | N | 页大小,默认10 |
+
+* Request: `http://localhost/api/diff/vote?validator=0x000e33ab7471186f3b1de9fc08bb9c480f453590&page=1&page_size=5`
+* Response:
+```json
+{
+ "total": 65,
+ "page": 1,
+ "page_size": 5,
+ "data": [
+ {
+ "block_num": 5475318,
+ "should_vote": 1135,
+ "voted": 1135
+ },
+ {
+ "block_num": 5475317,
+ "should_vote": 1134,
+ "voted": 1134
+ },
+ {
+ "block_num": 5475316,
+ "should_vote": 1134,
+ "voted": 1134
+ },
+ {
+ "block_num": 5475314,
+ "should_vote": 1131,
+ "voted": 1131
+ },
+ {
+ "block_num": 5475313,
+ "should_vote": 1130,
+ "voted": 1130
+ }
+ ]
+}
+```
+
2.1 获取bound数量
* `GET /api/bound`
diff --git a/indexer/src/main.rs b/indexer/src/main.rs
index 695452a..575974e 100644
--- a/indexer/src/main.rs
+++ b/indexer/src/main.rs
@@ -15,7 +15,7 @@ use crate::delegate::get_delegate_records;
use crate::receipt::get_receipts;
use crate::stake::get_stake_records;
use crate::undelegate::get_undelegate_records;
-use crate::validators::{get_latest20, get_validators};
+use crate::validators::{get_latest20, get_validator_votes, get_validators};
use axum::http::Method;
use axum::routing::get;
use axum::Router;
@@ -96,6 +96,7 @@ async fn main() -> Result<()> {
.route("/api/diff/latest", get(get_latest20))
.route("/api/records/delegate", get(get_delegate_records))
.route("/api/records/undelegate", get(get_undelegate_records))
+ .route("/api/diff/vote", get(get_validator_votes))
.route("/api/records/stake", get(get_stake_records))
.route("/api/receipts", get(get_receipts))
.route("/api/bound", get(get_delegator_bound))
diff --git a/indexer/src/types.rs b/indexer/src/types.rs
index cd9ebf8..1f7e3c8 100644
--- a/indexer/src/types.rs
+++ b/indexer/src/types.rs
@@ -1,5 +1,6 @@
use serde::{Deserialize, Serialize};
use serde_json::Value;
+
#[derive(Serialize, Deserialize)]
pub struct QueryResult {
pub total: i64,
@@ -8,6 +9,13 @@ pub struct QueryResult {
pub data: T,
}
+#[derive(Serialize, Deserialize)]
+pub struct ValidatorVoteResponse {
+ pub block_num: i64,
+ pub should_vote: i32,
+ pub voted: i32,
+}
+
#[derive(Serialize, Deserialize)]
pub struct DelegatorSumResponse {
pub delegate: String,
diff --git a/indexer/src/validators.rs b/indexer/src/validators.rs
index 5d01998..cb6a7e5 100644
--- a/indexer/src/validators.rs
+++ b/indexer/src/validators.rs
@@ -1,5 +1,7 @@
use crate::error::Result;
-use crate::types::{QueryResult, ValidatorLatest20Response, ValidatorResponse};
+use crate::types::{
+ QueryResult, ValidatorLatest20Response, ValidatorResponse, ValidatorVoteResponse,
+};
use crate::AppState;
use axum::extract::{Query, State};
use axum::Json;
@@ -11,6 +13,62 @@ use sqlx::Row;
use std::ops::{Add, Sub};
use std::sync::Arc;
+#[derive(Serialize, Deserialize)]
+pub struct GetVoteParams {
+ pub validator: String,
+ pub page: Option,
+ pub page_size: Option,
+}
+
+const BLOCKS_PER_DAY: i64 = 24 * 60 * 60 / 15; // 5760
+
+pub async fn get_validator_votes(
+ State(state): State>,
+ params: Query,
+) -> Result>>> {
+ let mut pool = state.pool.acquire().await?;
+ let page = params.page.unwrap_or(1);
+ let page_size = params.page_size.unwrap_or(10);
+
+ let sql_total = r#"SELECT count(*) FROM evm_validators WHERE validator = $1
+ AND block_num >= (SELECT max(block_num) FROM evm_validators) - $2"#;
+ let row = sqlx::query(sql_total)
+ .bind(¶ms.0.validator)
+ .bind(BLOCKS_PER_DAY)
+ .fetch_one(&mut *pool)
+ .await?;
+ let total: i64 = row.try_get("count")?;
+
+ let sql_query = r#"SELECT block_num,should_vote,voted FROM evm_validators WHERE
+ validator = $1 AND block_num >= (SELECT max(block_num) FROM evm_validators) - $2
+ ORDER BY block_num DESC LIMIT $3 OFFSET $4"#;
+ let rows = sqlx::query(sql_query)
+ .bind(¶ms.0.validator)
+ .bind(BLOCKS_PER_DAY)
+ .bind(page_size)
+ .bind((page - 1) * page_size)
+ .fetch_all(&mut *pool)
+ .await?;
+ let mut votes: Vec = vec![];
+ for r in rows {
+ let block_num: i64 = r.try_get("block_num")?;
+ let should_vote: i32 = r.try_get("should_vote")?;
+ let voted: i32 = r.try_get("voted")?;
+ votes.push(ValidatorVoteResponse {
+ block_num,
+ should_vote,
+ voted,
+ })
+ }
+
+ Ok(Json(QueryResult {
+ total,
+ page,
+ page_size,
+ data: votes,
+ }))
+}
+
#[derive(Serialize, Deserialize)]
pub struct GetValidatorsParams {
pub online: Option,