<?php declare(strict_types=1); if (!defined('DASHBOARD_ROOT')) { http_response_code(403); exit('Direct access denied.'); } class WalletMonitor { private static array $walletStatusMap = [ 4020 => 'active', 4021 => 'cold', ]; private static array $txStatusMap = [ 4010 => 'pending', 4011 => 'confirmed', 4012 => 'failed', 4013 => 'orphan', ]; private static function walletStatusEnum(int $code): string { return self::$walletStatusMap[$code] ?? 'watch'; } private static function txStatusEnum(int $code): string { return self::$txStatusMap[$code] ?? 'pending'; } public static function getWallets(): array { try { return Database::fetchAll( "SELECT id, currency_code, address, label, balance, last_checked,                         status, status_code                  FROM wallets ORDER BY currency_code, label" ); } catch (\Throwable $e) { return []; } } public static function getTransactions(int $walletId, int $limit = 50): array { try { return Database::fetchAll( "SELECT id, wallet_id, tx_hash, amount, confirmations,                         status, status_code, detected_at                  FROM wallet_transactions WHERE wallet_id = :wid                  ORDER BY detected_at DESC LIMIT :lim", [':wid' => $walletId, ':lim' => $limit] ); } catch (\Throwable $e) { return []; } } public static function verifyPayment(string $txHash, float $expectedAmount, int $currencyCode): array { $tx = Database::fetchOne( "SELECT id, amount, confirmations, status, status_code              FROM wallet_transactions WHERE tx_hash = :hash", [':hash' => $txHash] ); if (!$tx) { return ['verified' => false, 'reason' => 'Transaction not found.']; } if ((float) $tx['amount'] < $expectedAmount) { return ['verified' => false, 'reason' => 'Amount insufficient.', 'received' => $tx['amount']]; } if ((int) $tx['confirmations'] < self::getMinConfirmations($currencyCode)) { return ['verified' => false, 'reason' => 'Insufficient confirmations.', 'confirmations' => $tx['confirmations']]; } return ['verified' => true, 'tx_id' => $tx['id'], 'amount' => $tx['amount'], 'confirmations' => $tx['confirmations']]; } public static function updateBalance(int $walletId, float $balance): void { try { Database::execute( "UPDATE wallets SET balance = :bal, last_checked = NOW() WHERE id = :id", [':bal' => $balance, ':id' => $walletId] ); } catch (\Throwable $e) {} } public static function recordTransaction(int $walletId, string $txHash, float $amount, int $confirmations): int { $statusEnum = $confirmations >= 6 ? 'confirmed' : 'pending'; $existing = Database::fetchOne( "SELECT id FROM crypto_transactions WHERE tx_hash = :hash", [':hash' => $txHash] ); if ($existing) { Database::execute( "UPDATE crypto_transactions                  SET confirmations = :conf,                      status = CASE WHEN :conf2 >= 6 THEN 'confirmed' ELSE status END                  WHERE id = :id", [':conf' => $confirmations, ':conf2' => $confirmations, ':id' => $existing['id']] ); return (int) $existing['id']; } return Database::insert( "INSERT INTO crypto_transactions (wallet_id, tx_hash, amount, confirmations, status, detected_at)              VALUES (:wid, :hash, :amt, :conf, :status, NOW())", [ ':wid' => $walletId, ':hash' => $txHash, ':amt' => $amount, ':conf' => $confirmations, ':status' => $statusEnum, ] ); } private static function getMinConfirmations(int $currencyCode): int { return match ($currencyCode) { 4030 => 10, 4031 => 12, 4032 => 12, default => 6, }; } } 