Error: Unable to fetch detailed transaction information.

'; } exit; } // Get view and filter parameters $view = isset($_GET['view']) ? $_GET['view'] : 'largest'; $filter = isset($_GET['filter']) ? $_GET['filter'] : 'all'; // Determine Table Title and Amount Header $currency_filter_desc = ''; if ($filter === 'xrp_only') { $currency_filter_desc = ' XRP Only'; $amount_header = 'Delivered Amount (XRP)'; } elseif ($filter === 'non_xrp_only') { $currency_filter_desc = ' Non-XRP Only'; $amount_header = 'Delivered Amount'; } else { $currency_filter_desc = ''; $amount_header = 'Delivered Amount'; } if ($view == 'recent') { $tableTitle = "Last 100" . $currency_filter_desc . " Payment Transfers"; } else { $tableTitle = "Top 100 Largest" . $currency_filter_desc . " Payment Transfers"; } echo '
'; echo '

XRP Ledger Whale Watch

'; echo '

' . htmlspecialchars($tableTitle) . '

'; echo '
'; // --- Display Buttons --- echo '
'; echo 'View: '; echo 'Top 100 Largest'; echo 'Last 100 Recent'; echo 'Filter: '; echo 'All Currencies'; echo 'XRP Only'; echo 'Non-XRP Only'; echo '
'; // --- Build SQL Query --- // (SQL Query unchanged) $base_sql = "SELECT t.id, t.hash, t.date, l.ledger_index, t.TransactionType AS transaction_type, da.transaction_amount, da.transaction_currency, da.transaction_issuer, da.delivered_amount, da.delivered_currency, da.delivered_issuer, da.recipient_xrp_balance_change FROM transactions t INNER JOIN ledgers l ON t.ledger_id = l.id LEFT JOIN delivered_amounts da ON t.id = da.transaction_id WHERE t.TransactionType = 'Payment' "; $params = []; $types = ''; if ($filter === 'xrp_only') { $base_sql .= " AND da.delivered_currency = ? "; $params[] = 'XRP'; $types .= 's'; } elseif ($filter === 'non_xrp_only') { $base_sql .= " AND da.delivered_currency != ? AND da.delivered_currency IS NOT NULL "; $params[] = 'XRP'; $types .= 's'; } else { $base_sql .= " AND (da.delivered_amount IS NOT NULL OR da.recipient_xrp_balance_change IS NOT NULL OR da.transaction_amount IS NOT NULL)"; } if ($view == 'recent') { $sql = $base_sql . " ORDER BY t.date DESC LIMIT 100"; } else { if ($filter === 'xrp_only') { $sql = $base_sql . " ORDER BY COALESCE(CAST(da.delivered_amount AS DECIMAL(38,0)) / 1000000, da.recipient_xrp_balance_change, 0) DESC, t.date DESC LIMIT 100"; } elseif ($filter === 'non_xrp_only'){ $sql = $base_sql . " ORDER BY t.date DESC LIMIT 100"; } else { $sql = $base_sql . " ORDER BY COALESCE(da.recipient_xrp_balance_change, 0) DESC, t.date DESC LIMIT 100"; } } // --- Execute Query and Display Results --- $result = $mysql->mysql_request_with_prepare($sql, $types, $params); if ($result === false) { echo '

Error executing query: ' . $mysql->get_last_error() . '

'; } else { $transactions = $result; if (!empty($transactions) && !is_array($transactions[array_key_first($transactions)])) { $transactions = [$transactions]; } if (empty($transactions)) { echo '

No matching payment transactions found for the selected filter.

