Skip to content

Commit

Permalink
feat(api): adding endpoint for new stake data (#553)
Browse files Browse the repository at this point in the history
* feat(api): adding endpoint for new stake data

* feat(api,indexer): adding lock_duration to stake events

* feat(api): addnig admin api for new stake models

* feat(api): lock_duration --> lock_time, moving new endpoint to /stake/

---------

Co-authored-by: Gerald Iakobinyi-Pich <nutrina9@gmail.com>
  • Loading branch information
lucianHymer and nutrina authored Mar 18, 2024
1 parent 6f53e8a commit d1b06d8
Show file tree
Hide file tree
Showing 26 changed files with 598 additions and 113 deletions.
16 changes: 16 additions & 0 deletions api/ceramic_cache/api/v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,15 @@
from ninja_jwt.tokens import RefreshToken, Token, TokenError
from registry.api.v1 import (
DetailedScoreResponse,
ErrorMessageResponse,
SubmitPassportPayload,
ahandle_submit_passport,
handle_get_score,
handle_submit_passport,
)
from registry.models import Score
from stake.api import handle_get_gtc_stake
from stake.schema import StakeSchema

from ..exceptions import (
InternalServerException,
Expand Down Expand Up @@ -509,3 +512,16 @@ def get_detailed_score_response_for_address(address: str) -> DetailedScoreRespon
score = async_to_sync(ahandle_submit_passport)(submit_passport_payload, account)

return score


@router.get(
"/stake/gtc",
response={
200: List[StakeSchema],
400: ErrorMessageResponse,
},
auth=JWTDidAuth(),
)
def get_score(request) -> List[StakeSchema]:
address = get_address_from_did(request.did)
return handle_get_gtc_stake(address)
13 changes: 11 additions & 2 deletions api/registry/api/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ class StampDisplayResponse(Schema):
groups: List[StampDisplayResponseGroup]


class Stake(Schema):
class LegacyStakeSchema(Schema):
id: int
event_type: str
round_id: int
Expand All @@ -180,7 +180,16 @@ class Stake(Schema):


class GtcEventsResponse(Schema):
results: List[Stake]
results: List[LegacyStakeSchema]


class StakeSchema(Schema):
chain: str
staker: str
stakee: str
amount: str
unlock_time: str
lock_duration: str


class GtcStakeEventsSchema(Schema):
Expand Down
6 changes: 3 additions & 3 deletions api/registry/api/v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -731,12 +731,12 @@ def stamp_display(request) -> List[StampDisplayResponse]:
# auth=ApiKey(),
auth=None,
response=GtcEventsResponse,
summary="Retrieve GTC stake amounts for the GTC Staking stamp",
summary="Retrieve GTC stake amounts from legacy staking contract",
description="Get self and community GTC staking amounts based on address and round ID",
)
def get_gtc_stake(request, address: str, round_id: int) -> GtcEventsResponse:
def get_gtc_stake_legacy(request, address: str, round_id: str) -> GtcEventsResponse:
"""
Get GTC stake amount by address and round ID
Get GTC stake amount by address and round ID (from legacy contract)
"""
address = address.lower()

Expand Down
25 changes: 22 additions & 3 deletions api/registry/api/v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
DetailedScoreResponse,
ErrorMessageResponse,
SigningMessageResponse,
StakeSchema,
StampDisplayResponse,
SubmitPassportPayload,
)
Expand Down Expand Up @@ -270,15 +271,33 @@ def stamp_display(request) -> List[StampDisplayResponse]:

@router.get(
"gtc-stake/{str:address}/{int:round_id}",
summary="Retrieve GTC stake amounts for the GTC Staking stamp",
summary="Retrieve GTC stake amounts from legacy staking contract",
description="Get self and community GTC staking amounts based on address and round ID",
auth=ApiKey(),
response=v1.GtcEventsResponse,
)
def get_gtc_stake(request, address: str, round_id: str):
def get_gtc_stake_legacy(request, address: str, round_id: str):
if not v1.is_valid_address(address):
raise InvalidAddressException()
return v1.get_gtc_stake(request, address, round_id)
return v1.get_gtc_stake_legacy(request, address, round_id)


@router.get(
"/gtc-stake/{str:address}",
# auth=ApiKey(),
auth=None,
response={
200: List[StakeSchema],
400: ErrorMessageResponse,
},
summary="Retrieve GTC stake amounts for the GTC Staking stamp",
description="Get self and community GTC stakes for an address",
)
def get_gtc_stake(request, address: str) -> List[StakeSchema]:
"""
Get GTC relevant stakes for an address
"""
return v1.get_gtc_stake(request, address)


@router.get(
Expand Down
19 changes: 19 additions & 0 deletions api/registry/migrations/0032_delete_stake_delete_stakeevent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 4.2.6 on 2024-03-15 16:20

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("registry", "0031_stakeevent_stake"),
]

operations = [
migrations.DeleteModel(
name="Stake",
),
migrations.DeleteModel(
name="StakeEvent",
),
]
65 changes: 0 additions & 65 deletions api/registry/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,68 +192,3 @@ class Meta:
name="gtc_staking_index_by_staker",
),
]


# Stores the current state of each stake
class Stake(models.Model):
class Chain(models.TextChoices):
ETHEREUM = 0, "Ethereum Mainnet"
OPTIMISM = 1, "Optimism Mainnet"

chain = models.SmallIntegerField(
choices=Chain.choices, default=Chain.ETHEREUM, db_index=True
)
unlock_time = models.DateTimeField(null=False, blank=False)
last_updated_in_block = models.DecimalField(
decimal_places=0, null=False, blank=False, max_digits=78, db_index=True
)

# For self-stake, staker and stakee are the same
staker = EthAddressField(null=False, blank=False, db_index=True)
stakee = EthAddressField(null=False, blank=False, db_index=True)

