mirror of
https://github.com/mempool/mempool.git
synced 2025-05-31 18:19:28 +02:00
Add draft sip tx page
This commit is contained in:
parent
6852319e4d
commit
96ba7d0656
@ -102,6 +102,61 @@ const routes = {
|
||||
}
|
||||
}
|
||||
},
|
||||
tx: {
|
||||
render: true,
|
||||
params: 1,
|
||||
getTitle(path) {
|
||||
return `Transaction: ${path[0]}`;
|
||||
},
|
||||
sip: {
|
||||
template: 'tx',
|
||||
async getData (params: string[]) {
|
||||
if (params?.length) {
|
||||
let txid = params[0];
|
||||
const [transaction, times, cpfp, rbf, outspends]: any[] = await Promise.all([
|
||||
sipFetchJSON(config.API.ESPLORA + `/tx/${txid}`),
|
||||
sipFetchJSON(config.API.MEMPOOL + `/transaction-times?txId[]=${txid}`),
|
||||
sipFetchJSON(config.API.MEMPOOL + `/cpfp/${txid}`),
|
||||
sipFetchJSON(config.API.MEMPOOL + `/tx/${txid}/rbf`),
|
||||
sipFetchJSON(config.API.MEMPOOL + `/outspends?txId[]=${txid}`),
|
||||
])
|
||||
const features = transaction ? {
|
||||
segwit: transaction.vin.some((v) => v.prevout && ['v0_p2wsh', 'v0_p2wpkh'].includes(v.prevout.scriptpubkey_type)),
|
||||
taproot: transaction.vin.some((v) => v.prevout && v.prevout.scriptpubkey_type === 'v1_p2tr'),
|
||||
rbf: transaction.vin.some((v) => v.sequence < 0xfffffffe),
|
||||
} : {};
|
||||
return {
|
||||
transaction,
|
||||
times,
|
||||
cpfp,
|
||||
rbf,
|
||||
outspends,
|
||||
features,
|
||||
hex2ascii: function(hex) {
|
||||
const opPush = hex.split(' ').filter((_, i, a) => i > 0 && /^OP_PUSH/.test(a[i - 1]));
|
||||
if (opPush[0]) {
|
||||
hex = opPush[0];
|
||||
}
|
||||
if (!hex) {
|
||||
return '';
|
||||
}
|
||||
const bytes: number[] = [];
|
||||
for (let i = 0; i < hex.length; i += 2) {
|
||||
bytes.push(parseInt(hex.substr(i, 2), 16));
|
||||
}
|
||||
return new TextDecoder('utf8').decode(Uint8Array.from(bytes)).replace(/\uFFFD/g, '').replace(/\\0/g, '');
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
routes: {
|
||||
push: {
|
||||
title: "Push Transaction",
|
||||
fallbackImg: '/resources/previews/tx-push.jpg',
|
||||
}
|
||||
}
|
||||
},
|
||||
enterprise: {
|
||||
title: "Mempool Enterprise",
|
||||
fallbackImg: '/resources/previews/enterprise.jpg',
|
||||
@ -177,19 +232,6 @@ const routes = {
|
||||
title: "Trademark Policy",
|
||||
fallbackImg: '/resources/previews/trademark-policy.jpg',
|
||||
},
|
||||
tx: {
|
||||
render: true,
|
||||
params: 1,
|
||||
getTitle(path) {
|
||||
return `Transaction: ${path[0]}`;
|
||||
},
|
||||
routes: {
|
||||
push: {
|
||||
title: "Push Transaction",
|
||||
fallbackImg: '/resources/previews/tx-push.jpg',
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const networks = {
|
||||
|
@ -23,5 +23,8 @@
|
||||
<style type="text/css">
|
||||
body { padding: 0; margin: 0; }
|
||||
.main { padding: 0 20px; }
|
||||
.flag, .flex { display: flex; }
|
||||
.flag .yes { color: green; }
|
||||
.flag .no { color: red; }
|
||||
</style>
|
||||
</head>
|
126
unfurler/views/tx.ejs
Normal file
126
unfurler/views/tx.ejs
Normal file
@ -0,0 +1,126 @@
|
||||
<!doctype html>
|
||||
<html lang="en-US" dir="ltr">
|
||||
<%- include('head'); %>
|
||||
<body>
|
||||
<%- include('header'); %>
|
||||
<div class="main">
|
||||
<h1>Transaction <%- data.transaction.txid %></h1>
|
||||
<% if (data.transaction.status.confirmed) { %>
|
||||
<p>confirmed in <a href="/block/<%= data.transaction.status.block_hash %>">block <%= data.transaction.status.block_height %></a></p>
|
||||
<% } %>
|
||||
<h2>Summary</h2>
|
||||
<table>
|
||||
<% if (data.transaction.status.confirmed) { %>
|
||||
<tr>
|
||||
<td>Timestamp</td>
|
||||
<td><%= (new Date(data.transaction.status.block_time * 1000)).toISOString() %></td>
|
||||
</tr>
|
||||
<% } else { %>
|
||||
<tr>
|
||||
<td>First seen</td>
|
||||
<td><%= (new Date(data.times[0] * 1000)).toISOString() %></td>
|
||||
</tr>
|
||||
<% } %>
|
||||
<tr>
|
||||
<td>Features</td>
|
||||
<td>
|
||||
<dl>
|
||||
<div class="flag"><dt>SegWit</dt><dd class="<%= data.features.segwit ? "yes" : "no" %>"><%= data.features.segwit ? "yes" : "no" %></dd></div>
|
||||
<div class="flag"><dt>Taproot</dt><dd class="<%= data.features.taproot ? "yes" : "no" %>"><%= data.features.taproot ? "yes" : "no" %></dd></div>
|
||||
<div class="flag"><dt>RBF</dt><dd class="<%= data.features.rbf ? "yes" : "no" %>"><%= data.features.rbf ? "yes" : "no" %></dd></div>
|
||||
</dl>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Fee</td>
|
||||
<td><%= data.transaction.fee %> sat</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Fee rate</td>
|
||||
<td><%= (data.transaction.fee / (data.transaction.weight / 4)).toFixed(2) %> sat/vB</td>
|
||||
</tr>
|
||||
<% if (data.cpfp && data.cpfp.effectiveFeePerVsize && data.cpfp.effectiveFeePerVsize !== (data.transaction.fee / (data.transaction.weight / 4))) { %>
|
||||
<tr>
|
||||
<td>Effective fee rate</td>
|
||||
<td><%= data.cpfp.effectiveFeePerVsize.toFixed(2) %> sat/vB</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
</table>
|
||||
|
||||
<h2>Inputs & Outputs</h2>
|
||||
<div class="flex">
|
||||
<div>
|
||||
<h3>Inputs</h3>
|
||||
<table>
|
||||
<% data.transaction.vin.forEach((vin, i) => { %>
|
||||
<tr>
|
||||
<% if (vin.is_coinbase) { %>
|
||||
<td>Coinbase <%= data.hex2ascii(vin.scriptsig) %></td>
|
||||
<% } else { %>
|
||||
<td><a href="/address/<%= vin.prevout.scriptpubkey_address %>"><%= vin.prevout.scriptpubkey_address %></a></td>
|
||||
<% } %>
|
||||
<td><%= ((vin.prevout ? vin.prevout.value : 0) / 100_000_000).toFixed(8) %> BTC</td>
|
||||
</tr>
|
||||
<% }) %>
|
||||
</table>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Outputs</h3>
|
||||
<table>
|
||||
<% data.transaction.vout.forEach((vout, i) => { %>
|
||||
<tr>
|
||||
<td>
|
||||
<% if (vout.scriptpubkey_type === 'op_return') { %>
|
||||
OP_RETURN <%= data.hex2ascii(vout.scriptpubkey_asm) %>
|
||||
<% } else if (vout.scriptpubkey_type === 'p2pk') { %>
|
||||
P2PK <a href="/address/<%= vout.scriptpubkey.slice(2, -2) %>"><%= vout.scriptpubkey.slice(2, -2) %></a>
|
||||
<% } else if (!['p2pkh', 'p2sh', 'v0_p2wpkh', 'v0_p2wsh', 'v1_p2tr'].includes(vout.scriptpubkey_type)) { %>
|
||||
<%= vout.scriptpubkey_type.toUpperCase() %>
|
||||
<% } else { %>
|
||||
<a href="/address/<%= vout.scriptpubkey_address %>"><%= vout.scriptpubkey_address %></a>
|
||||
<% } %>
|
||||
</td>
|
||||
<td><%= (vout.value / 100_000_000).toFixed(8) %> BTC</td>
|
||||
</tr>
|
||||
<% }) %>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Details</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Size</td>
|
||||
<td><%= data.transaction.size %> B</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Virtual size</td>
|
||||
<td><%= data.transaction.weight / 4 %> vB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Weight</td>
|
||||
<td><%= data.transaction.weight %> WU</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Version</td>
|
||||
<td><%= data.transaction.version %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Locktime</td>
|
||||
<td><%= data.transaction.locktime %></td>
|
||||
</tr>
|
||||
<% if (data.cpfp && data.cpfp.adjustedVsize && data.cpfp.adjustedVsize > (data.transaction.weight / 4)) { %>
|
||||
<tr>
|
||||
<td>Sigops</td>
|
||||
<td><%= data.cpfp.sigops %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Adjusted vsize</td>
|
||||
<td><%= data.cpfp.adjustedVsize %> vB</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
</table>
|
||||
</div>
|
||||
<%- include('footer'); %>
|
||||
</body>
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user