'; } else { echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; // Data Rows foreach ($transactions as $tx) { // (Data extraction unchanged) $tx_amount = $tx['transaction_amount'] ?? null; $tx_currency_raw = $tx['transaction_currency'] ?? null; $tx_issuer = $tx['transaction_issuer'] ?? null; $delivered_amount = $tx['delivered_amount'] ?? null; $delivered_currency_raw = $tx['delivered_currency'] ?? null; $delivered_issuer = $tx['delivered_issuer'] ?? null; $xrp_balance_change = $tx['recipient_xrp_balance_change'] ?? null; // Determine primary display values (unchanged) $display_amount_value = $delivered_amount; $display_currency_raw = $delivered_currency_raw; if ($display_amount_value === null && $tx_amount !== null) { $display_amount_value = $tx_amount; $display_currency_raw = $tx_currency_raw; } $display_currency = decode_currency_code($display_currency_raw ?? 'XRP'); // Decode here for logic // Format amount for display using the MAIN formatter $amount_display = 'N/A'; $sort_value = 0; if ($display_amount_value !== null) { // Use formatter for the value part $formatted_output = format_detail_value('Amount', $display_amount_value, $display_currency_raw ?? 'XRP'); // Manually append the *decoded* currency // $amount_display = $formatted_output['value'] . ' ' . htmlspecialchars($display_currency); $amount_display = $formatted_output['value']; $sort_value = (float)$display_amount_value; // Adjust sort value for XRP 'largest' view if ($display_currency === 'XRP' && $view === 'largest' && $filter === 'xrp_only' && $xrp_balance_change !== null) { $sort_value = (float)$xrp_balance_change; } elseif ($view === 'largest' && ($filter === 'non_xrp_only' || $filter === 'all') && $xrp_balance_change !== null) { $sort_value = (float)$xrp_balance_change; } } elseif ($xrp_balance_change !== null) { // Format balance change using the formatter $formatted_output = format_detail_value('recipient_xrp_balance_change', $xrp_balance_change); // $amount_display = $formatted_output['value'] . ' (Balance Change)'; // Formatter includes ' XRP' $amount_display = $formatted_output['value']; // Formatter includes ' XRP' $sort_value = (float)$xrp_balance_change; } // (Rest of row generation unchanged) $hash = htmlspecialchars($tx['hash']); $ripple_epoch_offset = 946684800; $unix_timestamp = ($tx['date'] ?? 0) + $ripple_epoch_offset; $date_obj = DateTime::createFromFormat('U', $unix_timestamp); $date_display = ($date_obj instanceof DateTime) ? $date_obj->format('Y-m-d H:i:s') . ' UTC' : 'Invalid Date'; $ledger_index = intval($tx['ledger_index'] ?? 0); echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; } echo ''; echo '
'; // JavaScript HEREDOC block (unchanged) echo <<<'JS' JS; } } include 'footer.php'; // *** FUNCTION TO GET DETAILED INFO (Corrected Fee & Amount Display Logic) *** function get_detailed_transaction_info($hash, $mysql) { $sql_base = "SELECT t.id AS transaction_db_id, t.hash, t.date, t.Account, t.TransactionType, t.Fee, t.Sequence, t.SigningPubKey, t.TxnSignature, t.TransactionIndex, t.TransactionResult, t.LastLedgerSequence, t.validated, l.ledger_index, l.ledger_hash, l.close_time_human, l.close_time AS ledger_close_ripple_time, da.transaction_amount, da.transaction_currency, da.transaction_issuer, da.delivered_amount, da.delivered_currency, da.delivered_issuer, da.recipient_xrp_balance_change FROM transactions t INNER JOIN ledgers l ON t.ledger_id = l.id LEFT JOIN delivered_amounts da ON t.id = da.transaction_id WHERE t.hash = ?"; $base_info_result = $mysql->mysql_request_with_prepare($sql_base, 's', [$hash]); if (!$base_info_result || empty($base_info_result)) { return '

Transaction base information not found for hash: ' . htmlspecialchars($hash) . '

'; } $transaction = (isset($base_info_result[0]) && is_array($base_info_result[0])) ? $base_info_result[0] : $base_info_result; $transaction_db_id = $transaction['transaction_db_id']; $transaction_type = $transaction['TransactionType'] ?? 'Unknown'; $details_html = '
'; $details_html .= "

Transaction Details (Hash: " . htmlspecialchars($transaction['hash']) . ")

"; // --- Core Transaction Data --- $details_html .= "

Core Transaction Data

"; $details_html .= ""; // --- Ledger Information --- // (Unchanged) $details_html .= "

Ledger Information

"; $details_html .= ""; // --- Specific Transaction Type Details (e.g., Payment Details from transactions_payment) --- // *** Revised to pass currency code correctly to formatter *** $specific_details_html = ''; if ($transaction_type === 'Payment') { $specific_table_name = 'transactions_payment'; $sql_specific = "SELECT * FROM `" . $specific_table_name . "` WHERE transaction_id = ?"; $specific_result = $mysql->mysql_request_with_prepare($sql_specific, 'i', [$transaction_db_id]); $tx_specific_data = null; if ($specific_result && !empty($specific_result)) { $tx_specific_data = (isset($specific_result[0]) && is_array($specific_result[0])) ? $specific_result[0] : $specific_result; } if ($tx_specific_data) { $specific_details_html .= "