current_amount = models.DecimalField(
decimal_places=0, null=False, blank=False, max_digits=78
)

class Meta:
unique_together = ["staker", "stakee", "chain"]


# Stores raw staking events, for analysis and debugging
class StakeEvent(models.Model):
class StakeEventType(models.TextChoices):
SELF_STAKE = "SST"
COMMUNITY_STAKE = "CST"
SELF_STAKE_WITHDRAW = "SSW"
COMMUNITY_STAKE_WITHDRAW = "CSW"
SLASH = "SLA"
RELEASE = "REL"

event_type = models.CharField(
max_length=3,
choices=StakeEventType.choices,
blank=False,
db_index=True,
)

chain = models.SmallIntegerField(
choices=Stake.Chain.choices, null=False, blank=False, db_index=True
)

# For self-stake, staker and stakee are the same
staker = EthAddressField(null=False, blank=False, db_index=True)
stakee = EthAddressField(null=False, blank=False, db_index=True)

amount = models.DecimalField(
decimal_places=0, null=False, blank=False, max_digits=78
)

block_number = models.DecimalField(
decimal_places=0, null=False, blank=False, max_digits=78
)

tx_hash = models.CharField(max_length=66, null=False, blank=False)

# Only applies to SelfStake and CommunityStake events
unlock_time = models.DateTimeField(null=True, blank=True)
1 change: 1 addition & 0 deletions api/registry/test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from scorer.test.conftest import (
gtc_staking_response,
passport_holder_addresses,
sample_address,
scorer_account,
scorer_api_key,
scorer_api_key_no_permissions,
Expand Down
8 changes: 2 additions & 6 deletions api/registry/test/test_get_staking_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@

pytestmark = pytest.mark.django_db

user_address = "0x00Ac00000e4AbE2d293586A1f4F9C73e5512121e"
user_address = "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955"


class TestGetStakingResults:
@pytest.fixture
class TestGetLegacyStakingResults:
def test_successful_get_staking_results(self, scorer_api_key, gtc_staking_response):
stakes = list(GTCStakeEvent.objects.all())

Expand All @@ -24,7 +23,6 @@ def test_successful_get_staking_results(self, scorer_api_key, gtc_staking_respon
# an extra stake event was added that is below the filtered amount, hence the minus 1
assert len(stakes) - 1 == len(response_data)

@pytest.fixture
def test_item_in_filter_condition_is_not_present(
self, scorer_api_key, gtc_staking_response
):
Expand All @@ -41,7 +39,6 @@ def test_item_in_filter_condition_is_not_present(
# ID 16 belongs to the item that does not meet the filter criteria
assert item["id"] != 16

@pytest.fixture
def test_missing_address_error_response(self, scorer_api_key, gtc_staking_response):
client = Client()
response = client.get(
Expand All @@ -51,7 +48,6 @@ def test_missing_address_error_response(self, scorer_api_key, gtc_staking_respon

assert response.status_code == 404

@pytest.fixture
def test_missing_round_id_error_response(
self, scorer_api_key, gtc_staking_response
):
Expand Down
9 changes: 6 additions & 3 deletions api/scorer/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
"registry",
"scorer_apu",
"scorer_weighted",
"stake",
"ceramic_cache",
"corsheaders",
"account",
Expand Down Expand Up @@ -241,9 +242,11 @@
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "json_formatter"
if LOGGING_STRATEGY == "structlog_json"
else "plain_console",
"formatter": (
"json_formatter"
if LOGGING_STRATEGY == "structlog_json"
else "plain_console"
),
},
# "json_file": {
# "class": "logging.handlers.WatchedFileHandler",
Expand Down
4 changes: 2 additions & 2 deletions api/scorer/test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,14 +255,14 @@ def sample_token(sample_address):

@pytest.fixture
def gtc_staking_response():
user_address = "0x00Ac00000e4AbE2d293586A1f4F9C73e5512121e"
user_address = "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955"
# Make sure this one is filtered out because it's below the minimum amount
# 1 self stake of 2
GTCStakeEvent.objects.create(
id=16,
event_type="SelfStake",
round_id=1,
staker=user_address,
staker="0x976EA74026E726554dB657fA54763abd0C3a0aa9",
address=None,
amount=2,
staked=True,
Expand Down
1 change: 1 addition & 0 deletions api/scorer/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,6 @@
path("passport-admin/", passport_admin_api.urls),
# path("__debug__/", include("debug_toolbar.urls")),
path("trusta_labs/", include("trusta_labs.urls")),
path("stake/", include("stake.urls")),
path("passport/", include("passport.urls")),
]
Empty file added api/stake/__init__.py
Empty file.
53 changes: 53 additions & 0 deletions api/stake/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from django.contrib import admin
from scorer.scorer_admin import ScorerModelAdmin
from stake.models import Stake, StakeEvent


@admin.register(Stake)
class StakeAdmin(ScorerModelAdmin):
list_display = [
"id",
"chain",
"staker",
"stakee",
"lock_time",
"unlock_time",
"current_amount",
]

list_filter = [
"chain",
]

search_fields = [
"staker",
"stakee",
]
search_help_text = "Search by: " + ", ".join(search_fields)


@admin.register(StakeEvent)
class StakeEventAdmin(ScorerModelAdmin):
list_display = [
"id",
"event_type",
"staker",
"stakee",
"amount",
"block_number",
"tx_hash",
"unlock_time",
]

list_filter = [
"chain",
"event_type",
]

search_fields = [
"staker",
"stakee",
"block_number",
"tx_hash",
]
search_help_text = "Search by: " + ", ".join(search_fields)
Loading

0 comments on commit d1b06d8

Please sign in to comment.