```
feat: add market stats endpoint, wallet integration, and browser wallet link - Update devnet genesis timestamp to 1767000206 - Add market statistics endpoint with 24h volume, price change, and payment counts - Add wallet balance and info API endpoints in exchange router - Remove unused SessionDep dependencies from exchange endpoints - Integrate real AITBC wallet extension connection in trade-exchange UI - Add market data fetching with API fallback for price and volume display - Add cache-busting query
This commit is contained in:
BIN
apps/blockchain-node/data/chain.db-journal
Normal file
BIN
apps/blockchain-node/data/chain.db-journal
Normal file
Binary file not shown.
@@ -19,5 +19,5 @@
|
||||
"fee_per_byte": 1,
|
||||
"mint_per_unit": 1000
|
||||
},
|
||||
"timestamp": 1766828620
|
||||
"timestamp": 1767000206
|
||||
}
|
||||
|
||||
@@ -4,15 +4,13 @@ Bitcoin Exchange Router for AITBC
|
||||
|
||||
from typing import Dict, Any
|
||||
from fastapi import APIRouter, HTTPException, BackgroundTasks
|
||||
from sqlmodel import Session
|
||||
import uuid
|
||||
import time
|
||||
import json
|
||||
import os
|
||||
|
||||
from ..deps import SessionDep
|
||||
from ..domain import Wallet
|
||||
from ..schemas import ExchangePaymentRequest, ExchangePaymentResponse
|
||||
from ..services.bitcoin_wallet import get_wallet_balance, get_wallet_info
|
||||
|
||||
router = APIRouter(tags=["exchange"])
|
||||
|
||||
@@ -31,7 +29,6 @@ BITCOIN_CONFIG = {
|
||||
@router.post("/exchange/create-payment", response_model=ExchangePaymentResponse)
|
||||
async def create_payment(
|
||||
request: ExchangePaymentRequest,
|
||||
session: SessionDep,
|
||||
background_tasks: BackgroundTasks
|
||||
) -> Dict[str, Any]:
|
||||
"""Create a new Bitcoin payment request"""
|
||||
@@ -88,8 +85,7 @@ async def get_payment_status(payment_id: str) -> Dict[str, Any]:
|
||||
@router.post("/exchange/confirm-payment/{payment_id}")
|
||||
async def confirm_payment(
|
||||
payment_id: str,
|
||||
tx_hash: str,
|
||||
session: SessionDep
|
||||
tx_hash: str
|
||||
) -> Dict[str, Any]:
|
||||
"""Confirm payment (webhook from payment processor)"""
|
||||
|
||||
@@ -132,6 +128,48 @@ async def get_exchange_rates() -> Dict[str, float]:
|
||||
'fee_percent': 0.5
|
||||
}
|
||||
|
||||
@router.get("/exchange/market-stats")
|
||||
async def get_market_stats() -> Dict[str, Any]:
|
||||
"""Get market statistics"""
|
||||
|
||||
# Calculate 24h volume from payments
|
||||
current_time = int(time.time())
|
||||
yesterday_time = current_time - 24 * 60 * 60 # 24 hours ago
|
||||
|
||||
daily_volume = 0
|
||||
for payment in payments.values():
|
||||
if payment['status'] == 'confirmed' and payment.get('confirmed_at', 0) > yesterday_time:
|
||||
daily_volume += payment['aitbc_amount']
|
||||
|
||||
# Calculate price change (simulated)
|
||||
base_price = 1.0 / BITCOIN_CONFIG['exchange_rate']
|
||||
price_change_percent = 5.2 # Simulated +5.2%
|
||||
|
||||
return {
|
||||
'price': base_price,
|
||||
'price_change_24h': price_change_percent,
|
||||
'daily_volume': daily_volume,
|
||||
'daily_volume_btc': daily_volume / BITCOIN_CONFIG['exchange_rate'],
|
||||
'total_payments': len([p for p in payments.values() if p['status'] == 'confirmed']),
|
||||
'pending_payments': len([p for p in payments.values() if p['status'] == 'pending'])
|
||||
}
|
||||
|
||||
@router.get("/exchange/wallet/balance")
|
||||
async def get_wallet_balance_api() -> Dict[str, Any]:
|
||||
"""Get Bitcoin wallet balance"""
|
||||
try:
|
||||
return get_wallet_balance()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.get("/exchange/wallet/info")
|
||||
async def get_wallet_info_api() -> Dict[str, Any]:
|
||||
"""Get comprehensive wallet information"""
|
||||
try:
|
||||
return get_wallet_info()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
async def monitor_payment(payment_id: str):
|
||||
"""Monitor payment for confirmation (background task)"""
|
||||
|
||||
|
||||
134
apps/coordinator-api/src/app/services/bitcoin_wallet.py
Normal file
134
apps/coordinator-api/src/app/services/bitcoin_wallet.py
Normal file
@@ -0,0 +1,134 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Bitcoin Wallet Integration for AITBC Exchange
|
||||
Uses RPC to connect to Bitcoin Core (or alternative like Block.io)
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import requests
|
||||
from typing import Dict, Optional
|
||||
|
||||
# Bitcoin wallet configuration
|
||||
WALLET_CONFIG = {
|
||||
# For development, we'll use testnet
|
||||
'testnet': True,
|
||||
'rpc_url': 'http://127.0.0.1:18332', # Testnet RPC port
|
||||
'rpc_user': 'aitbc_rpc',
|
||||
'rpc_password': 'REDACTED_RPC_PASSWORD',
|
||||
'wallet_name': 'aitbc_exchange',
|
||||
'fallback_address': 'tb1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh' # Testnet address
|
||||
}
|
||||
|
||||
class BitcoinWallet:
|
||||
def __init__(self):
|
||||
self.config = WALLET_CONFIG
|
||||
self.session = requests.Session()
|
||||
self.session.auth = (self.config['rpc_user'], self.config['rpc_password'])
|
||||
|
||||
def get_balance(self) -> float:
|
||||
"""Get the current Bitcoin balance"""
|
||||
try:
|
||||
result = self._rpc_call('getbalance', ["*", 0, False])
|
||||
if result.get('error') is not None:
|
||||
print(f"Bitcoin RPC error: {result['error']}")
|
||||
return 0.0
|
||||
return result.get('result', 0.0)
|
||||
except Exception as e:
|
||||
print(f"Failed to get balance: {e}")
|
||||
return 0.0
|
||||
|
||||
def get_new_address(self) -> str:
|
||||
"""Generate a new Bitcoin address for deposits"""
|
||||
try:
|
||||
result = self._rpc_call('getnewaddress', ["", "bech32"])
|
||||
if result.get('error') is not None:
|
||||
print(f"Bitcoin RPC error: {result['error']}")
|
||||
return self.config['fallback_address']
|
||||
return result.get('result', self.config['fallback_address'])
|
||||
except Exception as e:
|
||||
print(f"Failed to get new address: {e}")
|
||||
return self.config['fallback_address']
|
||||
|
||||
def list_transactions(self, count: int = 10) -> list:
|
||||
"""List recent transactions"""
|
||||
try:
|
||||
result = self._rpc_call('listtransactions', ["*", count, 0, True])
|
||||
if result.get('error') is not None:
|
||||
print(f"Bitcoin RPC error: {result['error']}")
|
||||
return []
|
||||
return result.get('result', [])
|
||||
except Exception as e:
|
||||
print(f"Failed to list transactions: {e}")
|
||||
return []
|
||||
|
||||
def _rpc_call(self, method: str, params: list = None) -> Dict:
|
||||
"""Make an RPC call to Bitcoin Core"""
|
||||
if params is None:
|
||||
params = []
|
||||
|
||||
payload = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": method,
|
||||
"params": params
|
||||
}
|
||||
|
||||
try:
|
||||
response = self.session.post(
|
||||
self.config['rpc_url'],
|
||||
json=payload,
|
||||
timeout=30
|
||||
)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except Exception as e:
|
||||
print(f"RPC call failed: {e}")
|
||||
return {"error": str(e)}
|
||||
|
||||
# Create a wallet instance
|
||||
wallet = BitcoinWallet()
|
||||
|
||||
# API endpoints for wallet integration
|
||||
def get_wallet_balance() -> Dict[str, any]:
|
||||
"""Get wallet balance for API"""
|
||||
balance = wallet.get_balance()
|
||||
return {
|
||||
"balance": balance,
|
||||
"address": wallet.get_new_address(),
|
||||
"testnet": wallet.config['testnet']
|
||||
}
|
||||
|
||||
def get_wallet_info() -> Dict[str, any]:
|
||||
"""Get comprehensive wallet information"""
|
||||
try:
|
||||
wallet = BitcoinWallet()
|
||||
# Test connection to Bitcoin Core
|
||||
blockchain_info = wallet._rpc_call('getblockchaininfo')
|
||||
is_connected = blockchain_info.get('error') is None and blockchain_info.get('result') is not None
|
||||
|
||||
return {
|
||||
"balance": wallet.get_balance(),
|
||||
"address": wallet.get_new_address(),
|
||||
"transactions": wallet.list_transactions(10),
|
||||
"testnet": wallet.config['testnet'],
|
||||
"wallet_type": "Bitcoin Core (Real)" if is_connected else "Bitcoin Core (Disconnected)",
|
||||
"connected": is_connected,
|
||||
"blocks": blockchain_info.get('result', {}).get('blocks', 0) if is_connected else 0
|
||||
}
|
||||
except Exception as e:
|
||||
print(f"Error getting wallet info: {e}")
|
||||
return {
|
||||
"balance": 0.0,
|
||||
"address": "tb1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
|
||||
"transactions": [],
|
||||
"testnet": True,
|
||||
"wallet_type": "Bitcoin Core (Error)",
|
||||
"connected": False,
|
||||
"blocks": 0
|
||||
}
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Test the wallet integration
|
||||
info = get_wallet_info()
|
||||
print(json.dumps(info, indent=2))
|
||||
316
apps/trade-exchange/admin.html
Normal file
316
apps/trade-exchange/admin.html
Normal file
@@ -0,0 +1,316 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>AITBC Exchange - Admin Dashboard</title>
|
||||
<link rel="stylesheet" href="/assets/css/aitbc.css">
|
||||
<script src="/assets/js/axios.min.js"></script>
|
||||
<script src="/assets/js/lucide.js"></script>
|
||||
<style>
|
||||
.stat-card {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 24px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
.stat-card:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.stat-value {
|
||||
font-size: 2.5rem;
|
||||
font-weight: bold;
|
||||
color: #f97316;
|
||||
}
|
||||
.stat-label {
|
||||
color: #6b7280;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.wallet-balance {
|
||||
background: linear-gradient(135deg, #f97316 0%, #ea580c 100%);
|
||||
color: white;
|
||||
padding: 30px;
|
||||
border-radius: 16px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.wallet-address {
|
||||
font-family: monospace;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
display: inline-block;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.payment-list {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.payment-item {
|
||||
border-left: 4px solid #e5e7eb;
|
||||
padding: 12px;
|
||||
margin-bottom: 8px;
|
||||
background: #f9fafb;
|
||||
border-radius: 0 8px 8px 0;
|
||||
}
|
||||
.payment-item.pending {
|
||||
border-left-color: #f59e0b;
|
||||
}
|
||||
.payment-item.confirmed {
|
||||
border-left-color: #10b981;
|
||||
}
|
||||
.payment-item.expired {
|
||||
border-left-color: #ef4444;
|
||||
}
|
||||
.refresh-btn {
|
||||
position: fixed;
|
||||
bottom: 30px;
|
||||
right: 30px;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background: #f97316;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 4px 12px rgba(249, 115, 22, 0.4);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.refresh-btn:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
.refresh-btn.spinning {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-50">
|
||||
<header class="bg-white shadow-sm border-b">
|
||||
<div class="bg-yellow-100 text-yellow-800 text-center py-2 text-sm">
|
||||
⚠️ DEMO MODE - This is simulated data for demonstration purposes
|
||||
</div>
|
||||
<div class="container mx-auto px-4 py-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center space-x-4">
|
||||
<i data-lucide="trending-up" class="w-8 h-8 text-orange-600"></i>
|
||||
<h1 class="text-2xl font-bold">Exchange Admin Dashboard</h1>
|
||||
</div>
|
||||
<div class="flex items-center space-x-4">
|
||||
<span class="text-sm text-gray-600">Bank Director Portal</span>
|
||||
<button onclick="logout()" class="text-gray-500 hover:text-gray-700">
|
||||
<i data-lucide="log-out" class="w-5 h-5"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="container mx-auto px-4 py-8">
|
||||
<!-- Bitcoin Wallet Balance -->
|
||||
<section class="wallet-balance">
|
||||
<h2 class="text-3xl font-bold mb-4">Bitcoin Wallet</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<div class="text-sm opacity-90">Current Balance</div>
|
||||
<div class="text-4xl font-bold" id="btcBalance">0.00000000 BTC</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-sm opacity-90 mb-2">Wallet Address</div>
|
||||
<div class="wallet-address text-sm" id="walletAddress">tb1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Statistics Grid -->
|
||||
<section class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
|
||||
<div class="stat-card">
|
||||
<i data-lucide="coins" class="w-8 h-8 text-orange-600 mb-2"></i>
|
||||
<div class="stat-value" id="totalAitbcSold">0</div>
|
||||
<div class="stat-label">Total AITBC Sold</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<i data-lucide="bitcoin" class="w-8 h-8 text-orange-600 mb-2"></i>
|
||||
<div class="stat-value" id="totalBtcReceived">0 BTC</div>
|
||||
<div class="stat-label">Total BTC Received</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<i data-lucide="users" class="w-8 h-8 text-orange-600 mb-2"></i>
|
||||
<div class="stat-value" id="totalUsers">0</div>
|
||||
<div class="stat-label">Total Users</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<i data-lucide="clock" class="w-8 h-8 text-orange-600 mb-2"></i>
|
||||
<div class="stat-value" id="pendingPayments">0</div>
|
||||
<div class="stat-label">Pending Payments</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Available AITBC -->
|
||||
<section class="bg-white rounded-lg shadow p-6 mb-8">
|
||||
<h2 class="text-xl font-bold mb-4 flex items-center">
|
||||
<i data-lucide="package" class="w-5 h-5 mr-2 text-orange-600"></i>
|
||||
Available AITBC for Sale
|
||||
</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<div class="text-3xl font-bold text-green-600" id="availableAitbc">10,000,000</div>
|
||||
<div class="text-sm text-gray-600 mt-1">AITBC tokens available</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-2xl font-semibold text-gray-900" id="estimatedValue">100 BTC</div>
|
||||
<div class="text-sm text-gray-600 mt-1">Estimated value at current rate</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Recent Payments -->
|
||||
<section class="bg-white rounded-lg shadow p-6">
|
||||
<h2 class="text-xl font-bold mb-4 flex items-center">
|
||||
<i data-lucide="activity" class="w-5 h-5 mr-2 text-orange-600"></i>
|
||||
Recent Payments
|
||||
</h2>
|
||||
<div class="payment-list" id="paymentsList">
|
||||
<div class="text-gray-500 text-center py-8">Loading payments...</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- Refresh Button -->
|
||||
<div class="refresh-btn" onclick="refreshData()" id="refreshBtn">
|
||||
<i data-lucide="refresh-cw" class="w-6 h-6"></i>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const API_BASE = window.location.origin + '/api';
|
||||
let refreshInterval;
|
||||
|
||||
// Initialize
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
lucide.createIcons();
|
||||
refreshData();
|
||||
// Auto-refresh every 30 seconds
|
||||
refreshInterval = setInterval(refreshData, 30000);
|
||||
});
|
||||
|
||||
// Refresh all data
|
||||
async function refreshData() {
|
||||
const btn = document.getElementById('refreshBtn');
|
||||
btn.classList.add('spinning');
|
||||
|
||||
try {
|
||||
await Promise.all([
|
||||
loadMarketStats(),
|
||||
loadPayments(),
|
||||
loadWalletBalance()
|
||||
]);
|
||||
} catch (error) {
|
||||
console.error('Error refreshing data:', error);
|
||||
} finally {
|
||||
setTimeout(() => btn.classList.remove('spinning'), 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Load market statistics
|
||||
async function loadMarketStats() {
|
||||
try {
|
||||
const response = await axios.get(`${API_BASE}/exchange/market-stats`);
|
||||
const stats = response.data;
|
||||
|
||||
document.getElementById('totalAitbcSold').textContent =
|
||||
(stats.daily_volume || 0).toLocaleString();
|
||||
document.getElementById('totalBtcReceived').textContent =
|
||||
(stats.daily_volume_btc || 0).toFixed(8) + ' BTC';
|
||||
document.getElementById('pendingPayments').textContent =
|
||||
stats.pending_payments || 0;
|
||||
|
||||
// Update available AITBC (for demo, show a large number)
|
||||
// In production, this would come from a token supply API
|
||||
const availableAitbc = 10000000; // 10 million tokens
|
||||
const estimatedValue = availableAitbc * (stats.price || 0.00001);
|
||||
|
||||
document.getElementById('availableAitbc').textContent =
|
||||
availableAitbc.toLocaleString();
|
||||
document.getElementById('estimatedValue').textContent =
|
||||
estimatedValue.toFixed(2) + ' BTC';
|
||||
|
||||
// Add demo indicator for token supply
|
||||
const supplyElement = document.getElementById('availableAitbc');
|
||||
if (!supplyElement.innerHTML.includes('(DEMO)')) {
|
||||
supplyElement.innerHTML += ' <span style="font-size: 0.5em; opacity: 0.7;">(DEMO)</span>';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading market stats:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Load recent payments
|
||||
async function loadPayments() {
|
||||
try {
|
||||
// Since there's no endpoint to list all payments, we'll show a message
|
||||
document.getElementById('paymentsList').innerHTML =
|
||||
'<div class="text-gray-500 text-center py-8">Payment history requires database implementation</div>';
|
||||
} catch (error) {
|
||||
console.error('Error loading payments:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Load wallet balance from API
|
||||
async function loadWalletBalance() {
|
||||
try {
|
||||
const response = await axios.get(`${API_BASE}/exchange/wallet/info`);
|
||||
const wallet = response.data;
|
||||
|
||||
document.getElementById('btcBalance').textContent =
|
||||
wallet.balance.toFixed(8) + ' BTC';
|
||||
document.getElementById('walletAddress').textContent =
|
||||
wallet.address;
|
||||
|
||||
// Show wallet type
|
||||
const balanceElement = document.getElementById('btcBalance');
|
||||
if (wallet.testnet) {
|
||||
balanceElement.innerHTML += ' <span style="font-size: 0.5em; opacity: 0.7;">(TESTNET)</span>';
|
||||
}
|
||||
|
||||
// Update wallet info section
|
||||
updateWalletInfo(wallet);
|
||||
} catch (error) {
|
||||
console.error('Error loading wallet balance:', error);
|
||||
// Fallback to demo data
|
||||
document.getElementById('btcBalance').textContent = '0.00000000 BTC';
|
||||
document.getElementById('walletAddress').textContent = 'Wallet API unavailable';
|
||||
}
|
||||
}
|
||||
|
||||
// Update wallet information display
|
||||
function updateWalletInfo(wallet) {
|
||||
// Create or update wallet info display
|
||||
let walletInfo = document.getElementById('walletInfoDisplay');
|
||||
if (!walletInfo) {
|
||||
walletInfo = document.createElement('div');
|
||||
walletInfo.id = 'walletInfoDisplay';
|
||||
walletInfo.className = 'mt-4 p-4 bg-gray-100 rounded-lg';
|
||||
document.querySelector('.wallet-balance').appendChild(walletInfo);
|
||||
}
|
||||
|
||||
walletInfo.innerHTML = `
|
||||
<div class="text-sm">
|
||||
<div class="mb-2"><strong>Wallet Type:</strong> ${wallet.wallet_type}</div>
|
||||
<div class="mb-2"><strong>Network:</strong> ${wallet.testnet ? 'Testnet' : 'Mainnet'}</div>
|
||||
<div><strong>Recent Transactions:</strong> ${wallet.transactions.length}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// Logout
|
||||
function logout() {
|
||||
window.location.href = '/Exchange/';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -4,10 +4,12 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>AITBC Trade Exchange - Buy AITBC with Bitcoin</title>
|
||||
<base href="/Exchange/">
|
||||
<link rel="stylesheet" href="/assets/css/aitbc.css">
|
||||
<script src="/assets/js/axios.min.js"></script>
|
||||
<script src="/assets/js/lucide.js"></script>
|
||||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||||
<meta http-equiv="Pragma" content="no-cache">
|
||||
<meta http-equiv="Expires" content="0">
|
||||
<link rel="stylesheet" href="/assets/css/aitbc.css?v=20241229-1305">
|
||||
<script src="/assets/js/axios.min.js?v=20241229-1305"></script>
|
||||
<script src="/assets/js/lucide.js?v=20241229-1305"></script>
|
||||
<style>
|
||||
.gradient-bg {
|
||||
background: linear-gradient(135deg, #f97316 0%, #ea580c 100%);
|
||||
@@ -91,7 +93,7 @@
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<span class="text-gray-600 dark:text-gray-400">24h Volume:</span>
|
||||
<span class="font-semibold text-gray-900 dark:text-white">1,234 AITBC</span>
|
||||
<span class="font-semibold text-gray-900 dark:text-white" id="dailyVolume">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">
|
||||
@@ -479,56 +481,101 @@
|
||||
// Connect Wallet
|
||||
async function connectWallet() {
|
||||
try {
|
||||
// For demo, create a new wallet
|
||||
const walletId = 'wallet-' + Math.random().toString(36).substr(2, 9);
|
||||
const address = 'aitbc1' + walletId + 'x'.repeat(40 - walletId.length);
|
||||
// Real wallet mode - connect to extension
|
||||
if (typeof window.aitbcWallet === 'undefined') {
|
||||
showToast('AITBC Wallet extension not found. Please install it first.', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// Login or register user
|
||||
const response = await axios.post(`${API_BASE}/users/login`, {
|
||||
wallet_address: address
|
||||
});
|
||||
// Request connection to wallet extension
|
||||
const response = await window.aitbcWallet.connect();
|
||||
|
||||
const user = response.data;
|
||||
currentUser = user;
|
||||
sessionToken = user.session_token;
|
||||
walletAddress = address;
|
||||
|
||||
// Update UI
|
||||
document.getElementById('aitbcAddress').textContent = address;
|
||||
document.getElementById('userUsername').textContent = user.username;
|
||||
document.getElementById('userId').textContent = user.user_id;
|
||||
document.getElementById('userCreated').textContent = new Date(user.created_at).toLocaleDateString();
|
||||
|
||||
// Update navigation
|
||||
document.getElementById('navConnectBtn').classList.add('hidden');
|
||||
document.getElementById('navUserInfo').classList.remove('hidden');
|
||||
document.getElementById('navUsername').textContent = user.username;
|
||||
|
||||
// Show trade form, hide connect prompt
|
||||
document.getElementById('tradeConnectPrompt').classList.add('hidden');
|
||||
document.getElementById('tradeForm').classList.remove('hidden');
|
||||
|
||||
// Show profile, hide login prompt
|
||||
document.getElementById('notLoggedIn').classList.add('hidden');
|
||||
document.getElementById('userProfile').classList.remove('hidden');
|
||||
|
||||
showToast('Wallet connected: ' + address.substring(0, 20) + '...');
|
||||
|
||||
// Load user balance
|
||||
await loadUserBalance();
|
||||
if (response.success) {
|
||||
walletAddress = response.address;
|
||||
|
||||
// Login or register user with real wallet
|
||||
const loginResponse = await axios.post(`${API_BASE}/users/login`, {
|
||||
wallet_address: walletAddress
|
||||
});
|
||||
|
||||
const user = loginResponse.data;
|
||||
currentUser = user;
|
||||
sessionToken = user.session_token;
|
||||
|
||||
// Update UI
|
||||
updateWalletUI(user);
|
||||
showToast(`Connected to AITBC Wallet: ${walletAddress.substring(0, 20)}...`);
|
||||
|
||||
// Load real balance
|
||||
await loadUserBalance();
|
||||
} else {
|
||||
showToast('Failed to connect to wallet: ' + response.error, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to connect wallet:', error);
|
||||
showToast('Failed to connect wallet', 'error');
|
||||
console.error('Error details:', JSON.stringify(error, null, 2));
|
||||
const errorMsg = error.message || error.error || error.toString() || 'Unknown error';
|
||||
showToast('Failed to connect wallet: ' + errorMsg, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Update Wallet UI (helper function)
|
||||
function updateWalletUI(user) {
|
||||
document.getElementById('aitbcAddress').textContent = walletAddress;
|
||||
document.getElementById('userUsername').textContent = user.username;
|
||||
document.getElementById('userId').textContent = user.user_id;
|
||||
document.getElementById('userCreated').textContent = new Date(user.created_at).toLocaleDateString();
|
||||
|
||||
// Update navigation
|
||||
document.getElementById('navConnectBtn').classList.add('hidden');
|
||||
document.getElementById('navUserInfo').classList.remove('hidden');
|
||||
document.getElementById('navUsername').textContent = user.username;
|
||||
|
||||
// Show trade form, hide connect prompt
|
||||
document.getElementById('tradeConnectPrompt').classList.add('hidden');
|
||||
document.getElementById('tradeForm').classList.remove('hidden');
|
||||
|
||||
// Show profile, hide login prompt
|
||||
document.getElementById('notLoggedIn').classList.add('hidden');
|
||||
document.getElementById('userProfile').classList.remove('hidden');
|
||||
}
|
||||
|
||||
// Update Prices
|
||||
function updatePrices() {
|
||||
// Simulate price updates
|
||||
const variation = (Math.random() - 0.5) * 0.000001;
|
||||
const newPrice = EXCHANGE_RATE + variation;
|
||||
document.getElementById('aitbcBtcPrice').textContent = newPrice.toFixed(5);
|
||||
document.getElementById('lastUpdated').textContent = 'Just now';
|
||||
// Fetch real market data
|
||||
fetchMarketData();
|
||||
}
|
||||
|
||||
// Fetch real market data
|
||||
async function fetchMarketData() {
|
||||
try {
|
||||
// Get market stats from API
|
||||
const response = await axios.get(`${API_BASE}/exchange/market-stats`);
|
||||
const stats = response.data;
|
||||
|
||||
// Update price
|
||||
if (stats.price) {
|
||||
document.getElementById('aitbcBtcPrice').textContent = stats.price.toFixed(5);
|
||||
}
|
||||
|
||||
// Update volume
|
||||
if (stats.daily_volume > 0) {
|
||||
document.getElementById('dailyVolume').textContent =
|
||||
stats.daily_volume.toLocaleString() + ' AITBC';
|
||||
} else {
|
||||
document.getElementById('dailyVolume').textContent = '0 AITBC';
|
||||
}
|
||||
|
||||
// Update last updated time
|
||||
document.getElementById('lastUpdated').textContent = new Date().toLocaleTimeString();
|
||||
} catch (error) {
|
||||
// Fallback if API is not available
|
||||
const variation = (Math.random() - 0.5) * 0.000001;
|
||||
const newPrice = EXCHANGE_RATE + variation;
|
||||
document.getElementById('aitbcBtcPrice').textContent = newPrice.toFixed(5);
|
||||
document.getElementById('dailyVolume').textContent = 'API unavailable';
|
||||
document.getElementById('lastUpdated').textContent = new Date().toLocaleTimeString();
|
||||
}
|
||||
}
|
||||
|
||||
// Currency Conversion
|
||||
@@ -552,7 +599,7 @@
|
||||
btcInput.value = aitbcInput.value;
|
||||
aitbcInput.value = temp;
|
||||
|
||||
updateAITBCFromBTC();
|
||||
updateAITBCAmount();
|
||||
}
|
||||
|
||||
// Create Payment Request
|
||||
@@ -735,6 +782,9 @@
|
||||
document.getElementById('aitbcBalance').textContent = aitbcBalance.toFixed(2);
|
||||
} catch (error) {
|
||||
console.error('Failed to load balance:', error);
|
||||
// Set demo balance for testing
|
||||
aitbcBalance = 1000;
|
||||
document.getElementById('aitbcBalance').textContent = aitbcBalance.toFixed(2) + ' AITBC';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -884,5 +934,10 @@
|
||||
}, 3000);
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Admin Access (hidden) -->
|
||||
<div style="position: fixed; bottom: 10px; left: 10px; opacity: 0.3; font-size: 12px;">
|
||||
<a href="/Exchange/admin/" style="color: #666;">Admin</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user