[volo-vault]: add support for Volo Vault on Sui
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1 +1,4 @@
|
||||
*.log
|
||||
*.log
|
||||
*.csv
|
||||
*.json
|
||||
*.bat
|
||||
3
uniswap-v2/.gitignore
vendored
3
uniswap-v2/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
data/
|
||||
config/
|
||||
*.bat
|
||||
8
volo-vault/config/config.json.txt
Normal file
8
volo-vault/config/config.json.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
[
|
||||
{
|
||||
"address": "0x",
|
||||
"vault_id": [
|
||||
"0x"
|
||||
]
|
||||
}
|
||||
]
|
||||
225
volo-vault/main.py
Normal file
225
volo-vault/main.py
Normal file
@@ -0,0 +1,225 @@
|
||||
import json
|
||||
import csv
|
||||
import os
|
||||
import logging
|
||||
from datetime import datetime
|
||||
import requests
|
||||
from decimal import Decimal
|
||||
|
||||
|
||||
class VoloVaultTracker:
|
||||
def __init__(self, config_dir="config"):
|
||||
self.config_dir = config_dir
|
||||
self.setup_logging()
|
||||
self.load_configs()
|
||||
|
||||
def setup_logging(self):
|
||||
"""设置日志系统"""
|
||||
os.makedirs("logs", exist_ok=True)
|
||||
|
||||
log_format = "%(asctime)s - %(levelname)s - %(message)s"
|
||||
log_filename = f"logs/{datetime.now().strftime('%Y-%m-%d')}.log"
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format=log_format,
|
||||
handlers=[
|
||||
logging.FileHandler(log_filename, encoding="utf-8"),
|
||||
logging.StreamHandler(),
|
||||
],
|
||||
)
|
||||
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.logger.info("初始化日志系统")
|
||||
|
||||
def get_local_timestamp(self):
|
||||
"""获取本地时区的时间戳"""
|
||||
return datetime.now().strftime("%Y-%m-%dT%H:%M")
|
||||
|
||||
def load_configs(self):
|
||||
"""从JSON文件加载配置"""
|
||||
try:
|
||||
with open(f"{self.config_dir}/config.json", "r", encoding="utf-8") as f:
|
||||
self.config = json.load(f)
|
||||
self.logger.info(f"加载配置: {len(self.config)} 个地址")
|
||||
except FileNotFoundError:
|
||||
self.logger.error("配置文件缺失: config.json")
|
||||
self.config = []
|
||||
except json.JSONDecodeError as e:
|
||||
self.logger.error(f"配置文件格式错误 - config.json: {e}")
|
||||
self.config = []
|
||||
|
||||
os.makedirs("data", exist_ok=True)
|
||||
|
||||
def get_user_position(self, address):
|
||||
"""获取用户仓位数据"""
|
||||
try:
|
||||
url = f"https://vault-api.volosui.com/api/v1/users/{address}/position"
|
||||
response = requests.get(url)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except Exception as e:
|
||||
self.logger.error(f"获取用户仓位数据失败 {address}: {e}")
|
||||
return None
|
||||
|
||||
def get_vaults_info(self):
|
||||
"""获取所有金库信息"""
|
||||
try:
|
||||
url = "https://vault-api.volosui.com/api/v1/vaults"
|
||||
response = requests.get(url)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except Exception as e:
|
||||
self.logger.error(f"获取金库信息失败: {e}")
|
||||
return None
|
||||
|
||||
def read_previous_data(self, file_path):
|
||||
"""读取之前的数据用于计算变化量"""
|
||||
if not os.path.exists(file_path):
|
||||
return None
|
||||
|
||||
try:
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
reader = csv.DictReader(f)
|
||||
rows = list(reader)
|
||||
if rows:
|
||||
return rows[-1]
|
||||
except Exception as e:
|
||||
self.logger.error(f"读取历史数据失败 {file_path}: {e}")
|
||||
return None
|
||||
|
||||
def save_user_position_data(self, address, vault_id, position_data, vault_info):
|
||||
"""保存用户仓位数据到CSV"""
|
||||
vault_name = vault_info["name"]
|
||||
data_file = f"data/data_{address}_{vault_name}_{vault_id}.csv"
|
||||
|
||||
previous_data = self.read_previous_data(data_file)
|
||||
|
||||
pool_share_token_balance_change = "0"
|
||||
pool_share_token_usd_change = "0"
|
||||
yield_lifetime_amount_change = "0"
|
||||
yield_lifetime_usd_change = "0"
|
||||
|
||||
if previous_data:
|
||||
prev_balance = Decimal(previous_data["代币数量"])
|
||||
prev_usd = Decimal(previous_data["代币价值USD"])
|
||||
prev_yield_amount = Decimal(previous_data["累计收益数量"])
|
||||
prev_yield_usd = Decimal(previous_data["累计收益价值USD"])
|
||||
|
||||
pool_share_token_balance_change = str(Decimal(str(position_data["poolShareTokenBalance"])) - prev_balance)
|
||||
pool_share_token_usd_change = str(Decimal(str(position_data["poolShareTokenUsd"])) - prev_usd)
|
||||
yield_lifetime_amount_change = str(Decimal(str(position_data["yieldLifetimeAmount"])) - prev_yield_amount)
|
||||
yield_lifetime_usd_change = str(Decimal(str(position_data["yieldLifetimeUsd"])) - prev_yield_usd)
|
||||
|
||||
data_row = {
|
||||
"时间": self.get_local_timestamp(),
|
||||
"实时APR": str(position_data["vaultApr"]),
|
||||
"份额": str(position_data["shares"]),
|
||||
"代币价格": str(position_data["tokenPrice"]),
|
||||
"代币数量": str(position_data["poolShareTokenBalance"]),
|
||||
"代币数量变化量": pool_share_token_balance_change,
|
||||
"代币价值USD": str(position_data["poolShareTokenUsd"]),
|
||||
"代币价值USD变化量": pool_share_token_usd_change,
|
||||
"累计收益数量": str(position_data["yieldLifetimeAmount"]),
|
||||
"累计收益数量变化量": yield_lifetime_amount_change,
|
||||
"累计收益价值USD": str(position_data["yieldLifetimeUsd"]),
|
||||
"累计收益价值USD变化量": yield_lifetime_usd_change
|
||||
}
|
||||
|
||||
file_exists = os.path.exists(data_file)
|
||||
with open(data_file, "a", newline="", encoding="utf-8") as f:
|
||||
writer = csv.DictWriter(f, fieldnames=data_row.keys())
|
||||
if not file_exists:
|
||||
writer.writeheader()
|
||||
writer.writerow(data_row)
|
||||
|
||||
self.logger.info(f"保存用户仓位数据: {os.path.basename(data_file)}")
|
||||
|
||||
def save_vault_data(self, vault_info):
|
||||
"""保存金库数据到CSV"""
|
||||
vault_id = vault_info["id"]
|
||||
vault_name = vault_info["name"]
|
||||
vault_file = f"data/vault_{vault_name}_{vault_id}.csv"
|
||||
|
||||
data_row = {
|
||||
"时间": self.get_local_timestamp(),
|
||||
"实时APR": str(vault_info.get("instantAPR", "")),
|
||||
"7天APY": str(vault_info.get("apy7d", {}).get("value", "")),
|
||||
"30天APY": str(vault_info.get("apy30d", {}).get("value", "")),
|
||||
"总份额": str(vault_info.get("totalShares", "")),
|
||||
"代币价格": str(vault_info.get("coinPrice", "")),
|
||||
"总代币数量": str(vault_info.get("totalStaked", "")),
|
||||
"总代币价值USD": str(vault_info.get("totalStakedUsd", "")),
|
||||
"上限代币数量": str(vault_info.get("stakeCapAmount", ""))
|
||||
}
|
||||
|
||||
file_exists = os.path.exists(vault_file)
|
||||
with open(vault_file, "a", newline="", encoding="utf-8") as f:
|
||||
writer = csv.DictWriter(f, fieldnames=data_row.keys())
|
||||
if not file_exists:
|
||||
writer.writeheader()
|
||||
writer.writerow(data_row)
|
||||
|
||||
self.logger.info(f"保存金库数据: {os.path.basename(vault_file)}")
|
||||
|
||||
def track_all_positions(self):
|
||||
"""跟踪所有仓位"""
|
||||
self.logger.info(f"开始处理 {len(self.config)} 个地址")
|
||||
|
||||
vaults_info = self.get_vaults_info()
|
||||
if not vaults_info:
|
||||
self.logger.error("获取金库信息失败,无法继续处理")
|
||||
return
|
||||
|
||||
for vault in vaults_info.get("data", []):
|
||||
self.save_vault_data(vault)
|
||||
|
||||
success_count = 0
|
||||
|
||||
for i, config_item in enumerate(self.config, 1):
|
||||
address = config_item["address"]
|
||||
self.logger.info(f"[{i}/{len(self.config)}] 处理地址: {address}")
|
||||
|
||||
position_data = self.get_user_position(address)
|
||||
if not position_data:
|
||||
self.logger.error(f"获取用户仓位数据失败: {address}")
|
||||
continue
|
||||
|
||||
target_vault_ids = config_item.get("vault_id", [])
|
||||
for vault_id in target_vault_ids:
|
||||
found_position = None
|
||||
for position in position_data:
|
||||
if position.get("vaultId") == vault_id:
|
||||
found_position = position
|
||||
break
|
||||
|
||||
if found_position:
|
||||
vault_info = None
|
||||
for vault in vaults_info.get("data", []):
|
||||
if vault["id"] == vault_id:
|
||||
vault_info = vault
|
||||
break
|
||||
|
||||
if vault_info:
|
||||
self.save_user_position_data(address, vault_id, found_position, vault_info)
|
||||
success_count += 1
|
||||
else:
|
||||
self.logger.warning(f"未找到金库信息: {vault_id}")
|
||||
else:
|
||||
self.logger.warning(f"未找到vault_id {vault_id} 在地址 {address} 的仓位中")
|
||||
|
||||
self.logger.info(f"处理完成: 成功 {success_count} 个仓位")
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
try:
|
||||
tracker = VoloVaultTracker("config")
|
||||
tracker.track_all_positions()
|
||||
except Exception as e:
|
||||
logging.error(f"程序执行失败: {e}")
|
||||
raise
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user