import sys import os sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import aiosqlite import json from datetime import datetime DATABASE_FILE = "./app_data.db" async def init_db(): async with aiosqlite.connect(DATABASE_FILE) as db: await db.executescript(""" CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL, password_hash TEXT NOT NULL, email TEXT UNIQUE NOT NULL, api_key TEXT UNIQUE, plan TEXT DEFAULT 'free' ); CREATE TABLE IF NOT EXISTS wallets ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, address TEXT NOT NULL, name TEXT, FOREIGN KEY (user_id) REFERENCES users(id) ); CREATE TABLE IF NOT EXISTS transactions ( id INTEGER PRIMARY KEY AUTOINCREMENT, wallet_id INTEGER, timestamp TEXT, type TEXT, sell_currency TEXT, sell_amount REAL, sell_value REAL, buy_currency TEXT, buy_amount REAL, buy_value REAL, closed BOOLEAN DEFAULT 0, details TEXT, solana_signature TEXT UNIQUE, FOREIGN KEY (wallet_id) REFERENCES wallets(id) ); CREATE TABLE IF NOT EXISTS holdings ( id INTEGER PRIMARY KEY AUTOINCREMENT, wallet_id INTEGER, currency TEXT, amount REAL, last_updated TEXT, FOREIGN KEY (wallet_id) REFERENCES wallets(id) ); CREATE TABLE IF NOT EXISTS price_alerts ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, currency TEXT, target_price REAL, alert_type TEXT, FOREIGN KEY (user_id) REFERENCES users(id) ); CREATE TABLE IF NOT EXISTS followed_accounts ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, address TEXT, followed_address TEXT, name TEXT, FOREIGN KEY (address) REFERENCES wallets(address), FOREIGN KEY (followed_address) REFERENCES wallets(address), FOREIGN KEY (user_id) REFERENCES users(id) ); """) await db.commit() async def store_transaction(wallet_id, transaction_type, sell_currency, sell_amount, sell_value, buy_currency, buy_amount, buy_value, solana_signature, details=None): async with aiosqlite.connect(DATABASE_FILE) as db: await db.execute(""" INSERT INTO transactions (wallet_id, timestamp, type, sell_currency, sell_amount, sell_value, buy_currency, buy_amount, buy_value, solana_signature, details) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, (wallet_id, datetime.now().isoformat(), transaction_type, sell_currency, sell_amount, sell_value, buy_currency, buy_amount, buy_value, solana_signature, json.dumps(details or {}))) await db.commit() async def update_holdings(wallet_id, currency, amount_change): async with aiosqlite.connect(DATABASE_FILE) as db: cursor = await db.execute("SELECT amount FROM holdings WHERE wallet_id = ? AND currency = ?", (wallet_id, currency)) result = await cursor.fetchone() if result: new_amount = result[0] + amount_change await db.execute("UPDATE holdings SET amount = ?, last_updated = ? WHERE wallet_id = ? AND currency = ?", (new_amount, datetime.now().isoformat(), wallet_id, currency)) else: await db.execute("INSERT INTO holdings (wallet_id, currency, amount, last_updated) VALUES (?, ?, ?, ?)", (wallet_id, currency, amount_change, datetime.now().isoformat())) await db.commit() async def get_wallet_holdings(wallet_id): async with aiosqlite.connect(DATABASE_FILE) as db: cursor = await db.execute("SELECT currency, amount FROM holdings WHERE wallet_id = ?", (wallet_id,)) return await cursor.fetchall() async def get_transaction_history(wallet_id, start_date=None, end_date=None, include_closed=False): async with aiosqlite.connect(DATABASE_FILE) as db: query = "SELECT * FROM transactions WHERE wallet_id = ?" params = [wallet_id] if not include_closed: query += " AND closed = 0" if start_date: query += " AND timestamp >= ?" params.append(start_date) if end_date: query += " AND timestamp <= ?" params.append(end_date) query += " ORDER BY timestamp DESC" cursor = await db.execute(query, params) return await cursor.fetchall() # New utility functions async def close_transaction(transaction_id): async with aiosqlite.connect(DATABASE_FILE) as db: await db.execute("UPDATE transactions SET closed = 1 WHERE id = ?", (transaction_id,)) await db.commit() async def get_open_transactions(wallet_id, currency): async with aiosqlite.connect(DATABASE_FILE) as db: cursor = await db.execute(""" SELECT * FROM transactions WHERE wallet_id = ? AND buy_currency = ? AND closed = 0 ORDER BY timestamp ASC """, (wallet_id, currency)) return await cursor.fetchall() async def calculate_current_holdings(wallet_id): async with aiosqlite.connect(DATABASE_FILE) as db: cursor = await db.execute(""" SELECT buy_currency AS currency, SUM(buy_amount) - COALESCE( (SELECT SUM(sell_amount) FROM transactions t2 WHERE t2.wallet_id = t1.wallet_id AND t2.sell_currency = t1.buy_currency AND t2.closed = 0), 0 ) AS amount FROM transactions t1 WHERE wallet_id = ? AND closed = 0 GROUP BY buy_currency HAVING amount > 0 """, (wallet_id,)) return await cursor.fetchall() STABLECOINS = ['USDC', 'USDT', 'SOL'] async def is_transaction_closed(wallet_id, transaction_id): async with aiosqlite.connect(DATABASE_FILE) as db: cursor = await db.execute(""" SELECT t1.buy_currency, t1.buy_amount, (SELECT SUM(sell_amount) FROM transactions t2 WHERE t2.wallet_id = t1.wallet_id AND t2.sell_currency = t1.buy_currency AND t2.timestamp > t1.timestamp) AS sold_amount FROM transactions t1 WHERE t1.id = ? AND t1.wallet_id = ? """, (transaction_id, wallet_id)) result = await cursor.fetchone() if result: buy_currency, buy_amount, sold_amount = result return sold_amount is not None and sold_amount >= buy_amount return False async def close_completed_transactions(wallet_id): async with aiosqlite.connect(DATABASE_FILE) as db: cursor = await db.execute(""" SELECT id FROM transactions WHERE wallet_id = ? AND closed = 0 AND buy_currency NOT IN (?) """, (wallet_id, ','.join(STABLECOINS))) transactions = await cursor.fetchall() for (transaction_id,) in transactions: if await is_transaction_closed(wallet_id, transaction_id): await close_transaction(transaction_id) async def get_profit_loss(wallet_id, currency, start_date=None, end_date=None): async with aiosqlite.connect(DATABASE_FILE) as db: query = """ SELECT SUM(CASE WHEN sell_currency = ? THEN sell_value ELSE -buy_value END) as profit_loss FROM transactions WHERE wallet_id = ? AND (sell_currency = ? OR buy_currency = ?) """ params = [currency, wallet_id, currency, currency] if start_date: query += " AND timestamp >= ?" params.append(start_date) if end_date: query += " AND timestamp <= ?" params.append(end_date) cursor = await db.execute(query, params) result = await cursor.fetchone() return result[0] if result else 0 # # # # # # USERS # For this example, we'll use a simple dictionary to store users users = { "db": {"id": 1, "username": "db", "email": "user1@example.com", "password": "db"}, "popov": {"id": 2, "username": "popov", "email": "user2@example.com", "password": "popov"} } def get_or_create_user(email, google_id): user = next((u for u in users.values() if u['email'] == email), None) if not user: user_id = max(u['id'] for u in users.values()) + 1 username = email.split('@')[0] # Use the part before @ as username user = { 'id': user_id, 'username': username, 'email': email, 'google_id': google_id } users[username] = user return user def authenticate_user(username, password): """ Authenticate a user based on username and password. Returns user data if authentication is successful, None otherwise. """ user = users.get(username) if user and user['password'] == password: return {"id": user['id'], "username": user['username'], "email": user['email']} return None def get_user_by_id(user_id): """ Retrieve a user by their ID. """ for user in users.values(): if user['id'] == int(user_id): return {"id": user['id'], "username": user['username'], "email": user['email']} return None def store_api_key(user_id, api_key): """ Store the generated API key for a user. """ # In a real application, you would store this in a database # For this example, we'll just print it print(f"Storing API key {api_key} for user {user_id}") # async def get_new_transactions(wallet_address, rpc_url): # async with AsyncClient(rpc_url) as client: # last_tx = await get_last_stored_transaction(wallet_address) # if last_tx: # last_signature, last_timestamp = last_tx # else: # # If no transactions are stored, we'll fetch all transactions # last_signature = None # last_timestamp = None # new_transactions = [] # # Get the transaction history for the wallet # tx_history = await client.get_signatures_for_address(wallet_address, before=last_signature) # for tx in tx_history.value: # # Check if the transaction is newer than the last stored one # if not last_timestamp or tx.block_time > datetime.fromisoformat(last_timestamp).timestamp(): # # Fetch the full transaction details # tx_details = await client.get_transaction(tx.signature, commitment=Confirmed) # new_transactions.append(tx_details) # return new_transactions # async def process_new_transactions(wallet_id, wallet_address, rpc_url): # new_transactions = await get_new_transactions(wallet_address, rpc_url) # for tx in new_transactions: # # Process the transaction and extract relevant information # # This is a placeholder - you'll need to implement the actual logic based on your requirements # transaction_type = "swap" # Determine the type based on the transaction data # sell_currency = "SOL" # Extract from transaction data # sell_amount = 1.0 # Extract from transaction data # sell_value = 100.0 # Extract from transaction data # buy_currency = "USDC" # Extract from transaction data # buy_amount = 100.0 # Extract from transaction data # buy_value = 100.0 # Extract from transaction data # solana_signature = tx.transaction.signatures[0] # # Store the transaction in the database # await store_transaction( # wallet_id, transaction_type, sell_currency, sell_amount, sell_value, # buy_currency, buy_amount, buy_value, solana_signature # ) # # Update holdings # await update_holdings(wallet_id, sell_currency, -sell_amount) # await update_holdings(wallet_id, buy_currency, buy_amount) # # After processing all new transactions, close completed transactions # await close_completed_transactions(wallet_id) # Example usage if __name__ == "__main__": import asyncio async def main(): await init_db() # Add more test functions here asyncio.run(main())