SC CODE: Function InitializePrivate() Uint64
10 IF init() == 0 THEN GOTO 30
20 RETURN 1
30 STORE("var_header_name", "transactions.js")
31 STORE("var_header_description", "")
32 STORE("var_header_icon", "")
33 STORE("dURL", "")
34 STORE("docType", "TELA-JS-1")
35 STORE("subDir", "/")
36 STORE("fileCheckC", "25563bffd1ed45ffb6fd7a6d32c806e2bb1569598fa56a34abe44bdfb81110a7")
37 STORE("fileCheckS", "20a220f430bfb6bd0c6075965c19d598232817c1e043be0c5431efa5a3f82e6b")
100 RETURN 0
End Function
Function init() Uint64
10 IF EXISTS("owner") == 0 THEN GOTO 30
20 RETURN 1
30 STORE("owner", address())
50 STORE("docVersion", "1.0.0")
60 STORE("hash", HEX(TXID()))
70 STORE("likes", 0)
80 STORE("dislikes", 0)
100 RETURN 0
End Function
Function address() String
10 DIM s as String
20 LET s = SIGNER()
30 IF IS_ADDRESS_VALID(s) THEN GOTO 50
40 RETURN "anon"
50 RETURN ADDRESS_STRING(s)
End Function
Function Rate(r Uint64) Uint64
10 DIM addr as String
15 LET addr = address()
16 IF r < 100 && EXISTS(addr) == 0 && addr != "anon" THEN GOTO 30
20 RETURN 1
30 STORE(addr, ""+r+"_"+BLOCK_HEIGHT())
40 IF r < 50 THEN GOTO 70
50 STORE("likes", LOAD("likes")+1)
60 RETURN 0
70 STORE("dislikes", LOAD("dislikes")+1)
100 RETURN 0
End Function
/*
// DERO Explorer v2.0 - Transactions Module
const module = (function() {
'use strict';
var D = window.DeroExplorer, S = D.state, U = D.utils, ui = D.ui, R = D.router, M = D.modules;
var TX = { PREMINE: 'PREMINE', REGISTRATION: 'REGISTRATION', COINBASE: 'COINBASE', NORMAL: 'NORMAL', BURN_TX: 'BURN_TX', SC_TX: 'SC_TX' };
var T = { poolInterval: null, cache: new Map(), maxCache: 50 };
function init() {
window.addEventListener('explorer:viewChange', handleView);
window.addEventListener('explorer:connected', function() { if (S.currentView === 'dashboard') loadMempool(); });
}
function handleView(e) {
var v = e.detail.view, p = e.detail.param;
if (v === 'pool') renderPoolView();
else if (v === 'txDetail' && p) renderTxDetail(p);
else if (v === 'dashboard') loadMempool();
if (v !== 'pool' && T.poolInterval) { clearInterval(T.poolInterval); T.poolInterval = null; }
}
function detectType(tx) {
if (!tx) return 'UNKNOWN';
if (tx.transaction_type) return tx.transaction_type.toUpperCase();
if (tx.coinbase) return TX.COINBASE;
if (tx.sc_data || tx.scdata) return TX.SC_TX;
if (tx.burn_value && tx.burn_value > 0) return TX.BURN_TX;
return TX.NORMAL;
}
function typeBadge(t) {
if (t === TX.COINBASE) return 'badge-ok';
if (t === TX.SC_TX) return 'badge-live';
if (t === TX.BURN_TX) return 'badge-err';
if (t === TX.REGISTRATION) return 'badge-new';
return 'badge-pending';
}
async function loadMempool() {
var el = document.getElementById('mempool-list'), badge = document.getElementById('pool-badge');
if (!el) return;
try {
var conn = M.get('connectivity');
if (!conn) throw new Error('No connectivity');
var pool = await conn.getTxPool(), txs = pool.txs || [];
if (badge) badge.textContent = txs.length + ' PENDING';
if (txs.length === 0) { el.innerHTML = '<div class="empty-state"><div class="empty-icon">*</div><p>Mempool is empty - all transactions confirmed</p></div>'; return; }
el.innerHTML = renderPoolList(txs.slice(0, 10));
} catch (e) {}
}
async function renderPoolView() {
var el = document.getElementById('view-pool');
if (!el) return;
el.innerHTML = '<div class="section-card">' + ui.sectionHeader({ icon: '[T]', title: 'Transaction Pool (Mempool)', badge: { text: 'LIVE', class: 'badge-live' } }) +
'<div class="section-content"><div id="pool-full-list"><div class="loading-state"><div class="loading-spinner"></div><span>Loading mempool...</span></div></div></div></div>';
await loadFullPool();
T.poolInterval = setInterval(loadFullPool, 10000);
}
async function loadFullPool() {
var el = document.getElementById('pool-full-list');
if (!el) return;
try {
var conn = M.get('connectivity'), pool = await conn.getTxPool(), txs = pool.txs || [];
if (txs.length === 0) { el.innerHTML = '<div class="empty-state"><div class="empty-icon">*</div><p>Mempool is empty</p><p class="text-muted">All transactions have been confirmed</p></div>'; return; }
el.innerHTML = '<div class="pool-stats"><span class="pool-stat"><strong>' + txs.length + '</strong> pending transactions</span></div>' + renderPoolList(txs);
} catch (e) { el.innerHTML = '<div class="empty-state"><p>Failed to load mempool</p></div>'; }
}
function renderPoolList(txs) {
return '<div class="tx-list">' + txs.map(function(h) {
return '<div class="tx-item" onclick="viewTx(\'' + h + '\')"><div class="tx-item-left"><span class="tx-type badge badge-pending">PENDING</span><span class="tx-hash mono">' + U.truncHash(h, 16, 12) + '</span></div><div class="tx-item-right"><span class="tx-action">View ></span></div></div>';
}).join('') + '</div>';
}
async function renderTxDetail(txid) {
var el = document.getElementById('view-txDetail');
if (!el) { var main = document.getElementById('main-content'); el = document.createElement('div'); el.id = 'view-txDetail'; main.appendChild(el); }
el.classList.remove('hidden');
ui.showLoading(el, 'Loading transaction...');
try {
var conn = M.get('connectivity'), tx;
if (conn.hasEnhancedFeatures()) tx = await conn.getTransactionWithRings(txid);
else tx = await conn.getTransaction(txid);
if (!tx) throw new Error('Transaction not found');
el.innerHTML = renderTxHTML(tx, conn.hasEnhancedFeatures());
} catch (e) { ui.showError(el, 'Transaction Not Found', e.message); }
}
function renderTxHTML(tx, enhanced) {
var type = detectType(tx), cls = typeBadge(type), conf = tx.block_height && tx.block_height > 0;
return '<div class="detail-view"><div class="detail-nav"><button class="btn btn-ghost" onclick="goBack()">< Back</button></div>' +
'<div class="section-card">' + ui.sectionHeader({ icon: '[T]', title: 'Transaction', badge: { text: conf ? 'CONFIRMED' : 'PENDING', class: conf ? 'badge-ok' : 'badge-pending' } }) +
'<div class="section-content"><div class="detail-grid"><div class="detail-column"><h4 class="detail-subtitle">Transaction Information</h4>' +
ui.detailRow('TXID', tx.txid || tx.hash, { mono: true, copyable: true }) +
ui.detailRow('Type', '<span class="badge ' + cls + '">' + type + '</span>') +
(tx.block_height ? ui.detailRow('Block Height', U.fmtNum(tx.block_height)) : '') +
(tx.block_timestamp ? ui.detailRow('Time', U.fmtLocalTime(tx.block_timestamp)) : '') +
(tx.block_timestamp ? ui.detailRow('Age', U.fmtAge(tx.block_timestamp)) : '') +
'</div><div class="detail-column"><h4 class="detail-subtitle">Transaction Data</h4>' +
(tx.size ? ui.detailRow('Size', U.fmtBytes(tx.size)) : '') +
(tx.fee !== undefined ? ui.detailRow('Fee', U.fmtDERO(tx.fee) + ' DERO') : '') +
(tx.burn_value ? ui.detailRow('Burn', U.fmtDERO(tx.burn_value) + ' DERO') : '') +
(tx.ring_size ? ui.detailRow('Ring Size', tx.ring_size.toString()) : '') +
(tx.dest_port ? ui.detailRow('Dest Port', tx.dest_port.toString()) : '') +
'</div></div></div></div>' +
(type === TX.SC_TX ? renderSC(tx) : '') + renderRing(tx, enhanced) + renderProof(tx.txid || tx.hash, enhanced) + '</div>';
}
function renderSC(tx) {
var scid = tx.scid || extractSCID(tx), ep = tx.entrypoint || extractEP(tx);
if (!scid) return '';
return '<div class="section-card">' + ui.sectionHeader({ icon: '[SC]', title: 'Smart Contract Interaction' }) +
'<div class="section-content">' + ui.detailRow('SCID', scid, { mono: true, copyable: true }) +
(ep ? ui.detailRow('Function', ep) : '') +
(tx.sc_args ? '<div style="margin-top:var(--s-4)"><h4 class="detail-subtitle">Arguments</h4><div class="sc-args">' + renderArgs(tx.sc_args) + '</div></div>' : '') +
'<div style="margin-top:var(--s-4)"><button class="btn btn-ghost" onclick="viewSC(\'' + scid + '\')">View Contract ></button></div></div></div>';
}
function extractSCID(tx) {
if (tx.scid) return tx.scid;
if (tx.sc_args) { var a = tx.sc_args.find(function(x) { return x.name && (x.name.toLowerCase() === 'sc' || x.name.toLowerCase() === 'scid'); }); if (a) return a.value; }
return null;
}
function extractEP(tx) {
if (tx.entrypoint) return tx.entrypoint;
if (tx.sc_args) { var a = tx.sc_args.find(function(x) { return x.name && (x.name.toLowerCase() === 'entrypoint' || x.name.toLowerCase() === 'function'); }); if (a) return a.value; }
return null;
}
function renderArgs(args) {
if (!args || !args.length) return '<span class="text-muted">No arguments</span>';
return '<div class="args-list">' + args.map(function(a) {
return '<div class="arg-item"><span class="arg-name">' + U.escHtml(a.name || 'unnamed') + '</span><span class="arg-type">' + (a.datatype || a.type || '?') + '</span><span class="arg-value mono">' + U.escHtml(String(a.value || '')) + '</span></div>';
}).join('') + '</div>';
}
function renderRing(tx, enhanced) {
var ring = tx.ring || extractRing(tx), size = tx.ring_size || (ring ? ring.length : 0);
if (!ring || ring.length === 0) {
if (size > 0) return '<div class="section-card">' + ui.sectionHeader({ icon: '[R]', title: 'Ring Signature', badge: { text: 'SIZE ' + size, class: 'badge-pending' } }) +
'<div class="section-content"><p class="text-muted text-sm">Ring size: ' + size + ' members. Ring data not included in this transaction response.</p><p class="text-muted text-sm" style="margin-top:var(--s-2)">Ring signatures provide transaction privacy by mixing your input with decoy inputs from other transactions.</p></div></div>';
return '';
}
return '<div class="section-card">' + ui.sectionHeader({ icon: '[R]', title: 'Ring Members (' + ring.length + ')', badge: { text: 'PRIVACY', class: 'badge-ok' } }) +
'<div class="section-content"><p class="ring-explanation text-muted text-sm" style="margin-bottom:var(--s-4)">Ring signatures provide transaction privacy by mixing your input with ' + (ring.length - 1) + ' decoy inputs from other transactions.</p>' + renderRingMembers(ring) + '</div></div>';
}
function extractRing(tx) {
if (tx.ring) return tx.ring;
if (tx.payloads && tx.payloads[0]?.ring) return tx.payloads[0].ring;
return null;
}
function renderRingMembers(ring) {
var max = 16, disp = ring.slice(0, max), rem = ring.length - max;
var html = '<div class="ring-members">';
disp.forEach(function(m, i) {
var d = Array.isArray(m) ? m[0] : m;
html += '<div class="ring-member"><span class="ring-index">[' + i + ']</span><code class="ring-key">' + U.truncHash(d, 12, 8) + '</code></div>';
});
if (rem > 0) html += '<div class="ring-more text-muted">... and ' + rem + ' more members</div>';
return html + '</div>';
}
function renderProof(txid, enhanced) {
var id = txid.slice(0, 8);
if (!enhanced) return '<div class="section-card">' + ui.sectionHeader({ icon: '[P]', title: 'Verify Payment Proof', badge: { text: 'BACKEND REQUIRED', class: 'badge-pending' } }) +
'<div class="section-content"><div class="proof-limited"><p class="text-muted">Proof validation requires a backend that supports cryptographic verification (Go native code). This is available when running the explorer in HOLOGRAM or with a local explorer that supports proof validation.</p></div></div></div>';
return '<div class="section-card">' + ui.sectionHeader({ icon: '[P]', title: 'Verify Payment Proof' }) +
'<div class="section-content"><p class="text-muted text-sm" style="margin-bottom:var(--s-4)">Enter a DERO proof to verify if this transaction sent funds to you.</p>' +
'<div class="proof-form"><textarea id="proof-input-' + id + '" class="proof-textarea" placeholder="Paste deroproof1q... here" rows="4"></textarea>' +
'<button class="btn btn-primary" onclick="DeroExplorer.modules.get(\'transactions\').validateProof(\'' + txid + '\')">Validate Proof</button></div>' +
'<div id="proof-result-' + id + '" style="margin-top:var(--s-4)"></div></div></div>';
}
async function validateProof(txid) {
var id = txid.slice(0, 8), inp = document.getElementById('proof-input-' + id), res = document.getElementById('proof-result-' + id);
if (!inp || !res) return;
var proof = inp.value.trim();
if (!proof) { res.innerHTML = '<div class="proof-error">Please enter a proof string</div>'; return; }
res.innerHTML = '<div class="proof-loading"><div class="loading-spinner" style="width:16px;height:16px"></div>Validating...</div>';
try {
var conn = M.get('connectivity'), r = await conn.validateProof(proof, txid);
if (r.valid) res.innerHTML = '<div class="proof-valid"><div class="proof-header">[OK] Proof Valid</div><div class="proof-details">' + ui.detailRow('Recipient', U.truncHash(r.address, 16, 12), { mono: true }) + ui.detailRow('Amount', U.fmtDERO(r.amount) + ' DERO') + (r.payload ? ui.detailRow('Payload', r.payload) : '') + '</div></div>';
else res.innerHTML = '<div class="proof-invalid"><div class="proof-header">[X] Proof Invalid</div><p>' + (r.error || 'The proof could not be validated for this transaction.') + '</p></div>';
} catch (e) { res.innerHTML = '<div class="proof-error">Error: ' + U.escHtml(e.message) + '</div>'; }
}
return { init: init, loadMempool: loadMempool, loadFullPool: loadFullPool, renderTxDetail: renderTxDetail, validateProof: validateProof, detectTxType: detectType, TX_TYPES: TX };
})();
*/ |
| SC Arguments: [Name:SC_ACTION Type:uint64 Value:'1' Name:SC_CODE Type:string Value:'Function InitializePrivate() Uint64
10 IF init() == 0 THEN GOTO 30
20 RETURN 1
30 STORE("var_header_name", "transactions.js")
31 STORE("var_header_description", "")
32 STORE("var_header_icon", "")
33 STORE("dURL", "")
34 STORE("docType", "TELA-JS-1")
35 STORE("subDir", "/")
36 STORE("fileCheckC", "25563bffd1ed45ffb6fd7a6d32c806e2bb1569598fa56a34abe44bdfb81110a7")
37 STORE("fileCheckS", "20a220f430bfb6bd0c6075965c19d598232817c1e043be0c5431efa5a3f82e6b")
100 RETURN 0
End Function
Function init() Uint64
10 IF EXISTS("owner") == 0 THEN GOTO 30
20 RETURN 1
30 STORE("owner", address())
50 STORE("docVersion", "1.0.0")
60 STORE("hash", HEX(TXID()))
70 STORE("likes", 0)
80 STORE("dislikes", 0)
100 RETURN 0
End Function
Function address() String
10 DIM s as String
20 LET s = SIGNER()
30 IF IS_ADDRESS_VALID(s) THEN GOTO 50
40 RETURN "anon"
50 RETURN ADDRESS_STRING(s)
End Function
Function Rate(r Uint64) Uint64
10 DIM addr as String
15 LET addr = address()
16 IF r < 100 && EXISTS(addr) == 0 && addr != "anon" THEN GOTO 30
20 RETURN 1
30 STORE(addr, ""+r+"_"+BLOCK_HEIGHT())
40 IF r < 50 THEN GOTO 70
50 STORE("likes", LOAD("likes")+1)
60 RETURN 0
70 STORE("dislikes", LOAD("dislikes")+1)
100 RETURN 0
End Function
/*
// DERO Explorer v2.0 - Transactions Module
const module = (function() {
'use strict';
var D = window.DeroExplorer, S = D.state, U = D.utils, ui = D.ui, R = D.router, M = D.modules;
var TX = { PREMINE: 'PREMINE', REGISTRATION: 'REGISTRATION', COINBASE: 'COINBASE', NORMAL: 'NORMAL', BURN_TX: 'BURN_TX', SC_TX: 'SC_TX' };
var T = { poolInterval: null, cache: new Map(), maxCache: 50 };
function init() {
window.addEventListener('explorer:viewChange', handleView);
window.addEventListener('explorer:connected', function() { if (S.currentView === 'dashboard') loadMempool(); });
}
function handleView(e) {
var v = e.detail.view, p = e.detail.param;
if (v === 'pool') renderPoolView();
else if (v === 'txDetail' && p) renderTxDetail(p);
else if (v === 'dashboard') loadMempool();
if (v !== 'pool' && T.poolInterval) { clearInterval(T.poolInterval); T.poolInterval = null; }
}
function detectType(tx) {
if (!tx) return 'UNKNOWN';
if (tx.transaction_type) return tx.transaction_type.toUpperCase();
if (tx.coinbase) return TX.COINBASE;
if (tx.sc_data || tx.scdata) return TX.SC_TX;
if (tx.burn_value && tx.burn_value > 0) return TX.BURN_TX;
return TX.NORMAL;
}
function typeBadge(t) {
if (t === TX.COINBASE) return 'badge-ok';
if (t === TX.SC_TX) return 'badge-live';
if (t === TX.BURN_TX) return 'badge-err';
if (t === TX.REGISTRATION) return 'badge-new';
return 'badge-pending';
}
async function loadMempool() {
var el = document.getElementById('mempool-list'), badge = document.getElementById('pool-badge');
if (!el) return;
try {
var conn = M.get('connectivity');
if (!conn) throw new Error('No connectivity');
var pool = await conn.getTxPool(), txs = pool.txs || [];
if (badge) badge.textContent = txs.length + ' PENDING';
if (txs.length === 0) { el.innerHTML = '<div class="empty-state"><div class="empty-icon">*</div><p>Mempool is empty - all transactions confirmed</p></div>'; return; }
el.innerHTML = renderPoolList(txs.slice(0, 10));
} catch (e) {}
}
async function renderPoolView() {
var el = document.getElementById('view-pool');
if (!el) return;
el.innerHTML = '<div class="section-card">' + ui.sectionHeader({ icon: '[T]', title: 'Transaction Pool (Mempool)', badge: { text: 'LIVE', class: 'badge-live' } }) +
'<div class="section-content"><div id="pool-full-list"><div class="loading-state"><div class="loading-spinner"></div><span>Loading mempool...</span></div></div></div></div>';
await loadFullPool();
T.poolInterval = setInterval(loadFullPool, 10000);
}
async function loadFullPool() {
var el = document.getElementById('pool-full-list');
if (!el) return;
try {
var conn = M.get('connectivity'), pool = await conn.getTxPool(), txs = pool.txs || [];
if (txs.length === 0) { el.innerHTML = '<div class="empty-state"><div class="empty-icon">*</div><p>Mempool is empty</p><p class="text-muted">All transactions have been confirmed</p></div>'; return; }
el.innerHTML = '<div class="pool-stats"><span class="pool-stat"><strong>' + txs.length + '</strong> pending transactions</span></div>' + renderPoolList(txs);
} catch (e) { el.innerHTML = '<div class="empty-state"><p>Failed to load mempool</p></div>'; }
}
function renderPoolList(txs) {
return '<div class="tx-list">' + txs.map(function(h) {
return '<div class="tx-item" onclick="viewTx(\'' + h + '\')"><div class="tx-item-left"><span class="tx-type badge badge-pending">PENDING</span><span class="tx-hash mono">' + U.truncHash(h, 16, 12) + '</span></div><div class="tx-item-right"><span class="tx-action">View ></span></div></div>';
}).join('') + '</div>';
}
async function renderTxDetail(txid) {
var el = document.getElementById('view-txDetail');
if (!el) { var main = document.getElementById('main-content'); el = document.createElement('div'); el.id = 'view-txDetail'; main.appendChild(el); }
el.classList.remove('hidden');
ui.showLoading(el, 'Loading transaction...');
try {
var conn = M.get('connectivity'), tx;
if (conn.hasEnhancedFeatures()) tx = await conn.getTransactionWithRings(txid);
else tx = await conn.getTransaction(txid);
if (!tx) throw new Error('Transaction not found');
el.innerHTML = renderTxHTML(tx, conn.hasEnhancedFeatures());
} catch (e) { ui.showError(el, 'Transaction Not Found', e.message); }
}
function renderTxHTML(tx, enhanced) {
var type = detectType(tx), cls = typeBadge(type), conf = tx.block_height && tx.block_height > 0;
return '<div class="detail-view"><div class="detail-nav"><button class="btn btn-ghost" onclick="goBack()">< Back</button></div>' +
'<div class="section-card">' + ui.sectionHeader({ icon: '[T]', title: 'Transaction', badge: { text: conf ? 'CONFIRMED' : 'PENDING', class: conf ? 'badge-ok' : 'badge-pending' } }) +
'<div class="section-content"><div class="detail-grid"><div class="detail-column"><h4 class="detail-subtitle">Transaction Information</h4>' +
ui.detailRow('TXID', tx.txid || tx.hash, { mono: true, copyable: true }) +
ui.detailRow('Type', '<span class="badge ' + cls + '">' + type + '</span>') +
(tx.block_height ? ui.detailRow('Block Height', U.fmtNum(tx.block_height)) : '') +
(tx.block_timestamp ? ui.detailRow('Time', U.fmtLocalTime(tx.block_timestamp)) : '') +
(tx.block_timestamp ? ui.detailRow('Age', U.fmtAge(tx.block_timestamp)) : '') +
'</div><div class="detail-column"><h4 class="detail-subtitle">Transaction Data</h4>' +
(tx.size ? ui.detailRow('Size', U.fmtBytes(tx.size)) : '') +
(tx.fee !== undefined ? ui.detailRow('Fee', U.fmtDERO(tx.fee) + ' DERO') : '') +
(tx.burn_value ? ui.detailRow('Burn', U.fmtDERO(tx.burn_value) + ' DERO') : '') +
(tx.ring_size ? ui.detailRow('Ring Size', tx.ring_size.toString()) : '') +
(tx.dest_port ? ui.detailRow('Dest Port', tx.dest_port.toString()) : '') +
'</div></div></div></div>' +
(type === TX.SC_TX ? renderSC(tx) : '') + renderRing(tx, enhanced) + renderProof(tx.txid || tx.hash, enhanced) + '</div>';
}
function renderSC(tx) {
var scid = tx.scid || extractSCID(tx), ep = tx.entrypoint || extractEP(tx);
if (!scid) return '';
return '<div class="section-card">' + ui.sectionHeader({ icon: '[SC]', title: 'Smart Contract Interaction' }) +
'<div class="section-content">' + ui.detailRow('SCID', scid, { mono: true, copyable: true }) +
(ep ? ui.detailRow('Function', ep) : '') +
(tx.sc_args ? '<div style="margin-top:var(--s-4)"><h4 class="detail-subtitle">Arguments</h4><div class="sc-args">' + renderArgs(tx.sc_args) + '</div></div>' : '') +
'<div style="margin-top:var(--s-4)"><button class="btn btn-ghost" onclick="viewSC(\'' + scid + '\')">View Contract ></button></div></div></div>';
}
function extractSCID(tx) {
if (tx.scid) return tx.scid;
if (tx.sc_args) { var a = tx.sc_args.find(function(x) { return x.name && (x.name.toLowerCase() === 'sc' || x.name.toLowerCase() === 'scid'); }); if (a) return a.value; }
return null;
}
function extractEP(tx) {
if (tx.entrypoint) return tx.entrypoint;
if (tx.sc_args) { var a = tx.sc_args.find(function(x) { return x.name && (x.name.toLowerCase() === 'entrypoint' || x.name.toLowerCase() === 'function'); }); if (a) return a.value; }
return null;
}
function renderArgs(args) {
if (!args || !args.length) return '<span class="text-muted">No arguments</span>';
return '<div class="args-list">' + args.map(function(a) {
return '<div class="arg-item"><span class="arg-name">' + U.escHtml(a.name || 'unnamed') + '</span><span class="arg-type">' + (a.datatype || a.type || '?') + '</span><span class="arg-value mono">' + U.escHtml(String(a.value || '')) + '</span></div>';
}).join('') + '</div>';
}
function renderRing(tx, enhanced) {
var ring = tx.ring || extractRing(tx), size = tx.ring_size || (ring ? ring.length : 0);
if (!ring || ring.length === 0) {
if (size > 0) return '<div class="section-card">' + ui.sectionHeader({ icon: '[R]', title: 'Ring Signature', badge: { text: 'SIZE ' + size, class: 'badge-pending' } }) +
'<div class="section-content"><p class="text-muted text-sm">Ring size: ' + size + ' members. Ring data not included in this transaction response.</p><p class="text-muted text-sm" style="margin-top:var(--s-2)">Ring signatures provide transaction privacy by mixing your input with decoy inputs from other transactions.</p></div></div>';
return '';
}
return '<div class="section-card">' + ui.sectionHeader({ icon: '[R]', title: 'Ring Members (' + ring.length + ')', badge: { text: 'PRIVACY', class: 'badge-ok' } }) +
'<div class="section-content"><p class="ring-explanation text-muted text-sm" style="margin-bottom:var(--s-4)">Ring signatures provide transaction privacy by mixing your input with ' + (ring.length - 1) + ' decoy inputs from other transactions.</p>' + renderRingMembers(ring) + '</div></div>';
}
function extractRing(tx) {
if (tx.ring) return tx.ring;
if (tx.payloads && tx.payloads[0]?.ring) return tx.payloads[0].ring;
return null;
}
function renderRingMembers(ring) {
var max = 16, disp = ring.slice(0, max), rem = ring.length - max;
var html = '<div class="ring-members">';
disp.forEach(function(m, i) {
var d = Array.isArray(m) ? m[0] : m;
html += '<div class="ring-member"><span class="ring-index">[' + i + ']</span><code class="ring-key">' + U.truncHash(d, 12, 8) + '</code></div>';
});
if (rem > 0) html += '<div class="ring-more text-muted">... and ' + rem + ' more members</div>';
return html + '</div>';
}
function renderProof(txid, enhanced) {
var id = txid.slice(0, 8);
if (!enhanced) return '<div class="section-card">' + ui.sectionHeader({ icon: '[P]', title: 'Verify Payment Proof', badge: { text: 'BACKEND REQUIRED', class: 'badge-pending' } }) +
'<div class="section-content"><div class="proof-limited"><p class="text-muted">Proof validation requires a backend that supports cryptographic verification (Go native code). This is available when running the explorer in HOLOGRAM or with a local explorer that supports proof validation.</p></div></div></div>';
return '<div class="section-card">' + ui.sectionHeader({ icon: '[P]', title: 'Verify Payment Proof' }) +
'<div class="section-content"><p class="text-muted text-sm" style="margin-bottom:var(--s-4)">Enter a DERO proof to verify if this transaction sent funds to you.</p>' +
'<div class="proof-form"><textarea id="proof-input-' + id + '" class="proof-textarea" placeholder="Paste deroproof1q... here" rows="4"></textarea>' +
'<button class="btn btn-primary" onclick="DeroExplorer.modules.get(\'transactions\').validateProof(\'' + txid + '\')">Validate Proof</button></div>' +
'<div id="proof-result-' + id + '" style="margin-top:var(--s-4)"></div></div></div>';
}
async function validateProof(txid) {
var id = txid.slice(0, 8), inp = document.getElementById('proof-input-' + id), res = document.getElementById('proof-result-' + id);
if (!inp || !res) return;
var proof = inp.value.trim();
if (!proof) { res.innerHTML = '<div class="proof-error">Please enter a proof string</div>'; return; }
res.innerHTML = '<div class="proof-loading"><div class="loading-spinner" style="width:16px;height:16px"></div>Validating...</div>';
try {
var conn = M.get('connectivity'), r = await conn.validateProof(proof, txid);
if (r.valid) res.innerHTML = '<div class="proof-valid"><div class="proof-header">[OK] Proof Valid</div><div class="proof-details">' + ui.detailRow('Recipient', U.truncHash(r.address, 16, 12), { mono: true }) + ui.detailRow('Amount', U.fmtDERO(r.amount) + ' DERO') + (r.payload ? ui.detailRow('Payload', r.payload) : '') + '</div></div>';
else res.innerHTML = '<div class="proof-invalid"><div class="proof-header">[X] Proof Invalid</div><p>' + (r.error || 'The proof could not be validated for this transaction.') + '</p></div>';
} catch (e) { res.innerHTML = '<div class="proof-error">Error: ' + U.escHtml(e.message) + '</div>'; }
}
return { init: init, loadMempool: loadMempool, loadFullPool: loadFullPool, renderTxDetail: renderTxDetail, validateProof: validateProof, detectTxType: detectType, TX_TYPES: TX };
})();
*/'] |