Files
aitbc/apps/exchange/complete_cross_chain_exchange.py
oib bb5363bebc refactor: consolidate blockchain explorer into single app and update backup ignore patterns
- Remove standalone explorer-web app (README, HTML, package files)
- Add /web endpoint to blockchain-explorer for web interface access
- Update .gitignore to exclude application backup archives (*.tar.gz, *.zip)
- Add backup documentation files to .gitignore (BACKUP_INDEX.md, README.md)
- Consolidate explorer functionality into main blockchain-explorer application
2026-03-06 18:14:49 +01:00

652 lines
24 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Complete Cross-Chain AITBC Exchange
Multi-chain trading with cross-chain swaps and bridging
"""
import sqlite3
import json
import asyncio
import httpx
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Any
from fastapi import FastAPI, HTTPException, Query, BackgroundTasks
from pydantic import BaseModel, Field
import uvicorn
import os
import uuid
import hashlib
app = FastAPI(title="AITBC Complete Cross-Chain Exchange", version="3.0.0")
# Database configuration
DB_PATH = os.path.join(os.path.dirname(__file__), "exchange_multichain.db")
# Supported chains
SUPPORTED_CHAINS = {
"ait-devnet": {
"name": "AITBC Development Network",
"status": "active",
"blockchain_url": "http://localhost:8007",
"token_symbol": "AITBC-DEV",
"bridge_contract": "0x1234567890123456789012345678901234567890"
},
"ait-testnet": {
"name": "AITBC Test Network",
"status": "inactive",
"blockchain_url": None,
"token_symbol": "AITBC-TEST",
"bridge_contract": "0x0987654321098765432109876543210987654321"
}
}
# Models
class OrderRequest(BaseModel):
order_type: str = Field(..., regex="^(BUY|SELL)$")
amount: float = Field(..., gt=0)
price: float = Field(..., gt=0)
chain_id: str = Field(..., regex="^(ait-devnet|ait-testnet)$")
user_address: str = Field(..., min_length=1)
class CrossChainSwapRequest(BaseModel):
from_chain: str = Field(..., regex="^(ait-devnet|ait-testnet)$")
to_chain: str = Field(..., regex="^(ait-devnet|ait-testnet)$")
from_token: str = Field(..., min_length=1)
to_token: str = Field(..., min_length=1)
amount: float = Field(..., gt=0)
min_amount: float = Field(..., gt=0)
user_address: str = Field(..., min_length=1)
slippage_tolerance: float = Field(default=0.01, ge=0, le=0.1)
class BridgeRequest(BaseModel):
source_chain: str = Field(..., regex="^(ait-devnet|ait-testnet)$")
target_chain: str = Field(..., regex="^(ait-devnet|ait-testnet)$")
token: str = Field(..., min_length=1)
amount: float = Field(..., gt=0)
recipient_address: str = Field(..., min_length=1)
# Database functions
def get_db_connection():
"""Get database connection"""
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
return conn
def init_database():
"""Initialize complete cross-chain database"""
try:
conn = get_db_connection()
cursor = conn.cursor()
# Chains table
cursor.execute('''
CREATE TABLE IF NOT EXISTS chains (
chain_id TEXT PRIMARY KEY,
name TEXT NOT NULL,
status TEXT NOT NULL CHECK(status IN ('active', 'inactive', 'maintenance')),
blockchain_url TEXT,
token_symbol TEXT,
bridge_contract TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# Orders table with chain support
cursor.execute('''
CREATE TABLE IF NOT EXISTS orders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
order_type TEXT NOT NULL CHECK(order_type IN ('BUY', 'SELL')),
amount REAL NOT NULL,
price REAL NOT NULL,
total REAL NOT NULL,
filled REAL DEFAULT 0,
remaining REAL NOT NULL,
status TEXT DEFAULT 'open' CHECK(status IN ('open', 'filled', 'cancelled')),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
user_address TEXT,
tx_hash TEXT,
chain_id TEXT NOT NULL DEFAULT 'ait-devnet',
blockchain_tx_hash TEXT,
chain_status TEXT DEFAULT 'pending' CHECK(chain_status IN ('pending', 'confirmed', 'failed'))
)
''')
# Trades table with chain support
cursor.execute('''
CREATE TABLE IF NOT EXISTS trades (
id INTEGER PRIMARY KEY AUTOINCREMENT,
buy_order_id INTEGER,
sell_order_id INTEGER,
amount REAL NOT NULL,
price REAL NOT NULL,
total REAL NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
chain_id TEXT NOT NULL DEFAULT 'ait-devnet',
blockchain_tx_hash TEXT,
chain_status TEXT DEFAULT 'pending' CHECK(chain_status IN ('pending', 'confirmed', 'failed'))
)
''')
# Cross-chain swaps table
cursor.execute('''
CREATE TABLE IF NOT EXISTS cross_chain_swaps (
id INTEGER PRIMARY KEY AUTOINCREMENT,
swap_id TEXT UNIQUE NOT NULL,
from_chain TEXT NOT NULL,
to_chain TEXT NOT NULL,
from_token TEXT NOT NULL,
to_token TEXT NOT NULL,
amount REAL NOT NULL,
min_amount REAL NOT NULL,
expected_amount REAL NOT NULL,
actual_amount REAL DEFAULT NULL,
user_address TEXT NOT NULL,
status TEXT DEFAULT 'pending' CHECK(status IN ('pending', 'executing', 'completed', 'failed', 'refunded')),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
completed_at TIMESTAMP NULL,
from_tx_hash TEXT NULL,
to_tx_hash TEXT NULL,
bridge_fee REAL DEFAULT 0,
slippage REAL DEFAULT 0,
error_message TEXT NULL
)
''')
# Bridge transactions table
cursor.execute('''
CREATE TABLE IF NOT EXISTS bridge_transactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
bridge_id TEXT UNIQUE NOT NULL,
source_chain TEXT NOT NULL,
target_chain TEXT NOT NULL,
token TEXT NOT NULL,
amount REAL NOT NULL,
recipient_address TEXT NOT NULL,
status TEXT DEFAULT 'pending' CHECK(status IN ('pending', 'locked', 'transferred', 'completed', 'failed')),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
completed_at TIMESTAMP NULL,
source_tx_hash TEXT NULL,
target_tx_hash TEXT NULL,
bridge_fee REAL DEFAULT 0,
lock_address TEXT NULL,
error_message TEXT NULL
)
''')
# Cross-chain liquidity pools
cursor.execute('''
CREATE TABLE IF NOT EXISTS cross_chain_pools (
id INTEGER PRIMARY KEY AUTOINCREMENT,
pool_id TEXT UNIQUE NOT NULL,
token_a TEXT NOT NULL,
token_b TEXT NOT NULL,
chain_a TEXT NOT NULL,
chain_b TEXT NOT NULL,
reserve_a REAL DEFAULT 0,
reserve_b REAL DEFAULT 0,
total_liquidity REAL DEFAULT 0,
apr REAL DEFAULT 0,
fee_rate REAL DEFAULT 0.003,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# Insert default chains
for chain_id, chain_info in SUPPORTED_CHAINS.items():
cursor.execute('''
INSERT OR REPLACE INTO chains
(chain_id, name, status, blockchain_url, token_symbol, bridge_contract)
VALUES (?, ?, ?, ?, ?, ?)
''', (chain_id, chain_info["name"], chain_info["status"],
chain_info["blockchain_url"], chain_info["token_symbol"],
chain_info.get("bridge_contract")))
# Create sample liquidity pool
cursor.execute('''
INSERT OR IGNORE INTO cross_chain_pools
(pool_id, token_a, token_b, chain_a, chain_b, reserve_a, reserve_b, total_liquidity)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
''', ("ait-devnet-ait-testnet-AITBC", "AITBC", "AITBC", "ait-devnet", "ait-testnet", 1000, 1000, 2000))
# Create indexes
cursor.execute('CREATE INDEX IF NOT EXISTS idx_orders_chain_id ON orders(chain_id)')
cursor.execute('CREATE INDEX IF NOT EXISTS idx_trades_chain_id ON trades(chain_id)')
cursor.execute('CREATE INDEX IF NOT EXISTS idx_swaps_user ON cross_chain_swaps(user_address)')
cursor.execute('CREATE INDEX IF NOT EXISTS idx_swaps_status ON cross_chain_swaps(status)')
cursor.execute('CREATE INDEX IF NOT EXISTS idx_bridge_status ON bridge_transactions(status)')
conn.commit()
conn.close()
return True
except Exception as e:
print(f"Database initialization error: {e}")
return False
# Cross-chain rate calculation
def get_cross_chain_rate(from_chain: str, to_chain: str, from_token: str, to_token: str) -> Optional[float]:
"""Get cross-chain exchange rate"""
try:
conn = get_db_connection()
cursor = conn.cursor()
# Check liquidity pool
cursor.execute('''
SELECT reserve_a, reserve_b FROM cross_chain_pools
WHERE ((chain_a = ? AND chain_b = ? AND token_a = ? AND token_b = ?) OR
(chain_a = ? AND chain_b = ? AND token_a = ? AND token_b = ?))
''', (from_chain, to_chain, from_token, to_token, to_chain, from_chain, to_token, from_token))
pool = cursor.fetchone()
if pool and pool["reserve_a"] > 0 and pool["reserve_b"] > 0:
return pool["reserve_b"] / pool["reserve_a"]
# Fallback to 1:1 for same tokens
if from_token == to_token:
return 1.0
return 1.0 # Default fallback rate
except Exception as e:
print(f"Rate calculation error: {e}")
return None
# Cross-chain swap execution
async def execute_cross_chain_swap(swap_request: CrossChainSwapRequest) -> Dict[str, Any]:
"""Execute cross-chain swap"""
try:
# Validate chains
if swap_request.from_chain == swap_request.to_chain:
raise HTTPException(status_code=400, detail="Cannot swap within same chain")
# Get exchange rate
rate = get_cross_chain_rate(swap_request.from_chain, swap_request.to_chain,
swap_request.from_token, swap_request.to_token)
if not rate:
raise HTTPException(status_code=400, detail="No exchange rate available")
# Calculate expected amount (including fees)
bridge_fee = swap_request.amount * 0.003 # 0.3% bridge fee
swap_fee = swap_request.amount * 0.001 # 0.1% swap fee
total_fees = bridge_fee + swap_fee
net_amount = swap_request.amount - total_fees
expected_amount = net_amount * rate
# Check slippage
if expected_amount < swap_request.min_amount:
raise HTTPException(status_code=400, detail="Insufficient output due to slippage")
# Create swap record
swap_id = str(uuid.uuid4())
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute('''
INSERT INTO cross_chain_swaps
(swap_id, from_chain, to_chain, from_token, to_token, amount, min_amount,
expected_amount, user_address, bridge_fee, slippage)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (swap_id, swap_request.from_chain, swap_request.to_chain, swap_request.from_token,
swap_request.to_token, swap_request.amount, swap_request.min_amount, expected_amount,
swap_request.user_address, bridge_fee, swap_request.slippage_tolerance))
conn.commit()
conn.close()
# Process swap in background
asyncio.create_task(process_cross_chain_swap(swap_id))
return {
"success": True,
"swap_id": swap_id,
"from_chain": swap_request.from_chain,
"to_chain": swap_request.to_chain,
"from_token": swap_request.from_token,
"to_token": swap_request.to_token,
"amount": swap_request.amount,
"expected_amount": expected_amount,
"rate": rate,
"total_fees": total_fees,
"bridge_fee": bridge_fee,
"swap_fee": swap_fee,
"status": "pending"
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Swap execution failed: {str(e)}")
async def process_cross_chain_swap(swap_id: str):
"""Process cross-chain swap"""
try:
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("SELECT * FROM cross_chain_swaps WHERE swap_id = ?", (swap_id,))
swap = cursor.fetchone()
if not swap:
return
# Update status
cursor.execute("UPDATE cross_chain_swaps SET status = 'executing' WHERE swap_id = ?", (swap_id,))
conn.commit()
# Simulate cross-chain execution
await asyncio.sleep(3) # Simulate blockchain processing
# Generate mock transaction hashes
from_tx_hash = f"0x{uuid.uuid4().hex[:64]}"
to_tx_hash = f"0x{uuid.uuid4().hex[:64]}"
# Complete swap
actual_amount = swap["expected_amount"] * 0.98 # Small slippage
cursor.execute('''
UPDATE cross_chain_swaps SET status = 'completed', actual_amount = ?,
from_tx_hash = ?, to_tx_hash = ?, completed_at = CURRENT_TIMESTAMP
WHERE swap_id = ?
''', (actual_amount, from_tx_hash, to_tx_hash, swap_id))
conn.commit()
conn.close()
except Exception as e:
print(f"Cross-chain swap processing error: {e}")
# API Endpoints
@app.get("/health")
async def health_check():
"""Complete cross-chain health check"""
chain_status = {}
for chain_id, chain_info in SUPPORTED_CHAINS.items():
chain_status[chain_id] = {
"name": chain_info["name"],
"status": chain_info["status"],
"blockchain_url": chain_info["blockchain_url"],
"connected": False,
"bridge_contract": chain_info.get("bridge_contract")
}
if chain_info["status"] == "active" and chain_info["blockchain_url"]:
try:
async with httpx.AsyncClient() as client:
response = await client.get(f"{chain_info['blockchain_url']}/health", timeout=5.0)
chain_status[chain_id]["connected"] = response.status_code == 200
except:
pass
return {
"status": "ok",
"service": "complete-cross-chain-exchange",
"version": "3.0.0",
"supported_chains": list(SUPPORTED_CHAINS.keys()),
"chain_status": chain_status,
"cross_chain": True,
"features": ["trading", "swaps", "bridging", "liquidity_pools"],
"timestamp": datetime.now().isoformat()
}
@app.get("/api/v1/chains")
async def get_chains():
"""Get all supported chains"""
chains = []
for chain_id, chain_info in SUPPORTED_CHAINS.items():
chains.append({
"chain_id": chain_id,
"name": chain_info["name"],
"status": chain_info["status"],
"blockchain_url": chain_info["blockchain_url"],
"token_symbol": chain_info["token_symbol"],
"bridge_contract": chain_info.get("bridge_contract")
})
return {
"chains": chains,
"total_chains": len(chains),
"active_chains": len([c for c in chains if c["status"] == "active"])
}
@app.post("/api/v1/cross-chain/swap")
async def create_cross_chain_swap(swap_request: CrossChainSwapRequest):
"""Create cross-chain swap"""
return await execute_cross_chain_swap(swap_request)
@app.get("/api/v1/cross-chain/swap/{swap_id}")
async def get_cross_chain_swap(swap_id: str):
"""Get cross-chain swap details"""
try:
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("SELECT * FROM cross_chain_swaps WHERE swap_id = ?", (swap_id,))
swap = cursor.fetchone()
conn.close()
if not swap:
raise HTTPException(status_code=404, detail="Swap not found")
return dict(swap)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to get swap: {str(e)}")
@app.get("/api/v1/cross-chain/swaps")
async def get_cross_chain_swaps(user_address: Optional[str] = None, status: Optional[str] = None):
"""Get cross-chain swaps"""
try:
conn = get_db_connection()
cursor = conn.cursor()
query = "SELECT * FROM cross_chain_swaps"
params = []
if user_address:
query += " WHERE user_address = ?"
params.append(user_address)
if status:
if user_address:
query += " AND status = ?"
else:
query += " WHERE status = ?"
params.append(status)
query += " ORDER BY created_at DESC"
cursor.execute(query, params)
swaps = [dict(row) for row in cursor.fetchall()]
conn.close()
return {
"swaps": swaps,
"total_swaps": len(swaps)
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to get swaps: {str(e)}")
@app.post("/api/v1/cross-chain/bridge")
async def create_bridge_transaction(bridge_request: BridgeRequest):
"""Create bridge transaction"""
try:
if bridge_request.source_chain == bridge_request.target_chain:
raise HTTPException(status_code=400, detail="Cannot bridge to same chain")
bridge_id = str(uuid.uuid4())
bridge_fee = bridge_request.amount * 0.001 # 0.1% bridge fee
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute('''
INSERT INTO bridge_transactions
(bridge_id, source_chain, target_chain, token, amount, recipient_address, bridge_fee)
VALUES (?, ?, ?, ?, ?, ?, ?)
''', (bridge_id, bridge_request.source_chain, bridge_request.target_chain,
bridge_request.token, bridge_request.amount, bridge_request.recipient_address, bridge_fee))
conn.commit()
conn.close()
# Process bridge in background
asyncio.create_task(process_bridge_transaction(bridge_id))
return {
"success": True,
"bridge_id": bridge_id,
"source_chain": bridge_request.source_chain,
"target_chain": bridge_request.target_chain,
"token": bridge_request.token,
"amount": bridge_request.amount,
"bridge_fee": bridge_fee,
"status": "pending"
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Bridge creation failed: {str(e)}")
async def process_bridge_transaction(bridge_id: str):
"""Process bridge transaction"""
try:
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("SELECT * FROM bridge_transactions WHERE bridge_id = ?", (bridge_id,))
bridge = cursor.fetchone()
if not bridge:
return
# Update status
cursor.execute("UPDATE bridge_transactions SET status = 'locked' WHERE bridge_id = ?", (bridge_id,))
conn.commit()
# Simulate bridge processing
await asyncio.sleep(2)
# Generate mock transaction hashes
source_tx_hash = f"0x{uuid.uuid4().hex[:64]}"
target_tx_hash = f"0x{uuid.uuid4().hex[:64]}"
# Complete bridge
cursor.execute('''
UPDATE bridge_transactions SET status = 'completed',
source_tx_hash = ?, target_tx_hash = ?, completed_at = CURRENT_TIMESTAMP
WHERE bridge_id = ?
''', (source_tx_hash, target_tx_hash, bridge_id))
conn.commit()
conn.close()
except Exception as e:
print(f"Bridge processing error: {e}")
@app.get("/api/v1/cross-chain/bridge/{bridge_id}")
async def get_bridge_transaction(bridge_id: str):
"""Get bridge transaction details"""
try:
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("SELECT * FROM bridge_transactions WHERE bridge_id = ?", (bridge_id,))
bridge = cursor.fetchone()
conn.close()
if not bridge:
raise HTTPException(status_code=404, detail="Bridge transaction not found")
return dict(bridge)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to get bridge: {str(e)}")
@app.get("/api/v1/cross-chain/rates")
async def get_cross_chain_rates():
"""Get cross-chain exchange rates"""
rates = {}
for from_chain in SUPPORTED_CHAINS:
for to_chain in SUPPORTED_CHAINS:
if from_chain != to_chain:
pair_key = f"{from_chain}-{to_chain}"
rate = get_cross_chain_rate(from_chain, to_chain, "AITBC", "AITBC")
if rate:
rates[pair_key] = rate
return {
"rates": rates,
"timestamp": datetime.now().isoformat()
}
@app.get("/api/v1/cross-chain/pools")
async def get_cross_chain_pools():
"""Get cross-chain liquidity pools"""
try:
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("SELECT * FROM cross_chain_pools ORDER BY total_liquidity DESC")
pools = [dict(row) for row in cursor.fetchall()]
conn.close()
return {
"pools": pools,
"total_pools": len(pools)
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to get pools: {str(e)}")
@app.get("/api/v1/cross-chain/stats")
async def get_cross_chain_stats():
"""Get cross-chain trading statistics"""
try:
conn = get_db_connection()
cursor = conn.cursor()
# Swap stats
cursor.execute('''
SELECT status, COUNT(*) as count, SUM(amount) as volume
FROM cross_chain_swaps
GROUP BY status
''')
swap_stats = [dict(row) for row in cursor.fetchall()]
# Bridge stats
cursor.execute('''
SELECT status, COUNT(*) as count, SUM(amount) as volume
FROM bridge_transactions
GROUP BY status
''')
bridge_stats = [dict(row) for row in cursor.fetchall()]
# Total volume
cursor.execute("SELECT SUM(amount) FROM cross_chain_swaps WHERE status = 'completed'")
total_volume = cursor.fetchone()[0] or 0
conn.close()
return {
"swap_stats": swap_stats,
"bridge_stats": bridge_stats,
"total_volume": total_volume,
"supported_chains": list(SUPPORTED_CHAINS.keys()),
"timestamp": datetime.now().isoformat()
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to get stats: {str(e)}")
if __name__ == "__main__":
# Initialize database
if init_database():
print("✅ Complete cross-chain database initialized")
else:
print("❌ Database initialization failed")
# Run the server
uvicorn.run(app, host="0.0.0.0", port=8001)