
- Built complete CEX platform with FastAPI and Python - JWT-based authentication system with secure password hashing - Multi-currency crypto wallet support (BTC, ETH, USDT) - Fiat account management (USD, EUR, GBP) - Local transaction signing without external APIs - Comprehensive transaction handling (send/receive/deposit/withdraw) - SQLAlchemy models with Alembic migrations - Security middleware (rate limiting, headers, logging) - Input validation and sanitization - Encrypted private key storage with PBKDF2 - Standardized codebase architecture with service layer pattern - Complete API documentation with health endpoints - Comprehensive README with setup instructions Features: - User registration and authentication - Crypto wallet creation and management - Secure transaction signing using local private keys - Fiat deposit/withdrawal system - Transaction history and tracking - Rate limiting and security headers - Input validation for all endpoints - Error handling and logging
134 lines
4.8 KiB
Python
134 lines
4.8 KiB
Python
from sqlalchemy.orm import Session
|
|
from fastapi import HTTPException, status
|
|
from typing import List, Optional
|
|
from app.models.wallet import Wallet, FiatAccount
|
|
from app.models.user import User
|
|
from app.schemas.wallet import WalletCreate, FiatAccountCreate
|
|
from app.utils.crypto import WalletFactory, CryptoUtils
|
|
from app.core.config import settings
|
|
|
|
|
|
class WalletService:
|
|
def __init__(self, db: Session):
|
|
self.db = db
|
|
|
|
def create_crypto_wallet(self, user: User, wallet_data: WalletCreate) -> Wallet:
|
|
# Check if user already has a wallet for this currency
|
|
existing_wallet = self.db.query(Wallet).filter(
|
|
Wallet.user_id == user.id,
|
|
Wallet.currency == wallet_data.currency.upper()
|
|
).first()
|
|
|
|
if existing_wallet:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=f"Wallet for {wallet_data.currency} already exists"
|
|
)
|
|
|
|
try:
|
|
# Generate wallet
|
|
private_key, address = WalletFactory.create_wallet(wallet_data.currency)
|
|
|
|
# Encrypt private key
|
|
encryption_password = f"{user.email}_{settings.secret_key}"
|
|
encrypted_private_key = CryptoUtils.encrypt_private_key(private_key, encryption_password)
|
|
|
|
# Create wallet record
|
|
db_wallet = Wallet(
|
|
user_id=user.id,
|
|
currency=wallet_data.currency.upper(),
|
|
address=address,
|
|
private_key_encrypted=encrypted_private_key,
|
|
balance=0.0
|
|
)
|
|
|
|
self.db.add(db_wallet)
|
|
self.db.commit()
|
|
self.db.refresh(db_wallet)
|
|
|
|
return db_wallet
|
|
|
|
except Exception as e:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail=f"Failed to create wallet: {str(e)}"
|
|
)
|
|
|
|
def create_fiat_account(self, user: User, account_data: FiatAccountCreate) -> FiatAccount:
|
|
# Check if user already has an account for this currency
|
|
existing_account = self.db.query(FiatAccount).filter(
|
|
FiatAccount.user_id == user.id,
|
|
FiatAccount.currency == account_data.currency.upper()
|
|
).first()
|
|
|
|
if existing_account:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=f"Fiat account for {account_data.currency} already exists"
|
|
)
|
|
|
|
db_account = FiatAccount(
|
|
user_id=user.id,
|
|
currency=account_data.currency.upper(),
|
|
balance=0.0
|
|
)
|
|
|
|
self.db.add(db_account)
|
|
self.db.commit()
|
|
self.db.refresh(db_account)
|
|
|
|
return db_account
|
|
|
|
def get_user_wallets(self, user: User) -> List[Wallet]:
|
|
return self.db.query(Wallet).filter(
|
|
Wallet.user_id == user.id,
|
|
Wallet.is_active
|
|
).all()
|
|
|
|
def get_user_fiat_accounts(self, user: User) -> List[FiatAccount]:
|
|
return self.db.query(FiatAccount).filter(
|
|
FiatAccount.user_id == user.id,
|
|
FiatAccount.is_active
|
|
).all()
|
|
|
|
def get_wallet_by_id(self, wallet_id: int, user: User) -> Optional[Wallet]:
|
|
return self.db.query(Wallet).filter(
|
|
Wallet.id == wallet_id,
|
|
Wallet.user_id == user.id
|
|
).first()
|
|
|
|
def get_fiat_account_by_id(self, account_id: int, user: User) -> Optional[FiatAccount]:
|
|
return self.db.query(FiatAccount).filter(
|
|
FiatAccount.id == account_id,
|
|
FiatAccount.user_id == user.id
|
|
).first()
|
|
|
|
def update_wallet_balance(self, wallet_id: int, new_balance: float) -> bool:
|
|
wallet = self.db.query(Wallet).filter(Wallet.id == wallet_id).first()
|
|
if wallet:
|
|
wallet.balance = new_balance
|
|
self.db.commit()
|
|
return True
|
|
return False
|
|
|
|
def update_fiat_balance(self, account_id: int, new_balance: float) -> bool:
|
|
account = self.db.query(FiatAccount).filter(FiatAccount.id == account_id).first()
|
|
if account:
|
|
account.balance = new_balance
|
|
self.db.commit()
|
|
return True
|
|
return False
|
|
|
|
def get_private_key(self, wallet: Wallet, user: User) -> str:
|
|
try:
|
|
encryption_password = f"{user.email}_{settings.secret_key}"
|
|
private_key = CryptoUtils.decrypt_private_key(
|
|
wallet.private_key_encrypted,
|
|
encryption_password
|
|
)
|
|
return private_key
|
|
except Exception:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail="Failed to decrypt private key"
|
|
) |