Skip to content

Commit

Permalink
Merge pull request #368 from OriginProtocol/shah/tvl-history
Browse files Browse the repository at this point in the history
Add TVL history endpoint
  • Loading branch information
shahthepro authored Aug 24, 2023
2 parents eaa53df + 80a6fa0 commit bb880b7
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 39 deletions.
22 changes: 15 additions & 7 deletions eagleproject/core/blockchain/harvest/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@
)

from django.db.utils import IntegrityError
from django.core.exceptions import ObjectDoesNotExist

logger = get_logger(__name__)


def ensure_block(block_number):
stored_block = Block.objects.filter(block_number=block_number).first()
if stored_block is not None:
return stored_block

try:
stored_block = Block.objects.get(block_number=block_number)
if stored_block is not None:
return stored_block
except ObjectDoesNotExist:
pass

raw_block = get_block(block_number)
block_time = datetime.fromtimestamp(
int(raw_block["timestamp"], 16),
Expand All @@ -43,9 +47,13 @@ def ensure_day(d):
Requires there to be a block stored in the system before and after the target.
... More code could easily remove this restriction.
"""
stored_day = Day.objects.filter(date=d.date()).first()
if stored_day is not None:
return stored_day
try:
stored_day = Day.objects.get(date=d.date())
if stored_day is not None:
return stored_day
except ObjectDoesNotExist:
pass

d = datetime(d.year, d.month, d.day) # Zero seconds, keep timezone
# Offset for the window on the next day
target = (d + timedelta(seconds=26100)).replace(tzinfo=timezone.utc)
Expand Down
117 changes: 85 additions & 32 deletions eagleproject/core/views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import datetime
from datetime import datetime, timezone, timedelta
from decimal import Decimal
from django.shortcuts import render, redirect
from django.http import HttpResponse, JsonResponse
Expand Down Expand Up @@ -91,6 +91,8 @@
from core.blockchain.strategies import OUSD_STRATEGIES, OUSD_BACKING_ASSETS
from core.blockchain.strategies import OETH_STRATEGIES, OETH_BACKING_ASSETS

from core.blockchain.harvest.blocks import ensure_block, ensure_day

log = get_logger(__name__)


Expand Down Expand Up @@ -322,7 +324,7 @@ def remove_specific_month_report(request, month):
print("Reports disabled on this instance")
return HttpResponse("ok")

year = datetime.datetime.now().year
year = datetime.now().year
report = AnalyticsReport.objects.get(month=month, year=year)
report.delete()
return HttpResponse("ok")
Expand All @@ -333,7 +335,7 @@ def remove_specific_week_report(request, week):
print("Reports disabled on this instance")
return HttpResponse("ok")

year = datetime.datetime.now().year
year = datetime.now().year
report = AnalyticsReport.objects.get(week=week, year=year)
report.delete()
return HttpResponse("ok")
Expand Down Expand Up @@ -555,7 +557,7 @@ def api_address(request, project):

def active_stake_stats():
""" Get stats of the active stakes grouped by duration """
utc_now = datetime.datetime.now(tz=datetime.timezone.utc)
utc_now = datetime.now(tz=timezone.utc)

""" Trying to do aggregate math and Withdraw inferences here. This data is
a dict of OgnStaked with user_address as key. We should be able to filter
Expand Down Expand Up @@ -621,11 +623,11 @@ def active_stake_stats():
# Now we need to bucket all active stakes
for user_address in unique_addresses:
for stake in user_aggreate[user_address]:
if stake.staked_duration == datetime.timedelta(days=30):
if stake.staked_duration == timedelta(days=30):
total_30 += stake.amount
elif stake.staked_duration == datetime.timedelta(days=90):
elif stake.staked_duration == timedelta(days=90):
total_90 += stake.amount
elif stake.staked_duration == datetime.timedelta(days=365):
elif stake.staked_duration == timedelta(days=365):
total_365 += stake.amount
else:
log.error(
Expand Down Expand Up @@ -744,25 +746,23 @@ def api_address_history(request, address, project=OriginTokens.OUSD):
response.setdefault("Access-Control-Allow-Origin", "*")
return response


def strategies(request, project=OriginTokens.OUSD):
block_number = latest_snapshot_block_number(project)
def _get_allocations_on_block(block_number, project):
block = ensure_block(block_number)
assets = fetch_assets(block_number, project)
snapshot = snapshot_at_block(block_number, project)

eth_snap = next(snap for snap in ensure_oracle_snapshot(block_number) if snap.ticker_left=="ETH" and snap.ticker_right=="USD")

all_strats = _get_strat_holdings(assets, project=project)

# Returns an object with UUID as keys when set, otherwise returns an array
structured = project == OriginTokens.OETH or request.GET.get("structured") is not None

net_tvl = Decimal(0)
protocol_owned_supply = Decimal(0)
for (key, strat) in all_strats.items():
holdings = {}
holdings_value = {}
for (asset, holding) in strat["holdings"]:
holdings[asset] = Decimal(holding or 0)
if asset == "OUSD" or asset == "OETH":
protocol_owned_supply += holdings[asset]
for (asset, holding) in strat["holdings_value"]:
holdings_value[asset] = Decimal(holding or 0)
strat["total"] = Decimal(strat["total"] or 0)
Expand All @@ -771,28 +771,81 @@ def strategies(request, project=OriginTokens.OUSD):
strat["holdings"] = holdings
strat["holdings_value"] = holdings_value

if structured is None:
# TODO: Backward compatibility, remove after making sure that every repo has been updated
all_strats = [{
"name": strat["name"],
"total": strat["total"],
"dai": strat["holdings"].get("DAI"),
"usdt": strat["holdings"].get("USDT"),
"usdc": strat["holdings"].get("USDC"),
"ousd": strat["holdings"].get("OUSD"),
"lusd": strat["holdings"].get("LUSD"),
} for (strat_key, strat) in all_strats.items()]
circulating_supply = Decimal(snapshot.reported_supply) - protocol_owned_supply

response = JsonResponse({
return {
"block_time": block.block_time.replace(tzinfo=timezone.utc).timestamp(),
"block_number": block_number,
"strategies": all_strats,
"total_value": net_tvl,
"total_value_usd": net_tvl * (eth_snap.price if project == OriginTokens.OETH else 1),
"eth_price": eth_snap.price,
"total_supply": Decimal(snapshot.reported_supply)
}, encoder=DecimalEncoder)
"total_supply": Decimal(snapshot.reported_supply),
"circulating_supply": circulating_supply,
"protocol_owned_supply": protocol_owned_supply
}

def strategies(request, project=OriginTokens.OUSD):
block_number = latest_snapshot_block_number(project)

allocations = _get_allocations_on_block(
block_number,
project,
)

# Returns an object with UUID as keys when set, otherwise returns an array
structured = (project == OriginTokens.OETH or request.GET.get("structured") is not None)
if not structured:
# TODO: Backward compatibility, remove after making sure that every repo has been updated
allocations["strategies"] = [{
"name": strat["name"],
"total": strat["total"],
"dai": strat["holdings"].get("DAI", Decimal(0)),
"usdt": strat["holdings"].get("USDT", Decimal(0)),
"usdc": strat["holdings"].get("USDC", Decimal(0)),
"ousd": strat["holdings"].get("OUSD", Decimal(0)),
"lusd": strat["holdings"].get("LUSD", Decimal(0)),
} for (strat_key, strat) in allocations["strategies"].items()]


response = JsonResponse(allocations, encoder=DecimalEncoder)
response.setdefault("Access-Control-Allow-Origin", "*")
return _cache(120, response)

def tvl_history(request, project=OriginTokens.OUSD, days=30):
block_numbers = []

today = datetime.utcnow()
day_pointer = datetime(today.year, today.month, today.day).replace(
tzinfo=timezone.utc
)

for i in range(0, days):
day = ensure_day(day_pointer)
if day is not None:
block_numbers.append(day.block_number)
day_pointer = (
day_pointer - timedelta(seconds=24 * 60 * 60)
).replace(tzinfo=timezone.utc)

START_OF_PROJECT = START_OF_OUSD_V2 if project == OriginTokens.OUSD else START_OF_OETH
block_numbers = list(filter(lambda x: x >= START_OF_PROJECT, dict.fromkeys(block_numbers)))
block_numbers.reverse()

tvl_data = []

for block_number in block_numbers:
allocations = _get_allocations_on_block(
block_number,
project
)
tvl_data.append(allocations)

response = JsonResponse({
"data": tvl_data
}, encoder=DecimalEncoder)
response.setdefault("Access-Control-Allow-Origin", "*")
return _cache(120, response)

def collateral(request, project):
block_number = latest_snapshot_block_number(project=project)
Expand Down Expand Up @@ -867,7 +920,7 @@ def report_monthly(request, year, month):
is_monthly = True
change = calculate_report_change(report, prev_report)
report.transaction_report = json.loads(str(report.transaction_report))
latest = year == datetime.datetime.now().year and month == int(datetime.datetime.now().strftime("%m")) - 1
latest = year == datetime.now().year and month == int(datetime.now().strftime("%m")) - 1
if month == 12:
next_month = 0
next_year = year + 1
Expand All @@ -892,7 +945,7 @@ def report_weekly(request, year, week):
is_monthly = False
change = calculate_report_change(report, prev_report)
report.transaction_report = json.loads(str(report.transaction_report))
latest = year == datetime.datetime.now().year and week == int(datetime.datetime.now().strftime("%W")) - 1
latest = year == datetime.now().year and week == int(datetime.now().strftime("%W")) - 1
if week == 51:
next_week = 0
next_year = year + 1
Expand All @@ -906,8 +959,8 @@ def report_weekly(request, year, week):


def report_latest_weekly(request):
year = datetime.datetime.now().year
week = int(datetime.datetime.now().strftime("%W")) - 1
year = datetime.now().year
week = int(datetime.now().strftime("%W")) - 1
return redirect("weekly", year, week)


Expand Down
1 change: 1 addition & 0 deletions eagleproject/eagleproject/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
path("api/v2/<slug:project>/address/<slug:address>/yield", core_views.api_address_yield, name="api_address_yield"),
path("api/v2/<slug:project>/address/", core_views.api_address),
path("api/v2/<slug:project>/address/<slug:address>/history", core_views.api_address_history, name="api_address_history"),
path("api/v2/<slug:project>/tvl_history/<int:days>", core_views.tvl_history),
path("api/v2/<slug:project>/strategies", core_views.strategies),
path("api/v2/<slug:project>/collateral", core_views.collateral),
path("api/v2/<slug:project>/apr/trailing_history/<int:days>", core_views.api_apr_trailing_history),
Expand Down

0 comments on commit bb880b7

Please sign in to comment.