From 50cc635133224211faf523884ce02aacebeba5a7 Mon Sep 17 00:00:00 2001 From: guoshijiang Date: Wed, 27 Mar 2024 20:05:30 +0800 Subject: [PATCH] l3 staking api --- api/l3staking/__init__.py | 0 api/l3staking/api_v1.py | 39 ++++ api/urls.py | 10 + hailstone/settings.py | 1 + l3staking/__init__.py | 0 l3staking/admin.py | 23 +++ l3staking/apps.py | 6 + l3staking/migrations/0001_initial.py | 70 +++++++ .../migrations/0002_stakingstrategy_chain.py | 19 ++ l3staking/migrations/__init__.py | 0 l3staking/models.py | 180 ++++++++++++++++++ l3staking/tests.py | 3 + l3staking/views.py | 3 + l3staking_api.md | 97 ++++++++++ 14 files changed, 451 insertions(+) create mode 100644 api/l3staking/__init__.py create mode 100644 api/l3staking/api_v1.py create mode 100644 l3staking/__init__.py create mode 100644 l3staking/admin.py create mode 100644 l3staking/apps.py create mode 100644 l3staking/migrations/0001_initial.py create mode 100644 l3staking/migrations/0002_stakingstrategy_chain.py create mode 100644 l3staking/migrations/__init__.py create mode 100644 l3staking/models.py create mode 100644 l3staking/tests.py create mode 100644 l3staking/views.py create mode 100644 l3staking_api.md diff --git a/api/l3staking/__init__.py b/api/l3staking/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/l3staking/api_v1.py b/api/l3staking/api_v1.py new file mode 100644 index 0000000..e46fb21 --- /dev/null +++ b/api/l3staking/api_v1.py @@ -0,0 +1,39 @@ +# encoding=utf-8 + +import json +from common.helpers import ok_json, error_json +from l3staking.models import ( + StakingChain, + StakingStrategy, + Node, +) + + +# @check_api_token +def get_staking_chains(request): + staking_chains = StakingChain.objects.all() + staking_chain_list = [] + for sc in staking_chains: + staking_chain_list.append(sc.as_dict()) + return ok_json(staking_chain_list) + + +# @check_api_token +def get_staking_node_list(request): + params = json.loads(request.body.decode()) + chain_id = params.get('chain_id', 0) + staking_chain = StakingChain.objects.filter(id=chain_id).first() + if staking_chain is None: + return error_json("No support chain", 4000) + staking_strategies = StakingStrategy.objects.filter(chain=staking_chain) + staking_strategies_node_list = [] + for ss in staking_strategies: + staking_nodes = Node.objects.filter(chain_id=ss.chain, strategy=ss).order_by("-id") + staking_node_list = [] + for node in staking_nodes: + staking_node_list.append(node.as_dict()) + staking_strategies_node_list.append({ + "stategy_name": ss.name, + "node_list": staking_node_list + }) + return ok_json(staking_strategies_node_list) diff --git a/api/urls.py b/api/urls.py index 3d15fd1..5ec06b0 100644 --- a/api/urls.py +++ b/api/urls.py @@ -68,6 +68,12 @@ get_blog_cat_list ) +from api.l3staking.api_v1 import ( + get_staking_chains, + get_staking_node_list +) + + urlpatterns = [ # Hd wallet module path(r'get_balance', get_balance, name='get_balance'), @@ -129,4 +135,8 @@ path(r'get_blog_list', get_blog_list, name='get_blog_list'), path(r'get_event_list', get_event_list, name='get_event_list'), path(r'get_forum_list', get_forum_list, name='get_forum_list'), + + # l3staking + path(r'get_staking_chains', get_staking_chains, name='get_staking_chains'), + path(r'get_staking_node_list', get_staking_node_list, name='get_staking_node_list'), ] \ No newline at end of file diff --git a/hailstone/settings.py b/hailstone/settings.py index a7c3d18..862174b 100644 --- a/hailstone/settings.py +++ b/hailstone/settings.py @@ -24,6 +24,7 @@ "channels", 'airdrop', 'website', + 'l3staking', ] MIDDLEWARE = [ diff --git a/l3staking/__init__.py b/l3staking/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/l3staking/admin.py b/l3staking/admin.py new file mode 100644 index 0000000..d1fbe4a --- /dev/null +++ b/l3staking/admin.py @@ -0,0 +1,23 @@ +# encoding=utf-8 + +from django.contrib import admin +from l3staking.models import ( + StakingChain, + StakingStrategy, + Node +) + +@admin.register(StakingChain) +class AddressAmountStatAdmin(admin.ModelAdmin): + list_display = ('id', 'name', 'chain_id', 'rpc_url') + + +@admin.register(StakingStrategy) +class AddressAmountStatAdmin(admin.ModelAdmin): + list_display = ('id', 'name') + + +@admin.register(Node) +class AddressAmountStatAdmin(admin.ModelAdmin): + list_display = ('id', 'name', 'eth_income', 'eth_income_rate', 'dp_income', 'dp_income_rate', 'eth_evil', 'eth_evil_rate', 'dp_evil', 'dp_evil_rate', 'tvl') + diff --git a/l3staking/apps.py b/l3staking/apps.py new file mode 100644 index 0000000..2433e8e --- /dev/null +++ b/l3staking/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class L3StakingConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'l3staking' diff --git a/l3staking/migrations/0001_initial.py b/l3staking/migrations/0001_initial.py new file mode 100644 index 0000000..59c4a12 --- /dev/null +++ b/l3staking/migrations/0001_initial.py @@ -0,0 +1,70 @@ +# Generated by Django 4.1.1 on 2024-03-27 11:39 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='StakingChain', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('uuid', models.CharField(blank=True, max_length=100, null=True, unique=True)), + ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), + ('updated_at', models.DateTimeField(auto_now=True, db_index=True)), + ('name', models.CharField(default='', max_length=100, verbose_name='链名称')), + ('chain_id', models.CharField(default='', max_length=100, verbose_name='链ID')), + ('rpc_url', models.CharField(blank=True, default='', max_length=100, null=True, verbose_name='节点 rpc')), + ], + options={ + 'verbose_name': 'StakingChain', + 'verbose_name_plural': 'StakingChain', + }, + ), + migrations.CreateModel( + name='StakingStrategy', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('uuid', models.CharField(blank=True, max_length=100, null=True, unique=True)), + ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), + ('updated_at', models.DateTimeField(auto_now=True, db_index=True)), + ('name', models.CharField(default='Social', max_length=100, verbose_name='质押模块名称')), + ], + options={ + 'verbose_name': 'StakingStrategy', + 'verbose_name_plural': 'StakingStrategy', + }, + ), + migrations.CreateModel( + name='Node', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('uuid', models.CharField(blank=True, max_length=100, null=True, unique=True)), + ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), + ('updated_at', models.DateTimeField(auto_now=True, db_index=True)), + ('name', models.CharField(default='unknown', max_length=500, verbose_name='节点名称')), + ('eth_income', models.CharField(default='0', max_length=500, verbose_name='Eth 收益金额')), + ('eth_income_rate', models.CharField(default='0', max_length=500, verbose_name='Eth 收益率')), + ('dp_income', models.CharField(default='0', max_length=500, verbose_name='DP 收益金额')), + ('dp_income_rate', models.CharField(default='0', max_length=500, verbose_name='DP 收益率')), + ('eth_evil', models.CharField(default='0', max_length=500, verbose_name='Eth 惩罚金额')), + ('eth_evil_rate', models.CharField(default='0', max_length=500, verbose_name='Eth 惩罚率')), + ('dp_evil', models.CharField(default='0', max_length=500, verbose_name='DP 惩罚金额')), + ('dp_evil_rate', models.CharField(default='0', max_length=500, verbose_name='DP 惩罚率')), + ('tvl', models.CharField(default='0', max_length=500, verbose_name='总质押量')), + ('chain', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='staking_chain', to='l3staking.stakingchain', verbose_name='质押的链')), + ('strategy', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='staking_chain', to='l3staking.stakingstrategy', verbose_name='质押策略')), + ], + options={ + 'verbose_name': 'Node', + 'verbose_name_plural': 'Node', + }, + ), + ] diff --git a/l3staking/migrations/0002_stakingstrategy_chain.py b/l3staking/migrations/0002_stakingstrategy_chain.py new file mode 100644 index 0000000..0c9de40 --- /dev/null +++ b/l3staking/migrations/0002_stakingstrategy_chain.py @@ -0,0 +1,19 @@ +# Generated by Django 4.1.1 on 2024-03-27 11:42 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('l3staking', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='stakingstrategy', + name='chain', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='staking_chain_strategies', to='l3staking.stakingchain', verbose_name='质押的链'), + ), + ] diff --git a/l3staking/migrations/__init__.py b/l3staking/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/l3staking/models.py b/l3staking/models.py new file mode 100644 index 0000000..80ef3c1 --- /dev/null +++ b/l3staking/models.py @@ -0,0 +1,180 @@ +# encoding=utf-8 +import pytz + +from django.conf import settings +from django.db import models +from common.models import BaseModel, Asset + + +class StakingChain(BaseModel): + name = models.CharField( + default="", + max_length=100, + unique=False, + verbose_name='链名称' + ) + chain_id = models.CharField( + default="", + max_length=100, + unique=False, + verbose_name='链ID' + ) + rpc_url = models.CharField( + max_length=100, + default="", + blank=True, + null=True, + verbose_name="节点 rpc", + ) + + class Meta: + verbose_name = 'StakingChain' + verbose_name_plural = verbose_name + + def __str__(self): + return self.name + + def as_dict(self): + tz = pytz.timezone(settings.TIME_ZONE) + return { + 'id': self.id, + 'title': self.name, + 'chain_id': self.chain_id, + 'rpc_url': str(self.rpc_url), + 'created_at': self.created_at.astimezone(tz).strftime("%Y-%m-%d %H:%M:%S") + } + + +class StakingStrategy(BaseModel): + chain = models.ForeignKey( + StakingChain, + blank=True, + related_name='staking_chain_strategies', + null=True, + on_delete=models.CASCADE, + verbose_name='质押的链' + ) + name = models.CharField( + default="Social", + max_length=100, + unique=False, + verbose_name='质押模块名称' + ) + + class Meta: + verbose_name = 'StakingStrategy' + verbose_name_plural = verbose_name + + def __str__(self): + return self.name + + def as_dict(self): + return { + 'id': self.id, + 'name': self.name, + } + + +class Node(BaseModel): + chain = models.ForeignKey( + StakingChain, + blank=True, + related_name='staking_chain', + null=True, + on_delete=models.CASCADE, + verbose_name='质押的链' + ) + strategy = models.ForeignKey( + StakingStrategy, + blank=True, + related_name='staking_chain', + null=True, + on_delete=models.CASCADE, + verbose_name='质押策略' + ) + name = models.CharField( + default="unknown", + max_length=500, + unique=False, + verbose_name='节点名称' + ) + eth_income = models.CharField( + default="0", + max_length=500, + unique=False, + verbose_name='Eth 收益金额' + ) + eth_income_rate = models.CharField( + default="0", + max_length=500, + unique=False, + verbose_name='Eth 收益率' + ) + dp_income = models.CharField( + default="0", + max_length=500, + unique=False, + verbose_name='DP 收益金额' + ) + dp_income_rate = models.CharField( + default="0", + max_length=500, + unique=False, + verbose_name='DP 收益率' + ) + eth_evil = models.CharField( + default="0", + max_length=500, + unique=False, + verbose_name='Eth 惩罚金额' + ) + eth_evil_rate = models.CharField( + default="0", + max_length=500, + unique=False, + verbose_name='Eth 惩罚率' + ) + dp_evil = models.CharField( + default="0", + max_length=500, + unique=False, + verbose_name='DP 惩罚金额' + ) + dp_evil_rate = models.CharField( + default="0", + max_length=500, + unique=False, + verbose_name='DP 惩罚率' + ) + tvl = models.CharField( + default="0", + max_length=500, + unique=False, + verbose_name='总质押量' + ) + + class Meta: + verbose_name = 'Node' + verbose_name_plural = verbose_name + + def __str__(self): + return self.name + + def as_dict(self): + tz = pytz.timezone(settings.TIME_ZONE) + return { + 'id': self.id, + 'chain': self.chain.name, + 'strategy': self.strategy.name, + 'name': self.name, + 'eth_income': self.eth_income, + 'eth_income_rate': self.eth_income_rate, + 'dp_income': self.dp_income, + 'dp_income_rate': self.dp_income_rate, + 'eth_evil': self.eth_evil, + 'eth_evil_rate': self.eth_evil_rate, + 'dp_evil': self.eth_evil, + 'dp_evil_rate': self.eth_evil_rate, + 'tvl': self.tvl, + 'created_at': self.created_at.astimezone(tz).strftime("%Y-%m-%d %H:%M:%S") + } diff --git a/l3staking/tests.py b/l3staking/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/l3staking/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/l3staking/views.py b/l3staking/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/l3staking/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/l3staking_api.md b/l3staking_api.md new file mode 100644 index 0000000..646bf4f --- /dev/null +++ b/l3staking_api.md @@ -0,0 +1,97 @@ +# L3质押接口 + +## 1. 获取支持的链 + +接口请求 +``` +curl --location --request POST 'http://127.0.0.1:8000/api/get_staking_chains' \ +--data '' +``` +返回值 + +``` +{ + "ok": true, + "code": 200, + "result": [ + { + "id": 1, + "title": "Op", + "chain_id": "1011", + "rpc_url": "https://docsend.com/view/um727ucvskffethr", + "created_at": "2024-03-27 19:59:38" + }, + { + "id": 2, + "title": "zkfair", + "chain_id": "1011", + "rpc_url": "https://docsend.com/view/um727ucvskffethr", + "created_at": "2024-03-27 19:59:47" + } + ] +} +``` + + +## 2. 根据链获取策略节点列表 + +接口请求 +``` +curl --location 'http://127.0.0.1:8000/api/get_staking_node_list' \ +--header 'Content-Type: application/json' \ +--data '{ + "chain_id": 2 +}' +``` +返回值 + +``` +{ + "ok": true, + "code": 200, + "result": [ + { + "stategy_name": "Social", + "node_list": [ + { + "id": 2, + "chain": "zkfair", + "strategy": "Social", + "name": "DappLink Node2", + "eth_income": "0", + "eth_income_rate": "0", + "dp_income": "0", + "dp_income_rate": "0", + "eth_evil": "0", + "eth_evil_rate": "0", + "dp_evil": "0", + "dp_evil_rate": "0", + "tvl": "0", + "created_at": "2024-03-27 20:01:02" + } + ] + }, + { + "stategy_name": "Gaming", + "node_list": [ + { + "id": 4, + "chain": "zkfair", + "strategy": "Gaming", + "name": "DappLink Node", + "eth_income": "0", + "eth_income_rate": "0", + "dp_income": "0", + "dp_income_rate": "0", + "eth_evil": "0", + "eth_evil_rate": "0", + "dp_evil": "0", + "dp_evil_rate": "0", + "tvl": "0", + "created_at": "2024-03-27 20:01:35" + } + ] + } + ] +} +``` \ No newline at end of file