Browse Source
- new /block-analysis tool for summarizing data for all tx within a given block - include subsidy and tx fees in volume displays (on homepage network summary and on block details pages) - misc minor styling and frontend ux tweaksmaster
22 changed files with 932 additions and 145 deletions
@ -0,0 +1,18 @@ |
|||
function updateCurrencyValue(element, val) { |
|||
$.ajax({ |
|||
url: `/snippet/formatCurrencyAmount/${val}` |
|||
|
|||
}).done(function(result) { |
|||
element.html(result); |
|||
$('[data-toggle="tooltip"]').tooltip(); |
|||
}); |
|||
} |
|||
|
|||
function updateFeeRateValue(element, val, digits) { |
|||
$.ajax({ |
|||
url: `/api/utils/formatCurrencyAmountInSmallestUnits/${val},${digits}` |
|||
|
|||
}).done(function(result) { |
|||
element.html(`<span>${result.val} <small>${result.currencyUnit}/vB</small></span>`); |
|||
}); |
|||
} |
@ -0,0 +1,604 @@ |
|||
extends layout |
|||
|
|||
block headContent |
|||
title Block Analysis ##{result.getblock.height.toLocaleString()}, #{result.getblock.hash} |
|||
|
|||
block content |
|||
h1.h3 Block Analysis |
|||
small.text-monospace(style="width: 100%;") ##{result.getblock.height.toLocaleString()} |
|||
br |
|||
small.text-monospace.word-wrap(style="width: 100%;") #{result.getblock.hash} |
|||
hr |
|||
|
|||
div.mb-2 |
|||
if (result.getblock) |
|||
a.btn.btn-sm.btn-primary.mb-1(href=("/block/" + result.getblock.hash)) « Back to Block Details: |
|||
span.text-monospace ##{result.getblock.height.toLocaleString()} |
|||
|
|||
div.row |
|||
- var sumTableLabelClass = (result.blockstats != null ? "summary-split-table-label" : "summary-table-label"); |
|||
- var sumTableValueClass = (result.blockstats != null ? "summary-split-table-content" : "summary-table-content"); |
|||
|
|||
div.mb-3.col |
|||
div.card.shadow-sm(style="height: 100%;") |
|||
div.card-body.px-2.px-md-3 |
|||
h3.h6.mb-0 Summary |
|||
hr |
|||
|
|||
div.clearfix |
|||
div.row |
|||
div.summary-table-label Date |
|||
div.summary-table-content.text-monospace |
|||
- var timestampHuman = result.getblock.time; |
|||
include includes/timestamp-human.pug |
|||
small.ml-1 utc |
|||
|
|||
- var timeAgoTime = result.getblock.time; |
|||
span.text-muted.ml-2 ( |
|||
include includes/time-ago-text.pug |
|||
span ago) |
|||
|
|||
if (result.getblock.weight) |
|||
div.row |
|||
div.summary-table-label Weight |
|||
div.summary-table-content.text-monospace |
|||
span #{result.getblock.weight.toLocaleString()} |
|||
small wu |
|||
|
|||
span.text-muted (#{new Decimal(100 * result.getblock.weight / coinConfig.maxBlockWeight).toDecimalPlaces(2)}% full) |
|||
|
|||
else |
|||
div.row |
|||
div.summary-table-label Size |
|||
div.summary-table-content.text-monospace #{result.getblock.size.toLocaleString()} |
|||
small B |
|||
|
|||
div.row |
|||
div.summary-table-label Transactions |
|||
div.summary-table-content.text-monospace #{result.getblock.tx.length.toLocaleString()} |
|||
|
|||
div.row |
|||
div.summary-table-label Confirmations |
|||
div.summary-table-content.text-monospace |
|||
if (result.getblock.confirmations < 6) |
|||
span.font-weight-bold.text-warning #{result.getblock.confirmations.toLocaleString()} |
|||
a(data-toggle="tooltip", title="Fewer than 6 confirmations is generally considered 'unsettled' for high-value transactions. The applicability of this guidance may vary.") |
|||
i(class="fas fa-unlock-alt") |
|||
else |
|||
span.font-weight-bold.text-success #{result.getblock.confirmations.toLocaleString()} |
|||
a(data-toggle="tooltip", title="6 confirmations is generally considered 'settled'. High-value transactions may require more; low-value transactions may require less.") |
|||
i.fas.fa-lock |
|||
|
|||
div#progress-wrapper |
|||
div.card.shadow-sm.mb-3 |
|||
div.card-body |
|||
h4.h6 Loading transactions: |
|||
span(id="progress-text") |
|||
div.progress(id="progress-bar", style="height: 7px;") |
|||
div.progress-bar(id="data-progress", role="progressbar", aria-valuenow="0", aria-valuemin="0" ,aria-valuemax="100") |
|||
|
|||
|
|||
div#main-content(style="display: none;") |
|||
div.row |
|||
div.col-lg-6.col-lg-left |
|||
div.card.shadow-sm.mb-3 |
|||
div.card-body |
|||
h3.h6.mb-0 Top Value Transactions |
|||
hr |
|||
|
|||
div.table-responsive |
|||
table.table.table-striped.mb-0 |
|||
thead |
|||
tr |
|||
th |
|||
th.data-header Transaction |
|||
th.data-header.text-right Output Value |
|||
|
|||
tbody(id="tbody-top-value-tx") |
|||
tr.text-monospace.row-prototype(style="display: none;") |
|||
td |
|||
small.text-muted.data-index |
|||
|
|||
td.data-tx-link |
|||
td.text-right.data-tx-value |
|||
|
|||
div.col-lg-6.col-lg-right |
|||
div.card.shadow-sm.mb-3 |
|||
div.card-body |
|||
h3.h6.mb-0 Top Fee Transactions |
|||
hr |
|||
|
|||
div.table-responsive |
|||
table.table.table-striped.mb-0 |
|||
thead |
|||
tr |
|||
th |
|||
th.data-header Transaction |
|||
th.data-header.text-right Fee |
|||
|
|||
tbody(id="tbody-top-fee-tx") |
|||
tr.text-monospace.row-prototype(style="display: none;") |
|||
td |
|||
small.text-muted.data-index |
|||
|
|||
td.data-tx-link |
|||
td.text-right.data-tx-value |
|||
|
|||
div.col-lg-6.col-lg-left.mb-3 |
|||
div.card.shadow-sm(style="height: 100%;") |
|||
div.card-body |
|||
h3.h6.mb-0 Input Types |
|||
hr |
|||
|
|||
table.table.table-striped.mb-0 |
|||
tbody(id="tbody-input-types") |
|||
tr.text-monospace.row-prototype(style="display: none;") |
|||
td |
|||
small.data-tag.bg-dark.data-type |
|||
|
|||
td.data-count |
|||
|
|||
div.col-lg-6.col-lg-right.mb-3 |
|||
div.card.shadow-sm(style="height: 100%;") |
|||
div.card-body |
|||
h3.h6.mb-0 Output Types |
|||
hr |
|||
|
|||
table.table.table-striped.mb-0 |
|||
tbody(id="tbody-output-types") |
|||
tr.text-monospace.row-prototype(style="display: none;") |
|||
td |
|||
small.data-tag.bg-dark.data-type |
|||
|
|||
td.data-count |
|||
|
|||
div.row |
|||
div.col-lg-6.col-lg-left |
|||
div.card.shadow-sm.mb-3 |
|||
div.card-body |
|||
h3.h6.mb-0 Transaction Value |
|||
hr |
|||
|
|||
canvas(id="graph-tx-value") |
|||
|
|||
div.col-lg-6.col-lg-right |
|||
div.card.shadow-sm.mb-3 |
|||
div.card-body |
|||
h3.h6.mb-0 Transaction Value Distribution |
|||
hr |
|||
|
|||
canvas(id="chart-tx-value-distribution") |
|||
|
|||
div.col-lg-6.col-lg-left |
|||
div.card.shadow-sm.mb-3 |
|||
div.card-body |
|||
h3.h6.mb-0 Transaction Fee |
|||
hr |
|||
|
|||
canvas(id="graph-tx-fee") |
|||
|
|||
div.col-lg-6.col-lg-right |
|||
div.card.shadow-sm.mb-3 |
|||
div.card-body |
|||
h3.h6.mb-0 Transaction Fee Distribution |
|||
hr |
|||
|
|||
canvas(id="chart-tx-fee-distribution") |
|||
|
|||
div.col-lg-6.col-lg-left |
|||
div.card.shadow-sm.mb-3 |
|||
div.card-body |
|||
h3.h6.mb-0 Transaction Input Count |
|||
hr |
|||
|
|||
canvas(id="graph-tx-inputs") |
|||
|
|||
div.col-lg-6.col-lg-right |
|||
div.card.shadow-sm.mb-3 |
|||
div.card-body |
|||
h3.h6.mb-0 Transaction Output Count |
|||
hr |
|||
|
|||
canvas(id="graph-tx-outputs") |
|||
|
|||
div.col-lg-6.col-lg-left |
|||
div.card.shadow-sm.mb-3 |
|||
div.card-body |
|||
h3.h6.mb-0 Transaction Size |
|||
hr |
|||
|
|||
canvas(id="graph-tx-size") |
|||
|
|||
|
|||
|
|||
block endOfBody |
|||
- var txidChunkSize = 10; |
|||
|
|||
script(src="/js/chart.bundle.min.js", integrity="sha384-qgOtiGNaHh9fVWUnRjyHlV39rfbDcvPPkEzL1RHvsHKbuqUqM6uybNuVnghY2z4/") |
|||
script(src='/js/decimal.js') |
|||
script. |
|||
var txidChunkSize = !{txidChunkSize}; |
|||
var txidChunks = !{JSON.stringify(utils.splitArrayIntoChunks(result.getblock.tx, txidChunkSize))}; |
|||
var blockHeight = !{result.getblock.height}; |
|||
var satsMultiplier = !{coinConfig.baseCurrencyUnit.multiplier}; |
|||
|
|||
$(document).ready(function() { |
|||
loadTransactions(txidChunks, txidChunkSize, txidChunks.length * txidChunkSize); |
|||
}); |
|||
|
|||
function loadTransactions(txidChunks, chunkSize, count) { |
|||
var chunkStrs = []; |
|||
|
|||
for (var i = 0; i < txidChunks.length; i++) { |
|||
var txidChunk = txidChunks[i]; |
|||
|
|||
var chunkStr = ""; |
|||
|
|||
for (var j = 0; j < txidChunk.length; j++) { |
|||
if (j > 0) { |
|||
chunkStr += ","; |
|||
} |
|||
|
|||
chunkStr += txidChunk[j]; |
|||
} |
|||
|
|||
chunkStrs.push(chunkStr); |
|||
} |
|||
|
|||
//alert(JSON.stringify(chunks)); |
|||
|
|||
var results = []; |
|||
|
|||
var statusCallback = function(chunkIndexDone, chunkCount) { |
|||
//console.log("Done: " + Math.min(((chunkIndexDone + 1) * chunkSize), count) + " of " + count); |
|||
|
|||
var wPercent = `${parseInt(100 * (chunkIndexDone + 1) / parseFloat(chunkCount))}%`; |
|||
|
|||
$("#data-progress").css("width", wPercent); |
|||
$("#progress-text").text(`${Math.min(((chunkIndexDone + 1) * chunkSize), count).toLocaleString()} of ${count.toLocaleString()} (${wPercent})`); |
|||
}; |
|||
|
|||
var finishedCallback = function() { |
|||
var summary = summarizeData(results); |
|||
|
|||
fillTopValueTxTable(summary); |
|||
fillTopFeeTxTable(summary); |
|||
fillInputOutputTypesTable(summary); |
|||
|
|||
createGraph("graph-tx-value", summary.txValueGraphData, "Value"); |
|||
createGraph("graph-tx-fee", summary.txFeeGraphData, "Fee"); |
|||
createGraph("graph-tx-inputs", summary.txInputCountGraphData, "Input Count"); |
|||
createGraph("graph-tx-outputs", summary.txOutputCountGraphData, "Output Count"); |
|||
createGraph("graph-tx-size", summary.txSizeGraphData, "Size"); |
|||
|
|||
createChart("chart-tx-value-distribution", summary.valueDistribution, summary.valueDistributionLabels); |
|||
createChart("chart-tx-fee-distribution", summary.feeDistribution, summary.feeDistributionLabels); |
|||
|
|||
//$(".abc").text(JSON.stringify(summary)); |
|||
|
|||
$("#main-content").show(); |
|||
$("#progress-wrapper").hide(); |
|||
}; |
|||
|
|||
getTxData(results, chunkStrs, 0, statusCallback, finishedCallback); |
|||
} |
|||
|
|||
function fillTopValueTxTable(data) { |
|||
var count = Math.min(10, data.topValueTxs.length); |
|||
|
|||
for (var i = 0; i < count; i++) { |
|||
var item = data.topValueTxs[i]; |
|||
|
|||
var row = $("#tbody-top-value-tx .row-prototype").clone(); |
|||
|
|||
row.removeClass("row-prototype"); |
|||
|
|||
row.find(".data-index").text((i + 1).toLocaleString()); |
|||
row.find(".data-tx-link").html(`<a href='/tx/${item.txid}'>${item.txid}</a>`); |
|||
row.find(".data-tx-value").text(item.value); |
|||
|
|||
updateCurrencyValue(row.find(".data-tx-value"), item.value); |
|||
|
|||
row.show(); |
|||
|
|||
$("#tbody-top-value-tx").append(row); |
|||
} |
|||
} |
|||
|
|||
function fillTopFeeTxTable(data) { |
|||
var count = Math.min(10, data.topFeeTxs.length); |
|||
|
|||
for (var i = 0; i < count; i++) { |
|||
var item = data.topFeeTxs[i]; |
|||
|
|||
var row = $("#tbody-top-fee-tx .row-prototype").clone(); |
|||
|
|||
row.removeClass("row-prototype"); |
|||
|
|||
row.find(".data-index").text((i + 1).toLocaleString()); |
|||
row.find(".data-tx-link").html(`<a href='/tx/${item.txid}'>${item.txid}</a>`); |
|||
row.find(".data-tx-value").text(item.value); |
|||
|
|||
updateCurrencyValue(row.find(".data-tx-value"), item.value); |
|||
|
|||
row.show(); |
|||
|
|||
$("#tbody-top-fee-tx").append(row); |
|||
} |
|||
} |
|||
|
|||
function fillInputOutputTypesTable(data) { |
|||
var sortedInputs = []; |
|||
for (var key in data.inputTypeCounts) { |
|||
if (data.inputTypeCounts.hasOwnProperty(key)) { |
|||
sortedInputs.push({type:key, count:data.inputTypeCounts[key]}); |
|||
} |
|||
} |
|||
|
|||
var sortedOutputs = []; |
|||
for (var key in data.outputTypeCounts) { |
|||
if (data.outputTypeCounts.hasOwnProperty(key)) { |
|||
sortedOutputs.push({type:key, count:data.outputTypeCounts[key]}); |
|||
} |
|||
} |
|||
|
|||
sortedInputs.sort(function(a, b) { |
|||
return b.count - a.count; |
|||
}); |
|||
|
|||
sortedOutputs.sort(function(a, b) { |
|||
return b.count - a.count; |
|||
}); |
|||
|
|||
for (var i = 0; i < sortedInputs.length; i++) { |
|||
var item = sortedInputs[i]; |
|||
|
|||
var row = $("#tbody-input-types .row-prototype").clone(); |
|||
|
|||
row.removeClass("row-prototype"); |
|||
|
|||
if (i == 0) { |
|||
row.addClass("table-borderless"); |
|||
} |
|||
|
|||
// span(title=`Output Type: ${utils.outputTypeName(inputTypeKey)}`, data-toggle="tooltip") #{utils.outputTypeAbbreviation(inputTypeKey)} |
|||
row.find(".data-type").html(`<span title='Type: ${item.type}' data-toggle='tooltip'>${item.type}</span>`); |
|||
row.find(".data-count").text(item.count.toLocaleString()); |
|||
|
|||
row.show(); |
|||
|
|||
$("#tbody-input-types").append(row); |
|||
} |
|||
|
|||
for (var i = 0; i < sortedOutputs.length; i++) { |
|||
var item = sortedOutputs[i]; |
|||
|
|||
var row = $("#tbody-output-types .row-prototype").clone(); |
|||
|
|||
row.removeClass("row-prototype"); |
|||
|
|||
if (i == 0) { |
|||
row.addClass("table-borderless"); |
|||
} |
|||
|
|||
// span(title=`Output Type: ${utils.outputTypeName(inputTypeKey)}`, data-toggle="tooltip") #{utils.outputTypeAbbreviation(inputTypeKey)} |
|||
row.find(".data-type").html(`<span title='Type: ${item.type}' data-toggle='tooltip'>${item.type}</span>`); |
|||
row.find(".data-count").text(item.count.toLocaleString()); |
|||
|
|||
row.show(); |
|||
|
|||
$("#tbody-output-types").append(row); |
|||
} |
|||
} |
|||
|
|||
function createGraph(graphId, data, yLabelStr) { |
|||
var ctx = document.getElementById(graphId).getContext('2d'); |
|||
var graph = new Chart(ctx, { |
|||
type: 'line', |
|||
data: { |
|||
datasets: [{ |
|||
borderColor: '#007bff', |
|||
borderWidth: 2, |
|||
backgroundColor: 'rgba(0,0,0,0)', |
|||
data: data, |
|||
pointRadius: 1 |
|||
}] |
|||
}, |
|||
options: { |
|||
legend: { display: false }, |
|||
scales: { |
|||
xAxes: [{ |
|||
type: 'linear', |
|||
position: 'bottom', |
|||
scaleLabel: { |
|||
display: true, |
|||
labelString: 'Index in Block' |
|||
}, |
|||
//ticks: { |
|||
// stepSize: 100, |
|||
//} |
|||
}], |
|||
yAxes: [{ |
|||
scaleLabel: { |
|||
display: true, |
|||
labelString: yLabelStr |
|||
} |
|||
}] |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
|
|||
function createChart(chartId, data, labels) { |
|||
var bgColors = []; |
|||
|
|||
for (var i = 0; i < labels.length; i++) { |
|||
bgColors.push(`hsl(${(333 * i / labels.length)}, 100%, 50%)`); |
|||
} |
|||
|
|||
var ctx1 = document.getElementById(chartId).getContext('2d'); |
|||
var chart = new Chart(ctx1, { |
|||
type: 'bar', |
|||
data: { |
|||
labels: labels, |
|||
datasets: [{ |
|||
data: data, |
|||
backgroundColor: bgColors |
|||
}] |
|||
}, |
|||
options: { |
|||
legend: { |
|||
display: false |
|||
}, |
|||
scales: { |
|||
yAxes: [{ |
|||
ticks: { |
|||
beginAtZero:true |
|||
} |
|||
}] |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
|
|||
function getTxData(results, chunks, chunkIndex, statusCallback, finishedCallback) { |
|||
if (chunkIndex > chunks.length - 1) { |
|||
finishedCallback(); |
|||
|
|||
return; |
|||
} |
|||
|
|||
var url = `/api/block-tx-summaries/${blockHeight}/${chunks[chunkIndex]}`; |
|||
|
|||
//console.log(url); |
|||
|
|||
$.ajax({ |
|||
url: url |
|||
|
|||
}).done(function(result) { |
|||
for (var i = 0; i < result.length; i++) { |
|||
results.push(result[i]); |
|||
} |
|||
|
|||
statusCallback(chunkIndex, chunks.length); |
|||
|
|||
getTxData(results, chunks, chunkIndex + 1, statusCallback, finishedCallback); |
|||
}); |
|||
} |
|||
|
|||
function summarizeData(txResults) { |
|||
var analysis = {}; |
|||
|
|||
analysis.inputTypeCounts = {}; |
|||
analysis.outputTypeCounts = {}; |
|||
|
|||
analysis.txValues = []; |
|||
analysis.txValueGraphData = []; |
|||
|
|||
analysis.txFees = []; |
|||
analysis.txFeeGraphData = []; |
|||
analysis.txSizeGraphData = []; |
|||
analysis.txInputCountGraphData = []; |
|||
analysis.txOutputCountGraphData = []; |
|||
|
|||
for (var i = 0; i < txResults.length; i++) { |
|||
var txSummary = txResults[i]; |
|||
|
|||
//console.log(JSON.stringify(txSummary)); |
|||
|
|||
for (var j = 0; j < txSummary.vout.length; j++) { |
|||
var vout = txSummary.vout[j]; |
|||
var outputType = vout.type; |
|||
|
|||
if (!analysis.outputTypeCounts[outputType]) { |
|||
analysis.outputTypeCounts[outputType] = 0; |
|||
} |
|||
|
|||
analysis.outputTypeCounts[outputType]++; |
|||
} |
|||
|
|||
analysis.txValues.push({txid:txSummary.txid, value:new Decimal(txSummary.totalOutput)}); |
|||
analysis.txValueGraphData.push({x:i, y:new Decimal(txSummary.totalOutput).toNumber()}); |
|||
|
|||
analysis.txFees.push({txid:txSummary.txid, value:new Decimal(txSummary.totalFee)}); |
|||
analysis.txFeeGraphData.push({x:i, y:new Decimal(txSummary.totalFee).toNumber()}); |
|||
|
|||
analysis.txSizeGraphData.push({x:i, y:(txSummary.vsize ? txSummary.vsize : txSummary.size)}); |
|||
|
|||
analysis.txInputCountGraphData.push({x:i, y:txSummary.vin.length}); |
|||
analysis.txOutputCountGraphData.push({x:i, y:txSummary.vout.length}); |
|||
|
|||
if (!txSummary.coinbase) { |
|||
for (var j = 0; j < txSummary.vin.length; j++) { |
|||
var vin = txSummary.vin[j]; |
|||
|
|||
var inputType = vin.type; |
|||
|
|||
if (!analysis.inputTypeCounts[inputType]) { |
|||
analysis.inputTypeCounts[inputType] = 0; |
|||
} |
|||
|
|||
analysis.inputTypeCounts[inputType]++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
analysis.txValues.sort(function(a, b) { |
|||
return b.value.cmp(a.value); |
|||
}); |
|||
|
|||
analysis.txFees.sort(function(a, b) { |
|||
return b.value.cmp(a.value); |
|||
}); |
|||
|
|||
analysis.topValueTxs = analysis.txValues.slice(0, Math.min(100, analysis.txValues.length)); |
|||
analysis.topFeeTxs = analysis.txFees.slice(0, Math.min(100, analysis.txFees.length)); |
|||
|
|||
var topValue = new Decimal(analysis.txValues[parseInt(analysis.txValues.length * 0.1)].value).times(satsMultiplier); |
|||
var topFee = new Decimal(analysis.txFees[parseInt(analysis.txFees.length * 0.1)].value).times(satsMultiplier); |
|||
|
|||
var topValueSats = parseInt(topValue); |
|||
var topFeeSats = parseInt(topFee); |
|||
|
|||
var distributionBucketCount = 25; |
|||
|
|||
analysis.valueDistribution = []; |
|||
analysis.valueDistributionLabels = []; |
|||
|
|||
analysis.feeDistribution = []; |
|||
analysis.feeDistributionLabels = []; |
|||
|
|||
for (var i = 0; i < distributionBucketCount; i++) { |
|||
analysis.valueDistribution.push(0); |
|||
analysis.valueDistributionLabels.push(`[${new Decimal(i * topValueSats / distributionBucketCount).dividedBy(satsMultiplier).toDP(3)} - ${new Decimal((i + 1) * topValueSats / distributionBucketCount).dividedBy(satsMultiplier).toDP(3)})`); |
|||
|
|||
analysis.feeDistribution.push(0); |
|||
analysis.feeDistributionLabels.push(`[${new Decimal(i * topFeeSats / distributionBucketCount).toDP(0)} - ${new Decimal((i + 1) * topFeeSats / distributionBucketCount).toDP(0)})`); |
|||
} |
|||
|
|||
analysis.valueDistributionLabels[distributionBucketCount - 1] = `${new Decimal(topValueSats).dividedBy(satsMultiplier).toDP(3)}+`; |
|||
analysis.feeDistributionLabels[distributionBucketCount - 1] = `${topFeeSats}+`; |
|||
|
|||
for (var i = 0; i < txResults.length; i++) { |
|||
var txSummary = txResults[i]; |
|||
|
|||
var valueSats = new Decimal(txSummary.totalOutput).times(satsMultiplier); |
|||
var feeSats = new Decimal(txSummary.totalFee).times(satsMultiplier); |
|||
|
|||
var valueBucket = parseInt(distributionBucketCount * valueSats / topValueSats); |
|||
if (valueBucket >= distributionBucketCount) { |
|||
valueBucket = distributionBucketCount - 1; |
|||
} |
|||
|
|||
var feeBucket = parseInt(distributionBucketCount * feeSats / topFeeSats); |
|||
if (feeBucket >= distributionBucketCount) { |
|||
feeBucket = distributionBucketCount - 1; |
|||
} |
|||
|
|||
analysis.valueDistribution[valueBucket]++; |
|||
analysis.feeDistribution[feeBucket]++; |
|||
} |
|||
|
|||
return analysis; |
|||
} |
|||
|
Loading…
Reference in new issue