Payment Transaction Fields

"; } $details_html .= $specific_details_html; } // *** Amount Details Section (using corrected formatter + manual append) *** $details_html .= "

Amount Details (from delivered_amounts table)

"; $details_html .= ""; // --- Memos --- // (Unchanged) $memos_html = ''; $sql_memos = "SELECT memo_index, memo_type, memo_data, memo_format FROM transaction_memos WHERE transaction_id = ? ORDER BY memo_index"; $memos = $mysql->mysql_request_with_prepare($sql_memos, 'i', [$transaction_db_id]); if ($memos && !empty($memos)) { if (!is_array($memos[array_key_first($memos)])) { $memos = [$memos]; } $memos_html .= "

Memos

"; $details_html .= $memos_html; } // --- Signers --- // (Unchanged) $signers_html = ''; $sql_signers = "SELECT account, signing_pub_key, txn_signature FROM transaction_signers WHERE transaction_id = ?"; $signers = $mysql->mysql_request_with_prepare($sql_signers, 'i', [$transaction_db_id]); if ($signers && !empty($signers)) { if (!is_array($signers[array_key_first($signers)])) { $signers = [$signers]; } $signers_html .= "

Signers (Multi-Sign)

"; $details_html .= $signers_html; } // --- Affected Nodes --- // (Logic to call get_specific_node_details unchanged) $affected_nodes_html = ''; $sql_affected_nodes = "SELECT base_id, node_index, action, LedgerIndex, LedgerEntryType FROM affected_nodes_base WHERE transaction_id = ? ORDER BY node_index"; $affected_nodes_base = $mysql->mysql_request_with_prepare($sql_affected_nodes, 'i', [$transaction_db_id]); if ($affected_nodes_base && !empty($affected_nodes_base)) { if (!is_array($affected_nodes_base[array_key_first($affected_nodes_base)])) { $affected_nodes_base = [$affected_nodes_base]; } $affected_nodes_html .= "

Affected Ledger Nodes

"; $affected_nodes_html .= "

Nodes (ledger entries) created, modified, or deleted by this transaction.

"; $affected_nodes_html .= ""; $details_html .= $affected_nodes_html; } else { $details_html .= "

No affected node information found in the database for this transaction.

