-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
OCT-1825: Initial passthrough FastAPI server (rebased) (#525)
## Description Adds FastAPI as the main application. Mounts old flask app under /flask. If route does not exist in FastAPI it's routed to old implementation under flask. Rewrites socketio connection handling in FastAPI directly (using async). Fixes the socketio manager to share sessions via redis.
- Loading branch information
Showing
59 changed files
with
3,477 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
from typing import Annotated | ||
|
||
from fastapi import Depends | ||
from v2.allocations.services import Allocator | ||
from v2.allocations.validators import SignatureVerifier | ||
from v2.core.dependencies import GetChainSettings, GetSession | ||
from v2.epochs.dependencies import GetEpochsSubgraph, GetOpenAllocationWindowEpochNumber | ||
from v2.matched_rewards.dependencies import GetMatchedRewardsEstimator | ||
from v2.projects.dependencies import GetProjectsContracts | ||
from v2.uniqueness_quotients.dependencies import GetUQScoreGetter | ||
|
||
|
||
def get_signature_verifier( | ||
session: GetSession, | ||
epochs_subgraph: GetEpochsSubgraph, | ||
projects_contracts: GetProjectsContracts, | ||
settings: GetChainSettings, | ||
) -> SignatureVerifier: | ||
return SignatureVerifier( | ||
session, epochs_subgraph, projects_contracts, settings.chain_id | ||
) | ||
|
||
|
||
GetSignatureVerifier = Annotated[SignatureVerifier, Depends(get_signature_verifier)] | ||
|
||
|
||
async def get_allocator( | ||
epoch_number: GetOpenAllocationWindowEpochNumber, | ||
session: GetSession, | ||
signature_verifier: GetSignatureVerifier, | ||
uq_score_getter: GetUQScoreGetter, | ||
projects_contracts: GetProjectsContracts, | ||
matched_rewards_estimator: GetMatchedRewardsEstimator, | ||
) -> Allocator: | ||
return Allocator( | ||
session, | ||
signature_verifier, | ||
uq_score_getter, | ||
projects_contracts, | ||
matched_rewards_estimator, | ||
epoch_number, | ||
) | ||
|
||
|
||
GetAllocator = Annotated[Allocator, Depends(get_allocator)] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
from datetime import datetime | ||
from decimal import Decimal | ||
|
||
from app.infrastructure.database.models import Allocation | ||
from app.infrastructure.database.models import AllocationRequest as AllocationRequestDB | ||
from app.infrastructure.database.models import UniquenessQuotient, User | ||
from eth_utils import to_checksum_address | ||
from sqlalchemy import Numeric, cast, func, select, update | ||
from sqlalchemy.ext.asyncio import AsyncSession | ||
from sqlalchemy.orm import joinedload | ||
from sqlalchemy.sql.functions import coalesce | ||
from v2.allocations.schemas import ( | ||
AllocationWithUserUQScore, | ||
ProjectDonation, | ||
UserAllocationRequest, | ||
) | ||
from v2.core.types import Address | ||
from v2.users.repositories import get_user_by_address | ||
|
||
|
||
async def sum_allocations_by_epoch(session: AsyncSession, epoch_number: int) -> int: | ||
"""Get the sum of all allocations for a given epoch. We only consider the allocations that have not been deleted.""" | ||
|
||
result = await session.execute( | ||
select(coalesce(func.sum(cast(Allocation.amount, Numeric)), 0)) | ||
.filter(Allocation.epoch == epoch_number) | ||
.filter(Allocation.deleted_at.is_(None)) | ||
) | ||
count = result.scalar() | ||
|
||
if count is None: | ||
return 0 | ||
|
||
return int(count) | ||
|
||
|
||
async def get_allocations_with_user_uqs( | ||
session: AsyncSession, epoch_number: int | ||
) -> list[AllocationWithUserUQScore]: | ||
"""Get all allocations for a given epoch, including the uniqueness quotients of the users.""" | ||
|
||
result = await session.execute( | ||
select( | ||
Allocation.project_address, | ||
Allocation.amount, | ||
User.address.label("user_address"), | ||
UniquenessQuotient.score, | ||
) | ||
.join(User, Allocation.user_id == User.id) | ||
.join(UniquenessQuotient, UniquenessQuotient.user_id == User.id) | ||
.filter(Allocation.epoch == epoch_number) | ||
.filter(Allocation.deleted_at.is_(None)) | ||
.filter(UniquenessQuotient.epoch == epoch_number) | ||
) | ||
|
||
rows = result.all() | ||
|
||
return [ | ||
AllocationWithUserUQScore( | ||
projectAddress=project_address, | ||
amount=amount, | ||
userAddress=user_address, | ||
userUqScore=Decimal(uq_score), | ||
) | ||
for project_address, amount, user_address, uq_score in rows | ||
] | ||
|
||
|
||
async def soft_delete_user_allocations_by_epoch( | ||
session: AsyncSession, | ||
user_address: Address, | ||
epoch_number: int, | ||
) -> None: | ||
"""Soft delete all user allocations for a given epoch.""" | ||
|
||
# Find all the allocations for the user and epoch that have not been deleted | ||
user = await get_user_by_address(session, user_address) | ||
|
||
if user is None: | ||
return None | ||
|
||
now = datetime.utcnow() | ||
|
||
# Perform a batch update to soft delete the allocations | ||
await session.execute( | ||
update(Allocation) | ||
.where( | ||
Allocation.epoch == epoch_number, | ||
Allocation.user_id == user.id, | ||
Allocation.deleted_at.is_(None), | ||
) | ||
.values(deleted_at=now) | ||
) | ||
|
||
|
||
async def store_allocation_request( | ||
session: AsyncSession, | ||
user_address: Address, | ||
epoch_number: int, | ||
request: UserAllocationRequest, | ||
leverage: float, | ||
) -> None: | ||
"""Store an allocation request in the database.""" | ||
|
||
user = await get_user_by_address(session, user_address) | ||
if user is None: | ||
return None | ||
|
||
new_allocations = [ | ||
Allocation( | ||
epoch=epoch_number, | ||
user_id=user.id, | ||
nonce=request.nonce, | ||
project_address=to_checksum_address(a.project_address), | ||
amount=str(a.amount), | ||
) | ||
for a in request.allocations | ||
] | ||
|
||
allocation_request = AllocationRequestDB( | ||
user_id=user.id, | ||
epoch=epoch_number, | ||
nonce=request.nonce, | ||
signature=request.signature, | ||
is_manually_edited=request.is_manually_edited, | ||
leverage=leverage, | ||
) | ||
|
||
session.add(allocation_request) | ||
session.add_all(new_allocations) | ||
|
||
|
||
async def get_last_allocation_request_nonce( | ||
session: AsyncSession, | ||
user_address: Address, | ||
) -> int | None: | ||
"""Get the last nonce of the allocation requests for a user.""" | ||
|
||
user = await get_user_by_address(session, user_address) | ||
if user is None: | ||
return None | ||
|
||
return await session.scalar( | ||
select(func.max(AllocationRequestDB.nonce)).filter( | ||
AllocationRequestDB.user_id == user.id | ||
) | ||
) | ||
|
||
|
||
async def get_donations_by_project( | ||
session: AsyncSession, | ||
project_address: str, | ||
epoch_number: int, | ||
) -> list[ProjectDonation]: | ||
"""Get all donations for a project in a given epoch.""" | ||
|
||
result = await session.execute( | ||
select(Allocation) | ||
.options(joinedload(Allocation.user)) | ||
.filter(Allocation.project_address == project_address) | ||
.filter(Allocation.epoch == epoch_number) | ||
.filter(Allocation.deleted_at.is_(None)) | ||
) | ||
|
||
allocations = result.scalars().all() | ||
|
||
return [ | ||
ProjectDonation( | ||
amount=a.amount, | ||
donorAddress=a.user.address, | ||
projectAddress=a.project_address, | ||
) | ||
for a in allocations | ||
] |
Oops, something went wrong.