
- Create project structure with FastAPI - Add database models for blocks, transactions, arbitrages, pools, and DEXes - Implement Solana RPC client for fetching blockchain data - Create arbitrage detection algorithm - Implement comprehensive API endpoints for analytics - Set up database migrations with Alembic - Add detailed project documentation generated with BackendIM... (backend.im) Co-Authored-By: Claude <noreply@anthropic.com>
99 lines
3.5 KiB
Python
99 lines
3.5 KiB
Python
from typing import List, Optional, Dict, Any, Union, Tuple
|
|
|
|
from sqlalchemy import func, desc, or_, and_
|
|
from sqlalchemy.orm import Session, joinedload
|
|
|
|
from app.crud.base import CRUDBase
|
|
from app.models.pool import Pool
|
|
from app.models.dex import Dex
|
|
from app.schemas.pool import PoolCreate, PoolUpdate
|
|
|
|
|
|
class CRUDPool(CRUDBase[Pool, PoolCreate, PoolUpdate]):
|
|
"""CRUD operations for liquidity pools."""
|
|
|
|
def get_by_address(self, db: Session, *, address: str) -> Optional[Pool]:
|
|
"""Get a pool by its address."""
|
|
return db.query(Pool).filter(Pool.address == address).first()
|
|
|
|
def get_by_dex(
|
|
self, db: Session, *, dex_id: int, skip: int = 0, limit: int = 100
|
|
) -> List[Pool]:
|
|
"""Get pools for a specific DEX."""
|
|
return db.query(Pool).filter(
|
|
Pool.dex_id == dex_id
|
|
).offset(skip).limit(limit).all()
|
|
|
|
def get_by_token(
|
|
self, db: Session, *, token_address: str, skip: int = 0, limit: int = 100
|
|
) -> List[Pool]:
|
|
"""Get pools that contain a specific token."""
|
|
return db.query(Pool).filter(
|
|
or_(
|
|
Pool.token_a_address == token_address,
|
|
Pool.token_b_address == token_address
|
|
)
|
|
).offset(skip).limit(limit).all()
|
|
|
|
def get_by_token_pair(
|
|
self, db: Session, *, token_a: str, token_b: str, skip: int = 0, limit: int = 100
|
|
) -> List[Pool]:
|
|
"""Get pools for a specific token pair."""
|
|
return db.query(Pool).filter(
|
|
or_(
|
|
and_(Pool.token_a_address == token_a, Pool.token_b_address == token_b),
|
|
and_(Pool.token_a_address == token_b, Pool.token_b_address == token_a)
|
|
)
|
|
).offset(skip).limit(limit).all()
|
|
|
|
def get_by_tvl_range(
|
|
self, db: Session, *, min_tvl: float, max_tvl: Optional[float] = None,
|
|
skip: int = 0, limit: int = 100
|
|
) -> List[Pool]:
|
|
"""Get pools within a TVL range."""
|
|
query = db.query(Pool).filter(Pool.tvl >= min_tvl)
|
|
if max_tvl is not None:
|
|
query = query.filter(Pool.tvl <= max_tvl)
|
|
return query.order_by(desc(Pool.tvl)).offset(skip).limit(limit).all()
|
|
|
|
def get_pools_with_dex(
|
|
self, db: Session, *, skip: int = 0, limit: int = 100
|
|
) -> List[Tuple[Pool, Dex]]:
|
|
"""Get pools with their DEX information."""
|
|
return db.query(
|
|
Pool, Dex
|
|
).join(
|
|
Dex, Pool.dex_id == Dex.id
|
|
).order_by(
|
|
desc(Pool.tvl)
|
|
).offset(skip).limit(limit).all()
|
|
|
|
def update_reserves(
|
|
self, db: Session, *, pool_id: int,
|
|
token_a_reserve: float, token_b_reserve: float,
|
|
slot: int, tvl: Optional[float] = None
|
|
) -> Optional[Pool]:
|
|
"""Update token reserves for a pool."""
|
|
db_obj = db.query(Pool).filter(Pool.id == pool_id).first()
|
|
if db_obj and slot >= db_obj.last_updated_slot:
|
|
db_obj.token_a_reserve = token_a_reserve
|
|
db_obj.token_b_reserve = token_b_reserve
|
|
db_obj.last_updated_slot = slot
|
|
if tvl is not None:
|
|
db_obj.tvl = tvl
|
|
db.add(db_obj)
|
|
db.commit()
|
|
db.refresh(db_obj)
|
|
return db_obj
|
|
|
|
def get_most_active_pools(
|
|
self, db: Session, *, limit: int = 20
|
|
) -> List[Pool]:
|
|
"""Get the most active pools by volume."""
|
|
return db.query(Pool).order_by(
|
|
desc(Pool.volume_24h)
|
|
).limit(limit).all()
|
|
|
|
|
|
# Create a single instance for use in dependency injection
|
|
pool = CRUDPool(Pool) |