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 '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 '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 .= "
";
$core_table_name = 'transactions';
$details_html .= generate_li('Database Transaction ID', $transaction['transaction_db_id'] ?? null, get_column_comment($mysql, $core_table_name, 'id'));
$details_html .= generate_li('Transaction Hash', $transaction['hash'] ?? null, get_column_comment($mysql, $core_table_name, 'hash'));
$ripple_epoch_offset = 946684800;
$ledger_close_ripple_time = $transaction['ledger_close_ripple_time'] ?? null;
$unix_timestamp = $ledger_close_ripple_time !== null ? $ledger_close_ripple_time + $ripple_epoch_offset : null;
$date_obj = $unix_timestamp ? DateTime::createFromFormat('U', $unix_timestamp) : null;
$date_display = ($date_obj instanceof DateTime) ? $date_obj->format('Y-m-d H:i:s') . ' UTC' : 'N/A';
$details_html .= generate_li('Timestamp (Ledger Close Time)', $date_display , get_column_comment($mysql, 'ledgers', 'close_time_human'));
$details_html .= generate_li('Initiating Account', $transaction['Account'] ?? null, get_column_comment($mysql, $core_table_name, 'Account'));
$details_html .= generate_li('Transaction Type', $transaction_type, get_column_comment($mysql, $core_table_name, 'TransactionType'));
$fee_in_drops = $transaction['Fee'] ?? null;
// Pass 'Fee' key and drops value to formatter; formatter handles division and adding ' XRP'
$fee_formatted = format_detail_value('Fee', $fee_in_drops);
$fee_comment = get_column_comment($mysql, $core_table_name, 'Fee');
$details_html .= generate_li('Fee', $fee_formatted['value'], ($fee_in_drops !== null ? 'Fee paid (' . number_format($fee_in_drops) . ' drops)' : 'Fee N/A') . ($fee_comment ? ' - '.$fee_comment : ''), false, $fee_formatted['is_html']);
$details_html .= generate_li('Sequence Number', $transaction['Sequence'] ?? null, get_column_comment($mysql, $core_table_name, 'Sequence'));
$pubkey_formatted = format_detail_value('SigningPubKey', $transaction['SigningPubKey'] ?? null);
$details_html .= generate_li('Signing Public Key', $pubkey_formatted['value'], get_column_comment($mysql, $core_table_name, 'SigningPubKey'), false, $pubkey_formatted['is_html']);
$sig_formatted = format_detail_value('TxnSignature', $transaction['TxnSignature'] ?? null);
$details_html .= generate_li('Transaction Signature', $sig_formatted['value'], get_column_comment($mysql, $core_table_name, 'TxnSignature'), false, $sig_formatted['is_html']);
$details_html .= generate_li('Index in Ledger', $transaction['TransactionIndex'] ?? null, get_column_comment($mysql, $core_table_name, 'TransactionIndex'));
// (Transaction Result and Validated formatting unchanged)
$tx_result = $transaction['TransactionResult'] ?? null;
$result_interpretation = '';
$result_docs_link = '(See Docs)';
if ($tx_result === 'tesSUCCESS') { $result_interpretation = 'Applied successfully to the ledger.'; }
elseif (strpos($tx_result ?? '', 'tec') === 0) { $result_interpretation = 'Failed, but included in ledger to charge fee. ' . $result_docs_link; }
elseif (strpos($tx_result ?? '', 'tef') === 0) { $result_interpretation = 'Failed locally before consensus. May not be final. ' . $result_docs_link; }
elseif (strpos($tx_result ?? '', 'tel') === 0) { $result_interpretation = 'Local server error. May succeed if resubmitted. ' . $result_docs_link; }
elseif (strpos($tx_result ?? '', 'tem') === 0) { $result_interpretation = 'Malformed transaction. Should not succeed. ' . $result_docs_link; }
elseif (strpos($tx_result ?? '', 'ter') === 0) { $result_interpretation = 'Retryable failure. May succeed/fail differently later. ' . $result_docs_link; }
$details_html .= generate_li('Transaction Result', $tx_result, $result_interpretation . ' ' . get_column_comment($mysql, $core_table_name, 'TransactionResult'), false, true);
$validated_value = $transaction['validated'] ?? null;
$validated_formatted = format_detail_value('validated', $validated_value);
$validated_interpretation = '';
if ($validated_value === 1 || $validated_value === '1' || $validated_value === true) { $validated_interpretation = 'Final: Included in a validated ledger.'; }
elseif ($validated_value === 0 || $validated_value === '0' || $validated_value === false) { $validated_interpretation = 'Not final: Outcome may potentially change.'; }
else { $validated_interpretation = get_column_comment($mysql, $core_table_name, 'validated'); }
$details_html .= generate_li('Validated', $validated_formatted['value'], $validated_interpretation, false, $validated_formatted['is_html']);
$details_html .= generate_li('Last Ledger Sequence', $transaction['LastLedgerSequence'] ?? null, get_column_comment($mysql, $core_table_name, 'LastLedgerSequence'));
$details_html .= "
";
// --- Ledger Information ---
// (Unchanged)
$details_html .= "
Ledger Information
";
$details_html .= "
";
$ledger_table_name = 'ledgers';
$ledger_idx_formatted = format_detail_value('ledger_index', $transaction['ledger_index'] ?? null);
$details_html .= generate_li('Ledger Index', $ledger_idx_formatted['value'], get_column_comment($mysql, $ledger_table_name, 'ledger_index'), false, $ledger_idx_formatted['is_html']);
$ledger_hash_formatted = format_detail_value('ledger_hash', $transaction['ledger_hash'] ?? null);
$details_html .= generate_li('Ledger Hash', $ledger_hash_formatted['value'], get_column_comment($mysql, $ledger_table_name, 'ledger_hash'), false, $ledger_hash_formatted['is_html']);
$details_html .= generate_li('Ledger Close Time (Human)', $date_display, get_column_comment($mysql, $ledger_table_name, 'close_time_human'));
$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
";
$sql_flags = "SELECT flag_value, flag_name, context, description, is_deprecated FROM xrpl_flags WHERE context = ? OR context = 'Global'";
$known_flags = $mysql->mysql_request_with_prepare($sql_flags, 's', [$transaction_type]);
$known_flags_map = [];
// var_dump($known_flags);
if($known_flags){
if (!empty($known_flags) && !is_array($known_flags[0])) { $known_flags = [$known_flags]; }
foreach($known_flags as $kf) { $known_flags_map[(int)$kf['flag_value']] = $kf; }
}
foreach ($tx_specific_data as $key => $value) {
// Skip keys shown elsewhere or internal IDs
if (in_array($key, ['id', 'transaction_id', 'Amount_Value', 'Amount_Currency', 'Amount_Issuer', 'DeliveredAmount_Value', 'DeliveredAmount_Currency', 'DeliveredAmount_Issuer']) || $value === null || $value === '') { continue; }
$formatted_key = htmlspecialchars(preg_replace('/(? $flag) {
if (isset($flag['context']) && ($flag['context'] == $transaction_type || $flag['context'] == 'Global') && ($flags_value & $flag_val) === $flag_val) {
$flag_desc = htmlspecialchars($flag['description']) . (isset($flag['is_deprecated']) && $flag['is_deprecated'] ? ' (Deprecated)' : '');
$specific_details_html .= generate_li(' - Flag Active', htmlspecialchars($flag['flag_name']), $flag_desc, false, true);
$explained_bits |= $flag_val;
}
}
$unknown_bits = $flags_value & (~$explained_bits);
if ($unknown_bits > 0) {
$unknown_formatted = format_detail_value('UnknownFlags', $unknown_bits);
$specific_details_html .= generate_li(' - Other/Unknown Flags Set', $unknown_formatted['value'], 'Flag bits set but not defined in local xrpl_flags table for ' . htmlspecialchars($transaction_type) . ' or Global 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, $specific_table_name, $key); }
// Append the decoded currency value manually for clarity ONLY IF it's different from the raw value
$decoded_currency = decode_currency_code($value);
$display_val = $formatted_val['value'];
if ($decoded_currency !== $value && $decoded_currency !== 'XRP'){ // Append decoded only if different and not XRP
$display_val .= ' (' . htmlspecialchars($decoded_currency) . ')';
}
$specific_details_html .= generate_li($formatted_key, $display_val, $description, false, $formatted_val['is_html']);
}
else {
// Handle other fields, passing currency code if it's a value field
$currency_code_for_value = null;
if (strpos($key, '_Value') !== false) {
$currency_key = str_replace('_Value', '_Currency', $key);
if (isset($tx_specific_data[$currency_key])) {
$currency_code_for_value = $tx_specific_data[$currency_key];
}
}
$formatted_val = format_detail_value($key, $value, $currency_code_for_value);
$description = get_field_description($key);
if (empty($description)) { $description = get_column_comment($mysql, $specific_table_name, $key); }
// *** Manually append decoded currency to formatted value ***
$display_val = $formatted_val['value'];
if ($currency_code_for_value){
$decoded_curr = decode_currency_code($currency_code_for_value);
if($decoded_curr !== 'XRP'){
$display_val .= ' ' . htmlspecialchars($decoded_curr);
} else {
$display_val .= ' XRP'; // Add XRP if applicable
}
}
$specific_details_html .= generate_li($formatted_key, $display_val, $description, false, $formatted_val['is_html']);
}
}
$specific_details_html .= "
";
}
$details_html .= $specific_details_html;
}
// *** Amount Details Section (using corrected formatter + manual append) ***
$details_html .= "
Amount Details (from delivered_amounts table)
";
$details_html .= "
";
$da_table = 'delivered_amounts';
// Original Transaction Amount
$tx_curr_raw = $transaction['transaction_currency'] ?? null;
$tx_amt_value = $transaction['transaction_amount'] ?? null;
$tx_amt_fmt = format_detail_value('transaction_amount', $tx_amt_value); // Format value only
$tx_curr_decoded = decode_currency_code($tx_curr_raw); // Decode currency
$details_html .= generate_li(
'Original Transaction Amount',
$tx_amt_fmt['value'] . ($tx_curr_decoded ? ' ' . htmlspecialchars($tx_curr_decoded) : ''), // Append decoded currency
get_column_comment($mysql, $da_table, 'transaction_amount'),
false, $tx_amt_fmt['is_html']
);
if ($tx_curr_decoded !== 'XRP' && $tx_curr_raw !== null && isset($transaction['transaction_issuer'])) {
$details_html .= generate_li(' - Original Transaction Issuer', $transaction['transaction_issuer'], get_column_comment($mysql, $da_table, 'transaction_issuer'));
}
// Actual Delivered Amount
$del_curr_raw = $transaction['delivered_currency'] ?? null;
$del_amt_value = $transaction['delivered_amount'] ?? null;
$del_amt_fmt = format_detail_value('delivered_amount', $del_amt_value); // Format value only
$del_curr_decoded = decode_currency_code($del_curr_raw); // Decode currency
$details_html .= generate_li(
'Actual Delivered Amount (from Metadata)',
$del_amt_fmt['value'] . ($del_curr_decoded ? ' ' . htmlspecialchars($del_curr_decoded) : ''), // Append decoded currency
get_column_comment($mysql, $da_table, 'delivered_amount'),
false, $del_amt_fmt['is_html']
);
if ($del_curr_decoded !== 'XRP' && $del_curr_raw !== null && isset($transaction['delivered_issuer'])) {
$details_html .= generate_li(' - Delivered Currency Issuer', $transaction['delivered_issuer'], get_column_comment($mysql, $da_table, 'delivered_issuer'));
}
// Recipient XRP Balance Change
$xrp_change_value = $transaction['recipient_xrp_balance_change'] ?? null;
$xrp_change_fmt = format_detail_value('recipient_xrp_balance_change', $xrp_change_value); // Formatter includes ' XRP'
$details_html .= generate_li(
'Recipient XRP Balance Change (Calculated)',
($xrp_change_value !== null ? $xrp_change_fmt['value'] : 'N/A'),
get_column_comment($mysql, $da_table, 'recipient_xrp_balance_change'),
false, $xrp_change_fmt['is_html']
);
$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
";
$memos_table_name = 'transaction_memos';
foreach ($memos as $memo) {
$memo_idx_formatted = format_detail_value('memo_index', $memo['memo_index'] ?? null);
$memos_html .= generate_li('Memo Index', $memo_idx_formatted['value'], get_column_comment($mysql, $memos_table_name, 'memo_index'), false, $memo_idx_formatted['is_html']);
$memos_html .= "";
$memo_type_formatted = format_detail_value('memo_type', $memo['memo_type'] ?? null);
$memos_html .= generate_li('Type', $memo_type_formatted['value'], get_column_comment($mysql, $memos_table_name, 'memo_type'), false, $memo_type_formatted['is_html']);
$memo_fmt_formatted = format_detail_value('memo_format', $memo['memo_format'] ?? null);
$memos_html .= generate_li('Format', $memo_fmt_formatted['value'], get_column_comment($mysql, $memos_table_name, 'memo_format'), false, $memo_fmt_formatted['is_html']);
$memo_data_formatted = format_detail_value('memo_data', $memo['memo_data'] ?? null);
$memos_html .= generate_li('Data', $memo_data_formatted['value'], get_column_comment($mysql, $memos_table_name, 'memo_data'), false, $memo_data_formatted['is_html']);
$memos_html .= "
";
}
$memos_html .= "
";
$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)
";
$signers_table_name = 'transaction_signers';
foreach ($signers as $signer) {
$signers_html .= generate_li('Signer Account', $signer['account'] ?? null, get_column_comment($mysql, $signers_table_name, 'account'));
$signers_html .= "";
$signer_pk_formatted = format_detail_value('signing_pub_key', $signer['signing_pub_key'] ?? null);
$signers_html .= generate_li('Signing Public Key', $signer_pk_formatted['value'], get_column_comment($mysql, $signers_table_name, 'signing_pub_key'), false, $signer_pk_formatted['is_html']);
$signer_sig_formatted = format_detail_value('txn_signature', $signer['txn_signature'] ?? null);
$signers_html .= generate_li('Transaction Signature', $signer_sig_formatted['value'], get_column_comment($mysql, $signers_table_name, 'txn_signature'), false, $signer_sig_formatted['is_html']);
$signers_html .= "
";
}
$signers_html .= "
";
$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 .= "
";
$affected_base_table = 'affected_nodes_base';
foreach ($affected_nodes_base as $node_base) {
$affected_nodes_html .= generate_li('Node Index', $node_base['node_index'] ?? null, get_column_comment($mysql, $affected_base_table, 'node_index'));
$affected_nodes_html .= "";
$affected_nodes_html .= generate_li('Action', $node_base['action'] ?? null, get_column_comment($mysql, $affected_base_table, 'action'));
$entry_type = $node_base['LedgerEntryType'] ?? null;
$entry_type_desc = get_column_comment($mysql, $affected_base_table, 'LedgerEntryType');
$entry_type_interpretation = '';
switch($entry_type) {
case 'AccountRoot': $entry_type_interpretation = ': Represents an account\'s settings & XRP balance.'; break;
case 'RippleState': $entry_type_interpretation = ': Represents a trust line between two accounts.'; break;
// ... other cases ...
case 'Offer': $entry_type_interpretation = ': Represents an order in the decentralized exchange.'; break;
case 'DirectoryNode': $entry_type_interpretation = ': A list linking other ledger entries (Owner/Offer/NFT directories).'; break;
case 'Check': $entry_type_interpretation = ': Represents a deferred payment check.'; break;
case 'PayChannel': $entry_type_interpretation = ': Represents a payment channel for off-ledger payments.'; break;
case 'Escrow': $entry_type_interpretation = ': Represents XRP held conditionally (time/crypto-condition).'; break;
case 'NFTokenPage': $entry_type_interpretation = ': A page storing multiple NFTs owned by an account.'; break;
case 'NFTokenOffer': $entry_type_interpretation = ': Represents an offer to buy or sell an NFT.'; break;
case 'SignerList': $entry_type_interpretation = ': Represents a list of addresses authorized to multi-sign for an account.'; break;
case 'Ticket': $entry_type_interpretation = ': Represents a Ticket sequence for submitting future transactions.'; break;
case 'AMM': $entry_type_interpretation = ': Represents an Automated Market Maker pool instance.'; break;
}
$full_desc = ($entry_type_desc ?: 'Type of ledger object affected') . $entry_type_interpretation;
$affected_nodes_html .= generate_li('Ledger Entry Type', $entry_type, $full_desc);
$node_id_formatted = format_detail_value('LedgerIndex', $node_base['LedgerIndex'] ?? null);
$affected_nodes_html .= generate_li('Ledger Index (Node ID)', $node_id_formatted['value'], get_column_comment($mysql, $affected_base_table, 'LedgerIndex'), false, $node_id_formatted['is_html']);
$node_details_list_items = get_specific_node_details($mysql, $node_base['base_id'], $entry_type); // Calls the function below
$no_details_found = strpos($node_details_list_items, 'No specific details') !== false ||
strpos($node_details_list_items, 'not found or not supported') !== false ||
strpos($node_details_list_items, 'Invalid Ledger Entry Type') !== false ||
strpos($node_details_list_items, 'schema check failed') !== false;
if (!$no_details_found) {
$affected_nodes_html .= "- Details:
" . $node_details_list_items . "
";
} else {
$affected_nodes_html .= "- Details:" . $node_details_list_items . "
";
}
$affected_nodes_html .= "
";
}
$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 "