Promote explorer content links to real anchors

This commit is contained in:
defiQUG
2026-03-28 14:09:23 -07:00
parent 6d0e250f84
commit f309c303ff
2 changed files with 51 additions and 33 deletions

View File

@@ -366,7 +366,7 @@
if (list.length === 0) { container.innerHTML = filterBar + '<p style="color: var(--text-light);">No addresses in watchlist. Open an address and click "Add to watchlist".</p>'; return; }
if (filtered.length === 0) { container.innerHTML = filterBar + '<p style="color: var(--text-light);">No watchlist entries match the current filter.</p>'; return; }
var html = filterBar + '<table class="table"><thead><tr><th>Address</th><th>Label</th><th></th></tr></thead><tbody>';
filtered.forEach(function(addr){ var label = getAddressLabel(addr) || ''; html += '<tr><td class="hash" onclick="showAddressDetail(\'' + escapeHtml(addr) + '\')" style="cursor: pointer;">' + escapeHtml(shortenHash(addr)) + '</td><td>' + escapeHtml(label) + '</td><td><button type="button" class="btn btn-secondary" style="padding: 0.25rem 0.5rem; font-size: 0.75rem;" onclick="event.stopPropagation(); removeFromWatchlist(\'' + escapeHtml(addr) + '\'); if(window._renderWatchlist) window._renderWatchlist();">Remove</button></td></tr>'; });
filtered.forEach(function(addr){ var label = getAddressLabel(addr) || ''; html += '<tr><td>' + explorerAddressLink(addr, escapeHtml(shortenHash(addr)), 'color: inherit; text-decoration: none;') + '</td><td>' + escapeHtml(label) + '</td><td><button type="button" class="btn btn-secondary" style="padding: 0.25rem 0.5rem; font-size: 0.75rem;" onclick="event.stopPropagation(); removeFromWatchlist(\'' + escapeHtml(addr) + '\'); if(window._renderWatchlist) window._renderWatchlist();">Remove</button></td></tr>'; });
html += '</tbody></table>';
container.innerHTML = html;
};
@@ -3743,7 +3743,7 @@
html += '<td>' + escapeHtml(row.category) + '</td>';
html += '<td>' + escapeHtml(row.poolPair) + '</td>';
html += '<td>' + escapeHtml(row.poolType) + '</td>';
html += '<td>' + (safeAddress(addr) ? '<span class="hash" onclick="showAddressDetail(\'' + escapeHtml(addr) + '\')" style="cursor: pointer;">' + escapeHtml(shortenHash(addr)) + '</span>' : '<span style="color: var(--text-light);">—</span>') + '</td>';
html += '<td>' + (safeAddress(addr) ? explorerAddressLink(addr, escapeHtml(shortenHash(addr)), 'color: inherit; text-decoration: none;') : '<span style="color: var(--text-light);">—</span>') + '</td>';
html += '<td>' + escapeHtml(row.status) + '</td>';
html += '<td>' + escapeHtml(row.notes) + '</td>';
html += '</tr>';
@@ -4150,19 +4150,19 @@
<div class="chain-stat" style="background: var(--light); padding: 1rem; border-radius: 8px;">
<div class="chain-stat-label" style="font-weight: 600; margin-bottom: 0.5rem;">CCIPWETH9Bridge</div>
<div class="chain-stat-value">
<span class="hash" onclick="showAddressDetail('${WETH9_BRIDGE_138}')" style="cursor: pointer; font-size: 0.9rem;">${WETH9_BRIDGE_138}</span>
${explorerAddressLink(WETH9_BRIDGE_138, escapeHtml(WETH9_BRIDGE_138), 'color: inherit; text-decoration: none; font-size: 0.9rem;')}
</div>
<div style="margin-top: 0.5rem; font-size: 0.85rem; color: var(--text-light);">
Token: <span class="hash" onclick="showAddressDetail('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2')" style="cursor: pointer;">WETH9</span>
Token: ${explorerAddressLink('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', 'WETH9', 'color: inherit; text-decoration: none;')}
</div>
</div>
<div class="chain-stat" style="background: var(--light); padding: 1rem; border-radius: 8px;">
<div class="chain-stat-label" style="font-weight: 600; margin-bottom: 0.5rem;">CCIPWETH10Bridge</div>
<div class="chain-stat-value">
<span class="hash" onclick="showAddressDetail('${WETH10_BRIDGE_138}')" style="cursor: pointer; font-size: 0.9rem;">${WETH10_BRIDGE_138}</span>
${explorerAddressLink(WETH10_BRIDGE_138, escapeHtml(WETH10_BRIDGE_138), 'color: inherit; text-decoration: none; font-size: 0.9rem;')}
</div>
<div style="margin-top: 0.5rem; font-size: 0.85rem; color: var(--text-light);">
Token: <span class="hash" onclick="showAddressDetail('0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f')" style="cursor: pointer;">WETH10</span>
Token: ${explorerAddressLink('0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f', 'WETH10', 'color: inherit; text-decoration: none;')}
</div>
</div>
</div>
@@ -4315,8 +4315,8 @@
<h4 style="margin-top: 1.5rem; margin-bottom: 0.5rem;">CCIP Infrastructure:</h4>
<ul style="margin-left: 2rem; margin-top: 0.5rem;">
<li><strong>CCIP Router (Chain 138)</strong>: <span class="hash" onclick="showAddressDetail('0x8078A09637e47Fa5Ed34F626046Ea2094a5CDE5e')" style="cursor: pointer;">0x8078A09637e47Fa5Ed34F626046Ea2094a5CDE5e</span></li>
<li><strong>CCIP Sender (Chain 138)</strong>: <span class="hash" onclick="showAddressDetail('0x105F8A15b819948a89153505762444Ee9f324684')" style="cursor: pointer;">0x105F8A15b819948a89153505762444Ee9f324684</span></li>
<li><strong>CCIP Router (Chain 138)</strong>: ${explorerAddressLink('0x8078A09637e47Fa5Ed34F626046Ea2094a5CDE5e', '0x8078A09637e47Fa5Ed34F626046Ea2094a5CDE5e', 'color: inherit; text-decoration: none;')}</li>
<li><strong>CCIP Sender (Chain 138)</strong>: ${explorerAddressLink('0x105F8A15b819948a89153505762444Ee9f324684', '0x105F8A15b819948a89153505762444Ee9f324684', 'color: inherit; text-decoration: none;')}</li>
</ul>
</div>
</div>
@@ -4336,6 +4336,24 @@
if (/^0x0{40}$/i.test(s)) return null;
return s;
}
function escapeJsSingleQuoted(value) {
return String(value == null ? '' : value).replace(/\\/g, '\\\\').replace(/'/g, "\\'");
}
function explorerAddressLink(address, content, style) {
var safe = safeAddress(address);
if (!safe) return content || 'N/A';
return '<a class="hash" href="/address/' + encodeURIComponent(safe) + '" onclick="event.preventDefault(); event.stopPropagation(); showAddressDetail(\'' + escapeJsSingleQuoted(safe) + '\')" style="' + escapeAttr(style || 'color: inherit; text-decoration: none;') + '">' + (content || escapeHtml(shortenHash(safe))) + '</a>';
}
function explorerTransactionLink(txHash, content, style) {
var safe = safeTxHash(txHash);
if (!safe) return content || 'N/A';
return '<a class="hash" href="/tx/' + encodeURIComponent(safe) + '" onclick="event.preventDefault(); event.stopPropagation(); showTransactionDetail(\'' + escapeJsSingleQuoted(safe) + '\')" style="' + escapeAttr(style || 'color: inherit; text-decoration: none;') + '">' + (content || escapeHtml(shortenHash(safe))) + '</a>';
}
function explorerBlockLink(blockNumber, content, style) {
var safe = safeBlockNumber(blockNumber);
if (!safe) return content || 'N/A';
return '<a href="/block/' + encodeURIComponent(safe) + '" onclick="event.preventDefault(); event.stopPropagation(); showBlockDetail(\'' + escapeJsSingleQuoted(safe) + '\')" style="' + escapeAttr(style || 'color: inherit; text-decoration: none;') + '">' + (content || escapeHtml(String(safe))) + '</a>';
}
async function renderBlockDetail(blockNumber) {
const bn = safeBlockNumber(blockNumber);
if (!bn) { showToast('Invalid block number', 'error'); return; }
@@ -4396,7 +4414,7 @@
</div>
<div class="info-row">
<div class="info-label">Parent Hash</div>
<div class="info-value hash" onclick="showBlockDetail('${escapeHtml(String(parseInt(b.number) - 1))}')" style="cursor: pointer;">${escapeHtml(b.parent_hash || '')}</div>
<div class="info-value">${explorerBlockLink(String(parseInt(b.number) - 1), escapeHtml(b.parent_hash || ''), 'color: inherit; text-decoration: none;')}</div>
</div>
<div class="info-row">
<div class="info-label">Timestamp</div>
@@ -4404,7 +4422,7 @@
</div>
<div class="info-row">
<div class="info-label">Miner</div>
<div class="info-value hash" onclick="showAddressDetail('${escapeHtml(b.miner || '')}')" style="cursor: pointer;">${formatAddressWithLabel(b.miner || '') || 'N/A'}</div>
<div class="info-value">${explorerAddressLink(b.miner || '', formatAddressWithLabel(b.miner || '') || 'N/A', 'color: inherit; text-decoration: none;')}</div>
</div>
<div class="info-row">
<div class="info-label">Transaction Count</div>
@@ -4501,7 +4519,7 @@
const revertReason = t.revert_reason || (rawTx && (rawTx.revert_reason || rawTx.error || rawTx.result));
const inputHex = (t.input && t.input !== '0x') ? t.input : null;
const decodedInput = t.decoded_input || (rawTx && rawTx.decoded_input);
const toCellContent = t.to ? formatAddressWithLabel(t.to) + ' <button type="button" class="btn-copy" onclick="event.stopPropagation(); copyToClipboard(\'' + String(t.to).replace(/\\/g, '\\\\').replace(/'/g, "\\'") + '\', \'Copied\');" aria-label="Copy address"><i class="fas fa-copy"></i></button>' : 'N/A';
const toCellContent = t.to ? explorerAddressLink(t.to, formatAddressWithLabel(t.to), 'color: inherit; text-decoration: none;') + ' <button type="button" class="btn-copy" onclick="event.stopPropagation(); copyToClipboard(\'' + String(t.to).replace(/\\/g, '\\\\').replace(/'/g, "\\'") + '\', \'Copied\');" aria-label="Copy address"><i class="fas fa-copy"></i></button>' : 'N/A';
let mainHtml = `
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
@@ -4533,7 +4551,7 @@
</div>
<div class="info-row">
<div class="info-label">Block Number</div>
<div class="info-value hash" onclick="showBlockDetail('${escapeHtml(String(t.block_number || ''))}')" style="cursor: pointer;">${escapeHtml(String(t.block_number || 'N/A'))}</div>
<div class="info-value">${explorerBlockLink(String(t.block_number || ''), escapeHtml(String(t.block_number || 'N/A')), 'color: inherit; text-decoration: none;')}</div>
</div>
<div class="info-row">
<div class="info-label">Block Hash</div>
@@ -4541,11 +4559,11 @@
</div>
<div class="info-row">
<div class="info-label">From</div>
<div class="info-value hash" onclick="showAddressDetail('${escapeHtml(t.from || '')}')" style="cursor: pointer;">${formatAddressWithLabel(t.from || '')} <button type="button" class="btn-copy" onclick="event.stopPropagation(); copyToClipboard('${escapeHtml(t.from || '')}', 'Copied');" aria-label="Copy address"><i class="fas fa-copy"></i></button></div>
<div class="info-value">${explorerAddressLink(t.from || '', formatAddressWithLabel(t.from || ''), 'color: inherit; text-decoration: none;')} <button type="button" class="btn-copy" onclick="event.stopPropagation(); copyToClipboard('${escapeHtml(t.from || '')}', 'Copied');" aria-label="Copy address"><i class="fas fa-copy"></i></button></div>
</div>
<div class="info-row">
<div class="info-label">To</div>
<div class="info-value hash" onclick="showAddressDetail('${escapeHtml(t.to || '')}')" style="cursor: pointer;">${toCellContent}</div>
<div class="info-value">${toCellContent}</div>
</div>
<div class="info-row">
<div class="info-label">Value</div>
@@ -4569,7 +4587,7 @@
${t.tx_burnt_fee && parseInt(t.tx_burnt_fee) > 0 ? `<div class="info-row"><div class="info-label">Burnt Fee</div><div class="info-value">${burntFeeEth} ETH</div></div>` : ''}
<div class="info-row"><div class="info-label">Nonce</div><div class="info-value">${t.nonce || 'N/A'}</div></div>
<div class="info-row"><div class="info-label">Timestamp</div><div class="info-value">${timestamp}</div></div>
${t.contract_address ? `<div class="info-row"><div class="info-label">Contract Address</div><div class="info-value hash" onclick="showAddressDetail('${escapeHtml(t.contract_address)}')" style="cursor: pointer;">${escapeHtml(t.contract_address)}</div></div>` : ''}
${t.contract_address ? `<div class="info-row"><div class="info-label">Contract Address</div><div class="info-value">${explorerAddressLink(t.contract_address, escapeHtml(t.contract_address), 'color: inherit; text-decoration: none;')}</div></div>` : ''}
`;
if (revertReason && t.status !== 1) {
@@ -4650,7 +4668,7 @@
const to = it.to?.hash || it.to || 'N/A';
const val = it.value ? formatEther(it.value) : '0';
const type = it.type || it.call_type || 'call';
tbl += '<tr><td>' + escapeHtml(type) + '</td><td class="hash" onclick="showAddressDetail(\'' + escapeHtml(from) + '\')" style="cursor: pointer;">' + escapeHtml(shortenHash(from)) + '</td><td class="hash" onclick="showAddressDetail(\'' + escapeHtml(to) + '\')" style="cursor: pointer;">' + escapeHtml(shortenHash(to)) + '</td><td>' + escapeHtml(val) + ' ETH</td></tr>';
tbl += '<tr><td>' + escapeHtml(type) + '</td><td>' + explorerAddressLink(from, escapeHtml(shortenHash(from)), 'color: inherit; text-decoration: none;') + '</td><td>' + explorerAddressLink(to, escapeHtml(shortenHash(to)), 'color: inherit; text-decoration: none;') + '</td><td>' + escapeHtml(val) + ' ETH</td></tr>';
});
if (filteredInternals.length === 0) {
tbl += '<tr><td colspan="4" style="text-align:center; padding: 1rem;">No internal transactions match the current filter.</td></tr>';
@@ -4682,7 +4700,7 @@
const topics = (log.topics && Array.isArray(log.topics)) ? log.topics : (log.topic0 ? [log.topic0] : []);
const topicsStr = topics.join(', ');
const data = log.data || log.raw_data || '0x';
tbl += '<tr id="txLogRow' + idx + '"><td class="hash" onclick="showAddressDetail(\'' + escapeHtml(addr) + '\')" style="cursor: pointer;">' + escapeHtml(shortenHash(addr)) + '</td><td style="word-break: break-all; font-size: 0.75rem;">' + escapeHtml(String(topicsStr).substring(0, 80)) + (String(topicsStr).length > 80 ? '...' : '') + '</td><td style="word-break: break-all; font-size: 0.75rem;">' + escapeHtml(String(data).substring(0, 66)) + (String(data).length > 66 ? '...' : '') + '</td><td id="txLogDecoded' + idx + '" style="font-size: 0.8rem;">—</td></tr>';
tbl += '<tr id="txLogRow' + idx + '"><td>' + explorerAddressLink(addr, escapeHtml(shortenHash(addr)), 'color: inherit; text-decoration: none;') + '</td><td style="word-break: break-all; font-size: 0.75rem;">' + escapeHtml(String(topicsStr).substring(0, 80)) + (String(topicsStr).length > 80 ? '...' : '') + '</td><td style="word-break: break-all; font-size: 0.75rem;">' + escapeHtml(String(data).substring(0, 66)) + (String(data).length > 66 ? '...' : '') + '</td><td id="txLogDecoded' + idx + '" style="font-size: 0.8rem;">—</td></tr>';
});
if (filteredLogs.length === 0) {
tbl += '<tr><td colspan="4" style="text-align:center; padding: 1rem;">No event logs match the current filter.</td></tr>';
@@ -4912,7 +4930,7 @@
<div class="info-label">Type</div>
<div class="info-value">${a.is_contract ? '<span class="badge badge-success">Contract</span>' + verifiedBadge + (contractLink ? '<br/>' + contractLink : '') : '<span class="badge badge-primary">EOA</span>'}</div>
</div>
${a.creation_tx_hash ? `<div class="info-row"><div class="info-label">Contract created in</div><div class="info-value hash" onclick="showTransactionDetail('${escapeHtml(a.creation_tx_hash)}')" style="cursor: pointer;">${escapeHtml(shortenHash(a.creation_tx_hash))} <button type="button" class="btn-copy" onclick="event.stopPropagation(); copyToClipboard('${escapeHtml(a.creation_tx_hash).replace(/'/g, "\\'")}', 'Copied');" aria-label="Copy"><i class="fas fa-copy"></i></button></div></div>` : ''}
${a.creation_tx_hash ? `<div class="info-row"><div class="info-label">Contract created in</div><div class="info-value">${explorerTransactionLink(a.creation_tx_hash, escapeHtml(shortenHash(a.creation_tx_hash)), 'color: inherit; text-decoration: none;')} <button type="button" class="btn-copy" onclick="event.stopPropagation(); copyToClipboard('${escapeHtml(a.creation_tx_hash).replace(/'/g, "\\'")}', 'Copied');" aria-label="Copy"><i class="fas fa-copy"></i></button></div></div>` : ''}
${a.first_seen_at ? `<div class="info-row"><div class="info-label">First seen</div><div class="info-value">${escapeHtml(typeof a.first_seen_at === 'string' ? a.first_seen_at : new Date(a.first_seen_at).toISOString())}</div></div>` : ''}
${a.last_seen_at ? `<div class="info-row"><div class="info-label">Last seen</div><div class="info-value">${escapeHtml(typeof a.last_seen_at === 'string' ? a.last_seen_at : new Date(a.last_seen_at).toISOString())}</div></div>` : ''}
<div class="tabs" style="margin-top: 1.5rem;">
@@ -5013,7 +5031,7 @@
const divisor = Math.pow(10, parseInt(decimals, 10));
const displayBalance = (Number(balance) / divisor).toLocaleString(undefined, { maximumFractionDigits: 6 });
const type = token.type || b.token_type || 'ERC-20';
tbl += '<tr><td><a href="/token/' + encodeURIComponent(contract) + '">' + escapeHtml(symbol) + '</a></td><td class="hash" onclick="showAddressDetail(\'' + escapeHtml(contract) + '\')" style="cursor: pointer;">' + escapeHtml(shortenHash(contract)) + '</td><td>' + escapeHtml(displayBalance) + '</td><td>' + escapeHtml(type) + '</td></tr>';
tbl += '<tr><td><a href="/token/' + encodeURIComponent(contract) + '">' + escapeHtml(symbol) + '</a></td><td>' + explorerAddressLink(contract, escapeHtml(shortenHash(contract)), 'color: inherit; text-decoration: none;') + '</td><td>' + escapeHtml(displayBalance) + '</td><td>' + escapeHtml(type) + '</td></tr>';
});
if (filteredItems.length === 0) {
tbl += '<tr><td colspan="4" style="text-align:center; padding: 1rem;">No token balances match the current filter.</td></tr>';
@@ -5063,7 +5081,7 @@
var tokenId = b.token_id != null ? b.token_id : (b.tokenId != null ? b.tokenId : (b.id != null ? b.id : '-'));
var name = token.name || token.symbol || '-';
var balance = b.value != null ? b.value : (b.balance != null ? b.balance : '1');
tbl += '<tr><td class="hash" onclick="showAddressDetail(\'' + escapeHtml(contract) + '\')" style="cursor: pointer;">' + escapeHtml(shortenHash(contract)) + '</td>';
tbl += '<tr><td>' + explorerAddressLink(contract, escapeHtml(shortenHash(contract)), 'color: inherit; text-decoration: none;') + '</td>';
tbl += '<td>' + (tokenId !== '-' ? '<a href="/nft/' + encodeURIComponent(contract) + '/' + encodeURIComponent(String(tokenId)) + '" onclick="event.preventDefault(); showNftDetail(\'' + escapeHtml(contract) + '\', \'' + escapeHtml(String(tokenId)) + '\'); updatePath(\'/nft/' + encodeURIComponent(contract) + '/' + encodeURIComponent(String(tokenId)) + '\');">' + escapeHtml(String(tokenId)) + '</a>' : '-') + '</td>';
tbl += '<td>' + escapeHtml(name) + '</td><td>' + escapeHtml(String(balance)) + '</td></tr>';
});
@@ -5108,7 +5126,7 @@
const val = it.value ? formatEther(it.value) : '0';
const block = it.block_number || it.block || '-';
const txHash = it.transaction_hash || it.tx_hash || '-';
tbl += '<tr><td onclick="showBlockDetail(\'' + escapeHtml(block) + '\')" style="cursor: pointer;">' + escapeHtml(block) + '</td><td class="hash" onclick="showAddressDetail(\'' + escapeHtml(from) + '\')" style="cursor: pointer;">' + escapeHtml(shortenHash(from)) + '</td><td class="hash" onclick="showAddressDetail(\'' + escapeHtml(to) + '\')" style="cursor: pointer;">' + escapeHtml(shortenHash(to)) + '</td><td>' + escapeHtml(val) + ' ETH</td><td class="hash" onclick="showTransactionDetail(\'' + escapeHtml(txHash) + '\')" style="cursor: pointer;">' + (txHash !== '-' ? escapeHtml(shortenHash(txHash)) : '-') + '</td></tr>';
tbl += '<tr><td>' + explorerBlockLink(block, escapeHtml(block), 'color: inherit; text-decoration: none;') + '</td><td>' + explorerAddressLink(from, escapeHtml(shortenHash(from)), 'color: inherit; text-decoration: none;') + '</td><td>' + explorerAddressLink(to, escapeHtml(shortenHash(to)), 'color: inherit; text-decoration: none;') + '</td><td>' + escapeHtml(val) + ' ETH</td><td>' + (txHash !== '-' ? explorerTransactionLink(txHash, escapeHtml(shortenHash(txHash)), 'color: inherit; text-decoration: none;') : '-') + '</td></tr>';
});
if (filteredItems.length === 0) {
tbl += '<tr><td colspan="5" style="text-align:center; padding: 1rem;">No internal transactions match the current filter.</td></tr>';
@@ -5312,7 +5330,7 @@
}) : txs.data;
let txHtml = filterBar + '<table class="table"><thead><tr><th>Hash</th><th>Block</th><th>From</th><th>To</th><th>Value</th></tr></thead><tbody>';
filteredTxs.forEach(function(tx) {
txHtml += '<tr onclick="showTransactionDetail(\'' + escapeHtml(tx.hash) + '\')" style="cursor: pointer;"><td class="hash">' + escapeHtml(shortenHash(tx.hash)) + '</td><td>' + escapeHtml(String(tx.block_number)) + '</td><td class="hash" onclick="event.stopPropagation(); showAddressDetail(\'' + escapeHtml(tx.from) + '\')" style="cursor: pointer;">' + formatAddressWithLabel(tx.from) + '</td><td class="hash" onclick="event.stopPropagation(); showAddressDetail(\'' + escapeHtml(tx.to || '') + '\')" style="cursor: pointer;">' + (tx.to ? formatAddressWithLabel(tx.to) : 'N/A') + '</td><td>' + escapeHtml(formatEther(tx.value || '0')) + ' ETH</td></tr>';
txHtml += '<tr onclick="showTransactionDetail(\'' + escapeHtml(tx.hash) + '\')" style="cursor: pointer;"><td>' + explorerTransactionLink(tx.hash, escapeHtml(shortenHash(tx.hash)), 'color: inherit; text-decoration: none;') + '</td><td>' + explorerBlockLink(String(tx.block_number), escapeHtml(String(tx.block_number)), 'color: inherit; text-decoration: none;') + '</td><td>' + explorerAddressLink(tx.from, formatAddressWithLabel(tx.from), 'color: inherit; text-decoration: none;') + '</td><td>' + (tx.to ? explorerAddressLink(tx.to, formatAddressWithLabel(tx.to), 'color: inherit; text-decoration: none;') : 'N/A') + '</td><td>' + escapeHtml(formatEther(tx.value || '0')) + ' ETH</td></tr>';
});
if (filteredTxs.length === 0) {
txHtml += '<tr><td colspan="5" style="text-align:center; padding: 1rem;">No transactions match the current filter.</td></tr>';
@@ -5380,7 +5398,7 @@
var addrEsc = tokenAddress.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
var symbolEsc = String(symbol).replace(/\\/g, '\\\\').replace(/'/g, "\\'");
var html = '<div class="token-detail-actions" style="margin-bottom: 1.25rem;"><button type="button" class="btn btn-primary btn-add-token-wallet-prominent" onclick="window.addTokenToWallet && window.addTokenToWallet(\'' + addrEsc + '\', \'' + symbolEsc + '\', ' + decimals + ');" aria-label="Add token to wallet"><i class="fas fa-wallet" aria-hidden="true"></i> Add to wallet (MetaMask)</button></div>';
html += '<div class="info-row"><div class="info-label">Contract</div><div class="info-value"><span class="hash" onclick="showAddressDetail(\'' + escapeHtml(tokenAddress) + '\')" style="cursor: pointer;">' + escapeHtml(tokenAddress) + '</span> <button type="button" class="btn-add-token-wallet" onclick="window.addTokenToWallet && window.addTokenToWallet(\'' + addrEsc + '\', \'' + symbolEsc + '\', ' + decimals + ');" aria-label="Add to wallet" title="Add to wallet"><i class="fas fa-wallet" aria-hidden="true"></i></button></div></div>';
html += '<div class="info-row"><div class="info-label">Contract</div><div class="info-value">' + explorerAddressLink(tokenAddress, escapeHtml(tokenAddress), 'color: inherit; text-decoration: none;') + ' <button type="button" class="btn-add-token-wallet" onclick="window.addTokenToWallet && window.addTokenToWallet(\'' + addrEsc + '\', \'' + symbolEsc + '\', ' + decimals + ');" aria-label="Add to wallet" title="Add to wallet"><i class="fas fa-wallet" aria-hidden="true"></i></button></div></div>';
html += '<div class="info-row"><div class="info-label">Name</div><div class="info-value">' + escapeHtml(name) + '</div></div>';
html += '<div class="info-row"><div class="info-label">Symbol</div><div class="info-value">' + escapeHtml(symbol) + '</div></div>';
html += '<div class="info-row"><div class="info-label">Decimals</div><div class="info-value">' + decimals + '</div></div>';
@@ -5409,7 +5427,7 @@
var dec = tr.token?.decimals != null ? tr.token.decimals : decimals;
var v = Number(val) / Math.pow(10, parseInt(dec, 10));
var txHash = tr.transaction_hash || tr.tx_hash || '';
html += '<tr><td class="hash" onclick="showAddressDetail(\'' + escapeHtml(from) + '\')" style="cursor: pointer;">' + escapeHtml(shortenHash(from)) + '</td><td class="hash" onclick="showAddressDetail(\'' + escapeHtml(to) + '\')" style="cursor: pointer;">' + escapeHtml(shortenHash(to)) + '</td><td>' + escapeHtml(v.toLocaleString(undefined, { maximumFractionDigits: 6 })) + '</td><td class="hash" onclick="showTransactionDetail(\'' + escapeHtml(txHash) + '\')" style="cursor: pointer;">' + (txHash ? escapeHtml(shortenHash(txHash)) : '-') + '</td></tr>';
html += '<tr><td>' + explorerAddressLink(from, escapeHtml(shortenHash(from)), 'color: inherit; text-decoration: none;') + '</td><td>' + explorerAddressLink(to, escapeHtml(shortenHash(to)), 'color: inherit; text-decoration: none;') + '</td><td>' + escapeHtml(v.toLocaleString(undefined, { maximumFractionDigits: 6 })) + '</td><td>' + (txHash ? explorerTransactionLink(txHash, escapeHtml(shortenHash(txHash)), 'color: inherit; text-decoration: none;') : '-') + '</td></tr>';
});
if (filteredTransfers.length === 0) {
html += '<tr><td colspan="4" style="text-align:center; padding: 1rem;">No transfers match the current filter.</td></tr>';
@@ -5442,7 +5460,7 @@
if (r) { data = r; break; }
} catch (e) {}
}
var html = '<div class="info-row"><div class="info-label">Contract</div><div class="info-value hash" onclick="showAddressDetail(\'' + escapeHtml(contractAddress) + '\')" style="cursor: pointer;">' + escapeHtml(contractAddress) + '</div></div>';
var html = '<div class="info-row"><div class="info-label">Contract</div><div class="info-value">' + explorerAddressLink(contractAddress, escapeHtml(contractAddress), 'color: inherit; text-decoration: none;') + '</div></div>';
html += '<div class="info-row"><div class="info-label">Token ID</div><div class="info-value">' + escapeHtml(String(tokenId)) + '</div></div>';
if (data) {
if (data.metadata && data.metadata.image) {
@@ -5450,7 +5468,7 @@
}
if (data.name) html += '<div class="info-row"><div class="info-label">Name</div><div class="info-value">' + escapeHtml(data.name) + '</div></div>';
if (data.description) html += '<div class="info-row"><div class="info-label">Description</div><div class="info-value">' + escapeHtml(data.description) + '</div></div>';
if (data.owner) { var ownerAddr = (data.owner.hash || data.owner); html += '<div class="info-row"><div class="info-label">Owner</div><div class="info-value hash" onclick="showAddressDetail(\'' + escapeHtml(ownerAddr) + '\')" style="cursor: pointer;">' + escapeHtml(ownerAddr) + '</div></div>'; }
if (data.owner) { var ownerAddr = (data.owner.hash || data.owner); html += '<div class="info-row"><div class="info-label">Owner</div><div class="info-value">' + explorerAddressLink(ownerAddr, escapeHtml(ownerAddr), 'color: inherit; text-decoration: none;') + '</div></div>'; }
if (data.metadata && data.metadata.attributes && Array.isArray(data.metadata.attributes)) {
html += '<div class="info-row"><div class="info-label">Traits</div><div class="info-value"><div style="display: flex; flex-wrap: wrap; gap: 0.5rem;">';
data.metadata.attributes.forEach(function(attr) {
@@ -5488,20 +5506,20 @@
if (item.address_hash || item.hash) {
addr = item.address_hash || item.hash;
if (/^0x[a-f0-9]{40}$/i.test(addr)) {
html += '<tr style="cursor: pointer;" onclick="showAddressDetail(\'' + escapeHtml(addr) + '\')"><td>Address</td><td class="hash">' + escapeHtml(shortenHash(addr)) + '</td></tr>';
html += '<tr><td>Address</td><td>' + explorerAddressLink(addr, escapeHtml(shortenHash(addr)), 'color: inherit; text-decoration: none;') + '</td></tr>';
return;
}
}
if (item.tx_hash || (item.hash && item.hash.length === 66)) {
txHash = item.tx_hash || item.hash;
if (/^0x[a-f0-9]{64}$/i.test(txHash)) {
html += '<tr style="cursor: pointer;" onclick="showTransactionDetail(\'' + escapeHtml(txHash) + '\')"><td>Transaction</td><td class="hash">' + escapeHtml(shortenHash(txHash)) + '</td></tr>';
html += '<tr><td>Transaction</td><td>' + explorerTransactionLink(txHash, escapeHtml(shortenHash(txHash)), 'color: inherit; text-decoration: none;') + '</td></tr>';
return;
}
}
if (item.block_number != null) {
blockNum = String(item.block_number);
html += '<tr style="cursor: pointer;" onclick="showBlockDetail(\'' + escapeHtml(blockNum) + '\')"><td>Block</td><td>#' + escapeHtml(blockNum) + '</td></tr>';
html += '<tr><td>Block</td><td>' + explorerBlockLink(blockNum, '#' + escapeHtml(blockNum), 'color: inherit; text-decoration: none;') + '</td></tr>';
return;
}
html += '<tr><td>' + escapeHtml(type || 'Unknown') + '</td><td>' + escapeHtml(String(label).substring(0, 80)) + '</td></tr>';

View File

@@ -1200,7 +1200,7 @@
<div class="weth-card">
<div class="chain-name">WETH9 Token</div>
<div style="color: var(--text-light); margin-bottom: 1rem;">
Contract: <span class="hash" onclick="showAddressDetail('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2')" style="cursor: pointer;">0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2</span>
Contract: <a class="hash" href="/address/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" onclick="event.preventDefault(); showAddressDetail('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2')" style="color: inherit; text-decoration: none;">0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2</a>
<button type="button" class="btn-add-token-wallet" onclick="event.stopPropagation(); window.addTokenToWallet && window.addTokenToWallet('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', 'WETH', 18, 'Wrapped Ether');" aria-label="Add WETH9 to wallet" title="Add to wallet"><i class="fas fa-wallet" aria-hidden="true"></i></button>
</div>
@@ -1250,7 +1250,7 @@
<div class="weth-card">
<div class="chain-name">WETH10 Token</div>
<div style="color: var(--text-light); margin-bottom: 1rem;">
Contract: <span class="hash" onclick="showAddressDetail('0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f')" style="cursor: pointer;">0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f</span>
Contract: <a class="hash" href="/address/0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f" onclick="event.preventDefault(); showAddressDetail('0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f')" style="color: inherit; text-decoration: none;">0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f</a>
<button type="button" class="btn-add-token-wallet" onclick="event.stopPropagation(); window.addTokenToWallet && window.addTokenToWallet('0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f', 'WETH', 18, 'Wrapped Ether v10');" aria-label="Add WETH10 to wallet" title="Add to wallet"><i class="fas fa-wallet" aria-hidden="true"></i></button>
</div>
@@ -1307,8 +1307,8 @@
<h4 style="margin-top: 1.5rem; margin-bottom: 0.5rem;">Contract Addresses</h4>
<ul style="margin-left: 2rem; margin-top: 0.5rem;">
<li><strong>WETH9:</strong> <span class="hash" onclick="showAddressDetail('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2')" style="cursor: pointer;">0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2</span> <button type="button" class="btn-add-token-wallet" onclick="event.stopPropagation(); window.addTokenToWallet && window.addTokenToWallet('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', 'WETH', 18);" aria-label="Add WETH9 to wallet" title="Add to wallet"><i class="fas fa-wallet"></i></button></li>
<li><strong>WETH10:</strong> <span class="hash" onclick="showAddressDetail('0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f')" style="cursor: pointer;">0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f</span> <button type="button" class="btn-add-token-wallet" onclick="event.stopPropagation(); window.addTokenToWallet && window.addTokenToWallet('0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f', 'WETH', 18);" aria-label="Add WETH10 to wallet" title="Add to wallet"><i class="fas fa-wallet"></i></button></li>
<li><strong>WETH9:</strong> <a class="hash" href="/address/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" onclick="event.preventDefault(); showAddressDetail('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2')" style="color: inherit; text-decoration: none;">0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2</a> <button type="button" class="btn-add-token-wallet" onclick="event.stopPropagation(); window.addTokenToWallet && window.addTokenToWallet('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', 'WETH', 18);" aria-label="Add WETH9 to wallet" title="Add to wallet"><i class="fas fa-wallet"></i></button></li>
<li><strong>WETH10:</strong> <a class="hash" href="/address/0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f" onclick="event.preventDefault(); showAddressDetail('0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f')" style="color: inherit; text-decoration: none;">0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f</a> <button type="button" class="btn-add-token-wallet" onclick="event.stopPropagation(); window.addTokenToWallet && window.addTokenToWallet('0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f', 'WETH', 18);" aria-label="Add WETH10 to wallet" title="Add to wallet"><i class="fas fa-wallet"></i></button></li>
</ul>
<h4 style="margin-top: 1.5rem; margin-bottom: 0.5rem;">How to Use</h4>