"; } $details_html .= '
'; // Close detail-content div return $details_html; } // Fetches and renders LIs for specific node details (Revised Currency/Value Calls) function get_specific_node_details($mysql, $base_id, $ledgerEntryType) { if (!is_string($ledgerEntryType) || empty($ledgerEntryType)) { return "
  • Invalid Ledger Entry Type provided.
  • "; } $html_items = ''; $table_name = 'affected_nodes_' . strtolower($ledgerEntryType); $valid_node_tables = [ /* ... list unchanged ... */ 'affected_nodes_accountroot', 'affected_nodes_amm', 'affected_nodes_amendments', 'affected_nodes_check', 'affected_nodes_depositpreauth', 'affected_nodes_directorynode', 'affected_nodes_escrow', 'affected_nodes_feesettings', 'affected_nodes_ledgerhashes', 'affected_nodes_negativeunl', 'affected_nodes_nftokenoffer', 'affected_nodes_nftokenpage', 'affected_nodes_offer', 'affected_nodes_oracle', 'affected_nodes_paychannel', 'affected_nodes_ripplestate', 'affected_nodes_signerlist', 'affected_nodes_ticket', 'affected_nodes_did' ]; $sql_check_table_node = "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?"; $node_table_exists_result = $mysql->mysql_request_with_prepare($sql_check_table_node, 's', [$table_name]); if (empty($node_table_exists_result) || !in_array($table_name, $valid_node_tables)) { return "
  • Details table ('" . htmlspecialchars($table_name) . "') not found or schema check failed.
  • "; } $sql = "SELECT * FROM `" . $table_name . "` WHERE base_id = ?"; $node_details = $mysql->mysql_request_with_prepare($sql, 'i', [$base_id]); if ($node_details && !empty($node_details)) { $details = (isset($node_details[0]) && is_array($node_details[0])) ? $node_details[0] : $node_details; $sql_ledger_flags = "SELECT flag_value, flag_name, context, description, is_deprecated FROM xrpl_flags WHERE context = ?"; $known_ledger_flags = $mysql->mysql_request_with_prepare($sql_ledger_flags, 's', [$ledgerEntryType]); $known_ledger_flags_map = []; if($known_ledger_flags){ if (!empty($known_ledger_flags) && !is_array($known_ledger_flags[0])) { $known_ledger_flags = [$known_ledger_flags]; } foreach($known_ledger_flags as $kf) { $known_ledger_flags_map[(int)$kf['flag_value']] = $kf; } } foreach ($details as $key => $value) { if ($key === 'id' || $key === 'base_id' || $value === null || $value === '') { continue; } $formatted_key = htmlspecialchars(preg_replace('/(? $flag) { if (($flags_value_ledger & $flag_val) === $flag_val) { $flag_desc = htmlspecialchars($flag['description']) . (isset($flag['is_deprecated']) && $flag['is_deprecated'] ? ' (Deprecated)' : ''); $html_items .= generate_li(' - Flag Active', htmlspecialchars($flag['flag_name']), $flag_desc, false, true); $explained_ledger_bits |= $flag_val; } } $unknown_ledger_bits = $flags_value_ledger & (~$explained_ledger_bits); if ($unknown_ledger_bits > 0) { $unknown_formatted = format_detail_value('UnknownFlags', $unknown_ledger_bits); $html_items .= generate_li(' - Other/Unknown Ledger Flags Set', $unknown_formatted['value'], 'Flag bits set but not defined in local xrpl_flags table for ' . htmlspecialchars($ledgerEntryType) . ' context', false, $unknown_formatted['is_html']); } } elseif (strpos($key, '_Currency') !== false) { // *** Format currency codes WITHOUT passing currency code to formatter *** $formatted_val = format_detail_value($key, $value); // No 3rd arg $description = get_field_description($key); if (empty($description)) { $description = get_column_comment($mysql, $table_name, $key); } // Append decoded only if different and not XRP $decoded_currency = decode_currency_code($value); $display_val = $formatted_val['value']; if ($decoded_currency !== $value && $decoded_currency !== 'XRP'){ $display_val .= ' (' . htmlspecialchars($decoded_currency) . ')'; } $html_items .= generate_li($formatted_key, $display_val, $description, false, $formatted_val['is_html']); } else { // Handle other fields, pass currency code if value field $currency_code_for_value = null; if (strpos($key, '_Value') !== false || $key === 'Balance') { $currency_key = str_replace('_Value', '_Currency', $key); if (isset($details[$currency_key])) { $currency_code_for_value = $details[$currency_key]; } elseif ($key === 'Balance') { $currency_code_for_value = 'XRP'; } } $formatted_val = format_detail_value($key, $value, $currency_code_for_value); // Pass currency if value $description = get_field_description($key); if (empty($description)) { $description = get_column_comment($mysql, $table_name, $key); } // *** Manually append decoded currency if needed *** $display_val = $formatted_val['value']; if ($currency_code_for_value && $key !== 'Balance' && strpos($key, 'drops') === false && $key !== 'Fee' && strpos($key, 'recipient_xrp_balance_change') === false){ // Don't append if formatter added ' XRP' $decoded_curr = decode_currency_code($currency_code_for_value); if ($decoded_curr !== 'XRP') { $display_val .= ' ' . htmlspecialchars($decoded_curr); } // XRP suffix is now added by formatter for Balance/Fee/Drops/XRP keys } elseif ($key === 'Balance') { $display_val .= ' XRP'; // Add XRP for balance if not already added } $html_items .= generate_li($formatted_key, $display_val, $description, false, $formatted_val['is_html']); } } if (empty(trim($html_items))) { $html_items = "
  • No specific details found (fields might be null or empty).
  • "; } } else { $html_items = "
  • No specific details record found in " . htmlspecialchars($table_name) . ".
  • "; } return $html_items; } // --- Helper Function: Generate HTML list item --- function generate_li($label, $value, $description = '', $is_preformatted = false, $is_html = false) { // (Function unchanged) if ($value === null || $value === '' || $value === 'N/A') { $value_display = 'N/A'; } elseif ($is_html) { $value_display = $value; } else { $value_display = htmlspecialchars($value); } $desc_html = $description ? " (" . htmlspecialchars(trim($description)) . ")" : ""; return "
  • " . htmlspecialchars($label) . ": " . $value_display . $desc_html . "
  • \n"; } // --- Helper Function: Format detail values (FINAL REVISION) --- function format_detail_value($key, $value) { // Removed $currency_code parameter, logic simplified if ($value === null || $value === '') { return ['value' => 'N/A', 'is_html' => false]; } // Default: just escape $formatted_value = htmlspecialchars($value); $is_html = false; if (is_string($value)) { // JSON? $decoded_json = json_decode($value); if (json_last_error() === JSON_ERROR_NONE && (is_object($decoded_json) || is_array($decoded_json))) { $formatted_value = '
    ' . htmlspecialchars(json_encode($decoded_json, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)) . '
    '; $is_html = true; } // Hex? (Avoid common keys that might look like hex but aren't) elseif (ctype_xdigit($value) && strlen($value) > 4 && strpos($key, 'Currency') === false && strpos($key, 'Hash') === false && strpos($key, 'Index') === false && $key !== 'HighNode' && $key !== 'LowNode' ) { $bin_data = @hex2bin($value); if ($bin_data !== false && preg_match('//u', $bin_data)) { if (mb_check_encoding($bin_data, 'UTF-8') && strlen(trim(preg_replace('/[\x00-\x1F\x7F]/u', '', $bin_data))) > 0) { $formatted_value = '
    ' . htmlspecialchars($bin_data) . '
    (' . htmlspecialchars($value) . ' Hex)
    '; $is_html = true; } elseif (strlen($value) > 60) { // Show long unreadable hex in pre $formatted_value = '
    ' . htmlspecialchars($value) . ' (Hex)
    '; $is_html = true; } // else: short unreadable hex, keep as escaped string (default) } elseif (strlen($value) > 60) { // hex2bin failed, long string $formatted_value = '
    ' . htmlspecialchars($value) . '
    '; $is_html = true; } // else: short non-decoding hex, keep as escaped string (default) } // Numeric String? (Likely Issued Currency Value) -> return precisely elseif (is_numeric($value)) { // Exception: Balance is drops, format as XRP if ($key === 'Balance' || strpos($key, 'drops') !== false ) { $xrp_val = $value / 1000000; $formatted_value = rtrim(rtrim(number_format((float)$xrp_val, 6, '.', ','), '0'), '.'); // Suffix added separately where needed } else { // For other numeric strings (like amounts), return exactly as stored $formatted_value = htmlspecialchars($value); } $is_html = false; // It's numeric text, not HTML structure } // Other long strings elseif (strlen($value) > 60 && !str_contains($value, ' ')) { $formatted_value = '
    ' . htmlspecialchars($value) . '
    '; $is_html = true; } // else: default string handled by initial htmlspecialchars } elseif (is_numeric($value)) { // Fee (always drops) -> Format as XRP if ($key === 'Fee') { $xrp_val = $value / 1000000; $formatted_value = rtrim(rtrim(number_format((float)$xrp_val, 6, '.', ','), '0'), '.'); } // Balance change or key indicating already XRP -> Format as XRP elseif (strpos($key, 'recipient_xrp_balance_change') !== false || strpos($key, '_XRP') !== false) { $formatted_value = rtrim(rtrim(number_format((float)$value, 6, '.', ','), '0'), '.'); } // Other floats -> Format with precision elseif (is_float($value) || strpos((string)$value, '.') !== false) { $formatted_value = rtrim(rtrim(number_format($value, 16, '.', ','), '0'), '.'); } // Large integers (non-IDs) -> Add commas elseif (abs($value) >= 1000 && strpos($key,'Index') === false && strpos($key,'Seq') === false && strpos($key,'date') === false && strpos($key,'time') === false) { $formatted_value = number_format($value); } // else: small integers, IDs -> keep as is (default) $is_html = false; } elseif (is_bool($value)) { $formatted_value = $value ? 'Yes' : 'No'; $is_html = false; } else { // Other types $formatted_value = '
    ' . htmlspecialchars(var_export($value, true)) . '
    '; $is_html = true; } return ['value' => $formatted_value, 'is_html' => $is_html]; } // --- Helper function for hardcoded field descriptions --- function get_field_description($field_name) { // (Function unchanged) $descriptions = [ 'Node_PreviousTxnID' => 'Transaction ID of the last modification to this node', 'Node_PreviousTxnLgrSeq' => 'Ledger sequence number of the last modification', 'New_Account' => 'Account address for new nodes', 'New_Balance' => 'Initial account balance in drops for new nodes', 'New_Sequence' => 'Initial account sequence number for new nodes', 'Final_Account' => 'Account address after the transaction', 'Final_Balance' => 'Account balance in drops after the transaction', 'Previous_Balance' => 'Account balance in drops before the transaction', 'Final_OwnerCount' => 'Number of owned objects after the transaction', 'Previous_OwnerCount' => 'Number of owned objects before the transaction', 'Final_PreviousTxnID' => 'Previous transaction ID (affecting this node) after the transaction', 'Final_PreviousTxnLgrSeq' => 'Previous transaction ledger sequence (affecting this node) after the transaction', 'Final_Sequence' => 'Account sequence number after the transaction', 'Previous_Sequence' => 'Account sequence number before the transaction', 'Final_TakerPays_Value' => 'Amount the taker pays after modification', 'Final_TakerGets_Value' => 'Amount the taker gets after modification', 'Previous_TakerPays_Value' => 'Amount the taker pays before modification', 'Previous_TakerGets_Value' => 'Amount the taker gets before modification', 'Final_LPTokenBalance_Value' => 'Liquidity pool token balance value after transaction', 'Previous_LPTokenBalance_Value' => 'Liquidity pool token balance value before transaction', 'Final_TradingFee' => 'Trading fee percentage after transaction', 'Previous_TradingFee' => 'Trading fee percentage before transaction', 'Asset_Currency' => 'Currency code of the first asset', 'Asset_Issuer' => 'Issuer address of the first asset', 'Asset2_Currency' => 'Currency code of the second asset', 'Asset2_Issuer' => 'Issuer address of the second asset', 'Amount_Value' => 'Primary amount value', 'Amount_Currency' => 'Primary amount currency', 'Amount_Issuer' => 'Primary amount issuer', 'Amount2_Value' => 'Secondary amount value (e.g., in AMM)', 'Amount2_Currency' => 'Secondary amount currency', 'Amount2_Issuer' => 'Secondary amount issuer', 'LPTokenBalance_Value' => 'AMM Liquidity Provider token balance', 'LPTokenBalance_Currency' => 'AMM Liquidity Provider token currency', 'LPTokenBalance_Issuer' => 'AMM Liquidity Provider token issuer', 'EPrice_Value' => 'Effective Price value (AMM)', 'EPrice_Currency' => 'Effective Price currency (AMM)', 'EPrice_Issuer' => 'Effective Price issuer (AMM)', 'CheckID' => 'Unique identifier of the Check object', 'NFTokenID' => 'Unique identifier of the Non-Fungible Token', 'OfferSequence' => 'Sequence number of the OfferCreate transaction that created this Offer', 'Channel' => 'Unique identifier of the Payment Channel', ]; return $descriptions[$field_name] ?? ''; } // --- Helper Function: Get column comment from schema --- function get_column_comment($mysql, $table_name, $column_name) { // (Function unchanged) static $comment_cache = []; $cache_key = $table_name . '.' . $column_name; if (isset($comment_cache[$cache_key])) { return $comment_cache[$cache_key]; } $sql = "SELECT COLUMN_COMMENT FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?"; $comment_result = $mysql->mysql_request_with_prepare($sql, 'ss', [$table_name, $column_name]); $comment = ''; if ($comment_result && !empty($comment_result) && isset($comment_result[0]['COLUMN_COMMENT'])) { $comment = $comment_result[0]['COLUMN_COMMENT']; } $comment_cache[$cache_key] = $comment; return $comment; } // *** HELPER FUNCTION: Decode XRPL Currency Codes (Corrected) *** function decode_currency_code($code) { if (empty($code) || $code === 'XRP') { return 'XRP'; } // Check if it's a 40-character hex code if (strlen($code) === 40 && ctype_xdigit($code)) { $decoded_hex = hex2bin($code); if ($decoded_hex === false) { // Safety check for invalid hex return $code; // Return original hex if hex2bin fails } // Find the initial sequence of printable ASCII characters (space to ~) if (preg_match('/^([ -~]+)/', $decoded_hex, $matches)) { $printable_part = $matches[1]; $printable_len = strlen($printable_part); // Check if the *rest* of the 20-byte string is null bytes $is_standard_format = true; // Start checking from the byte *after* the printable part for ($i = $printable_len; $i < 20; $i++) { if (isset($decoded_hex[$i]) && ord($decoded_hex[$i]) !== 0) { $is_standard_format = false; break; } } // If it matches the standard format (printable chars followed by only nulls) if ($is_standard_format) { return $printable_part; // Return all leading printable chars } } // If not standard format (no printable chars found, or non-nulls after printable), return original hex var_dump($code); return $code; } // If it wasn't 40-char hex, return it as is (likely already 3-char standard) return $code; } ?>