Commit f0eaf49b authored by 陈涛's avatar 陈涛

增加收益接口,收益历史接口

parent a3d3346d
from typing import List from typing import List
import requests
from motor.core import AgnosticCollection from motor.core import AgnosticCollection
import dependencies import dependencies
...@@ -10,7 +10,7 @@ from fastapi import APIRouter, Depends, Query ...@@ -10,7 +10,7 @@ from fastapi import APIRouter, Depends, Query
from model.fund import FundType from model.fund import FundType
from model.node import BaseNode from model.node import BaseNode
from schema.beacon import Validator, ValidatorDeposit, ValidatorBlock, ValidatorIncome, Epoch from schema.beacon import Validator, ValidatorDeposit, ValidatorBlock, ValidatorIncome, Epoch, Rewards
from schema.node import BindNode from schema.node import BindNode
from service.beacon import BeaconChaService from service.beacon import BeaconChaService
from tools.jwt_tools import User from tools.jwt_tools import User
...@@ -163,3 +163,33 @@ async def get_epoch( ...@@ -163,3 +163,33 @@ async def get_epoch(
epoch = await beacon_service.get_epoch(epoch) epoch = await beacon_service.get_epoch(epoch)
response = Response[Epoch](data=epoch) response = Response[Epoch](data=epoch)
return response return response
@router.get("/consensus/income/", response_model=Response[list], summary="查询cl收益", description="")
async def income_history(
index: str,
beacon_service: BeaconChaService = Depends(BeaconChaService)
):
result = beacon_service.get_income_history(index)
response = Response(data=result)
return response
@router.get("/execute/income/", response_model=Response[list], summary="查询el收益", description="")
async def income_history(
index: str,
beacon_service: BeaconChaService = Depends(BeaconChaService)
):
result = beacon_service.get_execute_income_history(index)
response = Response(data=result)
return response
@router.get("/rewards/", response_model=Response[Rewards], summary="收益", description="")
async def get_rewards(
index_or_pubkey: str,
beacon_service: BeaconChaService = Depends(BeaconChaService)
):
rewards = beacon_service.get_rewards(index_or_pubkey)
response = Response(data=rewards.dict())
return response
...@@ -13,3 +13,7 @@ pymongo~=4.3.3 ...@@ -13,3 +13,7 @@ pymongo~=4.3.3
httpx~=0.23.3 httpx~=0.23.3
APScheduler~=3.10.1 APScheduler~=3.10.1
aiofiles==23.1.0 aiofiles==23.1.0
requests~=2.29.0
numpy~=1.24.3
Pillow~=9.5.0
lxml~=4.9.2
\ No newline at end of file
...@@ -67,3 +67,9 @@ class Epoch(BaseModel): ...@@ -67,3 +67,9 @@ class Epoch(BaseModel):
total_validator_balance: float = Field(None, title="节点总计余额") total_validator_balance: float = Field(None, title="节点总计余额")
ts: int = Field(None, title="时间") ts: int = Field(None, title="时间")
finalized: bool = Field(None, title="结果") finalized: bool = Field(None, title="结果")
class Rewards(BaseModel):
cl: str
el: str
total: str
...@@ -3,11 +3,13 @@ import re ...@@ -3,11 +3,13 @@ import re
import time import time
from typing import List, Union from typing import List, Union
import requests
from lxml import etree
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from exception.db import NotFundError from exception.db import NotFundError
from exception.http import RequestHttpException from exception.http import RequestHttpException
from schema.beacon import Validator, ValidatorDeposit, ValidatorBlock, ValidatorIncome, Epoch from schema.beacon import Validator, ValidatorDeposit, ValidatorBlock, ValidatorIncome, Epoch, Rewards
from tools.http_helper import aio_request from tools.http_helper import aio_request
from tools.time_helper import time_str_to_timestamp from tools.time_helper import time_str_to_timestamp
...@@ -128,7 +130,7 @@ class BeaconChaService: ...@@ -128,7 +130,7 @@ class BeaconChaService:
) for item in response["data"]] ) for item in response["data"]]
async def get_validator_blocks(self, index: int, start: int = 0, length: int = 10) -> ValidatorBlocks: async def get_validator_blocks(self, index: int, start: int = 0, length: int = 10) -> ValidatorBlocks:
url = f"https://beaconcha.in/validator/{index}/proposedblocks?draw=1&columns%5B0%5D%5Bdata%5D=0&columns%5B0%5D%5Bname%5D=&columns%5B0%5D%5Bsearchable%5D=true&columns%5B0%5D%5Borderable%5D=true&columns%5B0%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B0%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B1%5D%5Bdata%5D=1&columns%5B1%5D%5Bname%5D=&columns%5B1%5D%5Bsearchable%5D=true&columns%5B1%5D%5Borderable%5D=false&columns%5B1%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B1%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B2%5D%5Bdata%5D=2&columns%5B2%5D%5Bname%5D=&columns%5B2%5D%5Bsearchable%5D=true&columns%5B2%5D%5Borderable%5D=true&columns%5B2%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B2%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B3%5D%5Bdata%5D=3&columns%5B3%5D%5Bname%5D=&columns%5B3%5D%5Bsearchable%5D=true&columns%5B3%5D%5Borderable%5D=false&columns%5B3%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B3%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B4%5D%5Bdata%5D=4&columns%5B4%5D%5Bname%5D=&columns%5B4%5D%5Bsearchable%5D=true&columns%5B4%5D%5Borderable%5D=false&columns%5B4%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B4%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B5%5D%5Bdata%5D=5&columns%5B5%5D%5Bname%5D=&columns%5B5%5D%5Bsearchable%5D=true&columns%5B5%5D%5Borderable%5D=true&columns%5B5%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B5%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B6%5D%5Bdata%5D=6&columns%5B6%5D%5Bname%5D=&columns%5B6%5D%5Bsearchable%5D=true&columns%5B6%5D%5Borderable%5D=true&columns%5B6%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B6%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B7%5D%5Bdata%5D=7&columns%5B7%5D%5Bname%5D=&columns%5B7%5D%5Bsearchable%5D=true&columns%5B7%5D%5Borderable%5D=false&columns%5B7%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B7%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B8%5D%5Bdata%5D=8&columns%5B8%5D%5Bname%5D=&columns%5B8%5D%5Bsearchable%5D=true&columns%5B8%5D%5Borderable%5D=true&columns%5B8%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B8%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B9%5D%5Bdata%5D=9&columns%5B9%5D%5Bname%5D=&columns%5B9%5D%5Bsearchable%5D=true&columns%5B9%5D%5Borderable%5D=true&columns%5B9%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B9%5D%5Bsearch%5D%5Bregex%5D=false&order%5B0%5D%5Bcolumn%5D=0&order%5B0%5D%5Bdir%5D=desc&start={start}&length={length}&search%5Bvalue%5D=&search%5Bregex%5D=false&_={time.time() * 1000}" url = f"{self.base_url}/validator/{index}/proposedblocks?draw=1&columns%5B0%5D%5Bdata%5D=0&columns%5B0%5D%5Bname%5D=&columns%5B0%5D%5Bsearchable%5D=true&columns%5B0%5D%5Borderable%5D=true&columns%5B0%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B0%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B1%5D%5Bdata%5D=1&columns%5B1%5D%5Bname%5D=&columns%5B1%5D%5Bsearchable%5D=true&columns%5B1%5D%5Borderable%5D=false&columns%5B1%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B1%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B2%5D%5Bdata%5D=2&columns%5B2%5D%5Bname%5D=&columns%5B2%5D%5Bsearchable%5D=true&columns%5B2%5D%5Borderable%5D=true&columns%5B2%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B2%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B3%5D%5Bdata%5D=3&columns%5B3%5D%5Bname%5D=&columns%5B3%5D%5Bsearchable%5D=true&columns%5B3%5D%5Borderable%5D=false&columns%5B3%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B3%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B4%5D%5Bdata%5D=4&columns%5B4%5D%5Bname%5D=&columns%5B4%5D%5Bsearchable%5D=true&columns%5B4%5D%5Borderable%5D=false&columns%5B4%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B4%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B5%5D%5Bdata%5D=5&columns%5B5%5D%5Bname%5D=&columns%5B5%5D%5Bsearchable%5D=true&columns%5B5%5D%5Borderable%5D=true&columns%5B5%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B5%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B6%5D%5Bdata%5D=6&columns%5B6%5D%5Bname%5D=&columns%5B6%5D%5Bsearchable%5D=true&columns%5B6%5D%5Borderable%5D=true&columns%5B6%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B6%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B7%5D%5Bdata%5D=7&columns%5B7%5D%5Bname%5D=&columns%5B7%5D%5Bsearchable%5D=true&columns%5B7%5D%5Borderable%5D=false&columns%5B7%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B7%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B8%5D%5Bdata%5D=8&columns%5B8%5D%5Bname%5D=&columns%5B8%5D%5Bsearchable%5D=true&columns%5B8%5D%5Borderable%5D=true&columns%5B8%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B8%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B9%5D%5Bdata%5D=9&columns%5B9%5D%5Bname%5D=&columns%5B9%5D%5Bsearchable%5D=true&columns%5B9%5D%5Borderable%5D=true&columns%5B9%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B9%5D%5Bsearch%5D%5Bregex%5D=false&order%5B0%5D%5Bcolumn%5D=0&order%5B0%5D%5Bdir%5D=desc&start={start}&length={length}&search%5Bvalue%5D=&search%5Bregex%5D=false&_={time.time() * 1000}"
response = await aio_request(url) response = await aio_request(url)
blocks = [ValidatorBlock( blocks = [ValidatorBlock(
epoch=re.findall(r'/epoch/(\d+)">', item[0])[0], epoch=re.findall(r'/epoch/(\d+)">', item[0])[0],
...@@ -162,3 +164,25 @@ class BeaconChaService: ...@@ -162,3 +164,25 @@ class BeaconChaService:
finalized=response["data"]["finalized"] finalized=response["data"]["finalized"]
) )
return result return result
def get_income_history(self, index_or_pubkey: str):
response = requests.get(f"{self.base_url}/validator/{index_or_pubkey}")
return eval(re.findall(r'var incomeHistory = \[(.*?)\]', response.text)[0])
def get_execute_income_history(self, index_or_pubkey: str):
response = requests.get(f"{self.base_url}/validator/{index_or_pubkey}")
return eval(re.findall(r'var executionIncomeHistory = \[(.*?)\]', response.text)[0])
def get_rewards(self, index_or_pubkey: Union[int, str]) -> Rewards:
response = requests.get(f"{self.base_url}/validator/{index_or_pubkey}")
html = response.content
# 解析 HTML 页面
tree = etree.HTML(html)
total_rewards_re = tree.xpath('/html/body/main/div[1]/div[2]/div[2]/div/table/tbody/tr[1]/td/span')[0]
total_rewards_title = total_rewards_re.get("title")
cl = re.findall(r'CL: <span>(\+[\d\.]+ ETH)</span>', total_rewards_title)[0]
el = re.findall(r'EL: <span>(\+[\d\.]+ ETH)</span>', total_rewards_title)[0]
total_re = tree.xpath('/html/body/main/div[1]/div[2]/div[2]/div/table/tbody/tr[1]/td/span/b')[0]
total = total_re.text
result = Rewards(cl=cl, el=el, total=total)
return result
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment