mirror of
https://github.com/cryptosharks131/lndg.git
synced 2026-08-19 13:17:52 +02:00
985 lines
48 KiB
HTML
985 lines
48 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %} {{ block.super }} - Dashboard{% endblock %}
|
|
{% block meta %}<meta http-equiv="refresh" content="1260">{% endblock %}
|
|
{% block content %}
|
|
{% load humanize %}
|
|
<span style="position: absolute;top: 0;right: 0;">Dashboard Last Updated: <span id='datetime'></span> ... refreshing every 21 seconds</span>
|
|
<div class="w3-container w3-padding-small">
|
|
<script language="Javascript">
|
|
function qr_gen(addr){
|
|
var qrcode = new QRCode(byId('qrcode'), {text:addr, width:256, height:256})
|
|
byId('qrcode').style.display = 'block'
|
|
}
|
|
function qr_clear(){
|
|
byId('qrcode').replaceChildren()
|
|
byId('qrcode').style.display = 'none'
|
|
}
|
|
function qr_addr(addr){
|
|
var qrcode = new QRCode(byId(addr), {text:addr, width:256, height:256})
|
|
}
|
|
</script>
|
|
<h3>
|
|
<span style="font-size: 12px;vertical-align: middle; padding-left: 18px; border:1px solid {{ node_info.color }}; background-color: {{ node_info.color }}" class="w3-circle" title="{{ node_info.color }}"></span>
|
|
<a style="margin-left:6px;" href="{{ graph_links }}/{{ network }}node/{{ node_info.identity_pubkey }}" target="_blank">{{ node_info.alias }}</a> | {{ node_info.identity_pubkey }}
|
|
</h3>
|
|
<h6>
|
|
<span id="lnd" class="w3-tag w3-round" title="{{ node_info.version }}">LND</span>
|
|
<span id="timechain" style="padding:1.5px 8px;" class="w3-round w3-border">---</span>
|
|
</h6>
|
|
<h6>
|
|
<span style="padding:1.5px 8px;" class="w3-round w3-border w3-border-grey">Public Capacity: <span id="public_capacity">0</span></span>
|
|
<span style="padding:1.5px 8px;" class="w3-round w3-border w3-border-grey">Active Channels: <span id="public_channels_count">0</span> / <span id="total_channels">0</span></span>
|
|
<span id="peers" style="padding:1.5px 8px;" class="w3-round w3-border w3-border-grey">0</span>
|
|
<span id="dbSize" style="padding:1.5px 8px;" class="w3-round w3-border w3-border-grey">---</span>
|
|
<span style="padding:1.5px 8px;" class="w3-round w3-border w3-border-grey">Total States Updates: <span id="updates">---</span></span>
|
|
<span style="padding:1.5px 8px;margin-left:4px" class="w3-round w3-border w3-border-grey"><a href="#sign">Sign a message</a></span>
|
|
</h6>
|
|
<h6 style="display: none" id="private_stats">
|
|
<span style="padding:1.5px 8px;" class="w3-round w3-border w3-border-grey">Private Capacity: <span id="private_capacity">0</span></span>
|
|
<span style="padding:1.5px 8px;" class="w3-round w3-border w3-border-grey">Locked Liquidity: <span id="private_liquidity">0</span></span>
|
|
<span style="padding:1.5px 8px;" class="w3-round w3-border w3-border-grey">Active Private Channels: <span id="private_active">0</span> / <span id="private_count">0</span></span>
|
|
</h6>
|
|
Addresses:
|
|
{% for addr in node_info.uris %}
|
|
<div class="w3-container" style="height:40px">
|
|
<text readonly style="word-wrap: break-word;vertical-align:top;">{{ addr }}</text>
|
|
<a href="#." data-value="{{addr}}" onclick="toggle(this)" onmouseleave="qr_clear()" onmouseenter="qr_gen('{{ addr }}');">
|
|
<svg style="height:30px;width:30px;transform:matrix(1, 0, 0, 1, 4, 3)" version="1.1" >
|
|
<path style="" fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path>
|
|
<path style="" fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path>
|
|
</svg>
|
|
<svg style="height:30px;width:30px;visibility:collapse;transform:matrix(1, 0, 0, 1, -30, 3);" version="1.1" >
|
|
<path style="fill:#3fb950;stroke:#3fb950;" fill-rule="evenodd" d="M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z"></path>
|
|
</svg>
|
|
</a>
|
|
<div style="display:none;position:absolute;top:30px;right:30px;padding:30px;z-index:11;background:white" id="qrcode"></div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
<div class="w3-row w3-container w3-padding-small" >
|
|
<table class="w3-col w3-small w3-table-all w3-centered w3-hoverable" style="min-width:300px;width:25%">
|
|
<tr>
|
|
<th title="Total Inbound/Total Outbound">Ratio: <span id="liq_ratio">0</span></h>
|
|
<th>Inbound</th>
|
|
<th>Outbound</th>
|
|
<th>Unsettled</th>
|
|
</tr>
|
|
<tr>
|
|
<th>Active</th>
|
|
<td id="active_inbound">0</td>
|
|
<td id="active_outbound">0</td>
|
|
<td id="active_unsettled">0</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Inactive</th>
|
|
<td id="inactive_inbound">0</td>
|
|
<td id="inactive_outbound">0</td>
|
|
<td id="inactive_unsettled">0</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Total</th>
|
|
<td id="total_inbound">0</td>
|
|
<td id="total_outbound">0</td>
|
|
<td id="total_unsettled">0</td>
|
|
</tr>
|
|
</table>
|
|
<table class="w3-col w3-small w3-table-all w3-centered w3-hoverable" style="min-width:1000px;width:75%">
|
|
<thead>
|
|
<tr>
|
|
<th class="w3-border-bottom w3-border-right"><a href="/balances" target="_blank">Balance</a>: <span id="total_balance">0</span></th>
|
|
<th id="onChain" class="w3-border-bottom w3-border-right">Onchain: 0</th>
|
|
<th id="confirmed" colspan="2" class="w3-border-bottom w3-border-right">Confirmed: 0</th>
|
|
<th id="unconfirmed" class="w3-border-bottom w3-border-right">Unconfirmed: 0</th>
|
|
<th id="limbo" class="w3-border-bottom w3-border-right">Limbo: 0</th>
|
|
<th class="w3-border-bottom w3-border-right"><a href="/pending_htlcs" target="_blank">Pending HTLCs</a>: <span id="pending_htlcs">0</span></th>
|
|
<th class="w3-border-bottom">
|
|
<form method="post" action="/newaddress/">
|
|
{% csrf_token %}
|
|
<a style="cursor: pointer" onclick="Sync.POST('newaddress/', {body: {legacy:false}}, r => {showBannerMsg('Onchain Address:', r.data, true, r.data);qr_addr(r.data);})">New Onchain Address</a> | <a style="cursor: pointer" onclick="Sync.POST('newaddress/', {body: {legacy:true}}, r => {showBannerMsg('Legacy Address:', r.data, true, r.data);qr_addr(r.data);})">Legacy</a>
|
|
</form>
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tr>
|
|
<th>Period</th>
|
|
<th>Routed</th>
|
|
<th>Fees Earned</th>
|
|
<th>Fees Paid</th>
|
|
<th>Onchain fees</th>
|
|
<th>Cost</th>
|
|
<th title="Profit made per outbound [profit per outbound including onchain fees]">Profit/Outbound</th>
|
|
<th>Outbound Utilization</th>
|
|
</tr>
|
|
<tr>
|
|
<td>1-Day</td>
|
|
<td id="routed_1day">0</td>
|
|
<td id="earned_1day">0</td>
|
|
<td id="fees_1day">0</td>
|
|
<td id="onchain_costs_1day">0</td>
|
|
<td id="percent_cost_1day">0</td>
|
|
<td id="profit_per_outbound_1d">0</td>
|
|
<td id="routed_1day_percent">0</td>
|
|
</tr>
|
|
<tr>
|
|
<td>7-Day</td>
|
|
<td id="routed_7day">0</td>
|
|
<td id="earned_7day">0</td>
|
|
<td id="fees_7day">0</td>
|
|
<td id="onchain_costs_7day">0</td>
|
|
<td id="percent_cost_7day">0</td>
|
|
<td id="profit_per_outbound_7d">0</td>
|
|
<td id="routed_7day_percent">0</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<br/>
|
|
<div class="w3-bar w3-border-top" style="background-color: transparent">
|
|
<a class="w3-bar-item w3-hover-blue" href="/income" target="_blank">P&L</a>
|
|
<a class="w3-bar-item w3-hover-blue" href="/closures" target="_blank">Closures</a>
|
|
<a class="w3-bar-item w3-hover-blue" href="/opens" target="_blank">New Peers</a>
|
|
<a class="w3-bar-item w3-hover-blue" href="/peerevents" target="_blank">Peer Events</a>
|
|
<a class="w3-bar-item w3-hover-blue" href="/actions" target="_blank">AR Actions</a>
|
|
<a class="w3-bar-item w3-hover-blue" href="/fees" target="_blank">Fee Rates</a>
|
|
<a class="w3-bar-item w3-hover-blue" href="/autopilot" target="_blank">Autopilot</a>
|
|
<a class="w3-bar-item w3-hover-blue" href="/autofees" target="_blank">Fee Log</a>
|
|
<a class="w3-bar-item w3-hover-blue" href="/channels" target="_blank">Channel Performance</a>
|
|
<a class="w3-bar-item w3-hover-blue" href="/keysends" target="_blank">Keysends</a>
|
|
<a class="w3-bar-item w3-hover-blue" href="/rebalancing" target="_blank">Rebalancing</a>
|
|
<a class="w3-bar-item w3-hover-blue" href="/towers" target="_blank">Towers</a>
|
|
<a class="w3-bar-item w3-hover-blue" href="/batch" target="_blank">Batching</a>
|
|
<a class="w3-bar-item w3-hover-blue" href="/advanced" target="_blank">Advanced Settings</a>
|
|
</div>
|
|
<div id="active_channels_container" style="display:none" class="w3-container w3-padding-small">
|
|
<div class="w3-container w3-padding-small table" style="overflow:auto;max-height:1000px">
|
|
<table class="w3-table-all w3-centered w3-hoverable">
|
|
<thead>
|
|
<tr>
|
|
<th onclick="sortTable(event.target, 0, 'String', 0, 'a')">Channel ID</th>
|
|
<th onclick="sortTable(event.target, 1, 'String', 0, 'a')">Peer Alias</th>
|
|
<th onclick="sortTable(event.target, 2, 'int')">Outbound Liquidity</th>
|
|
<th onclick="sortTable(event.target, 3, 'int', 0, 'label')">Capacity</th>
|
|
<th onclick="sortTable(event.target, 4, 'int')">Inbound Liquidity</th>
|
|
<th onclick="sortTable(event.target, 5, 'int')">Unsettled</th>
|
|
<th width=3% onclick="sortTable(event.target, 6, 'int')">oRate</th>
|
|
<th width=3% onclick="sortTable(event.target, 7, 'int')">oBase</th>
|
|
<th onclick="sortTable(event.target, 8, 'm')">o1D</th>
|
|
<th onclick="sortTable(event.target, 9, 'm')">i1D</th>
|
|
<th onclick="sortTable(event.target, 10, 'm')">o7D</th>
|
|
<th onclick="sortTable(event.target, 11, 'm')">i7D</th>
|
|
<th width=3% onclick="sortTable(event.target, 12, 'int')">iRate</th>
|
|
<th width=3% onclick="sortTable(event.target, 13, 'int')">iBase</th>
|
|
<th title="When AR is ENABLED for the channel, keep pulling IN to the channel until its inbound liquidity falls below the iTarget%." width=4%>iTarget%</th>
|
|
<th title="When AR is ENABLED it will refill the channel with outbound liquidity." width=4%>AR</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="active_channels"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div id="inactive_channels_container" style="display:none" class="w3-container w3-padding-small">
|
|
<table class="w3-table-all w3-centered w3-hoverable">
|
|
<thead>
|
|
<tr>
|
|
<th onclick="sortTable(event.target, 0, 'String')">Channel ID</th>
|
|
<th onclick="sortTable(event.target, 1, 'String', 0, 'a')">Peer Alias</th>
|
|
<th onclick="sortTable(event.target, 2, ' (')" width=9%>Outbound Liquidity</th>
|
|
<th onclick="sortTable(event.target, 3, 'int', 0, 'label')">Capacity</th>
|
|
<th onclick="sortTable(event.target, 4, ' (')" width=9%>Inbound Liquidity</th>
|
|
<th onclick="sortTable(event.target, 5, ' (')" width=6%>Unsettled</th>
|
|
<th onclick="sortTable(event.target, 6, 'int')" width=4%>oRate</th>
|
|
<th onclick="sortTable(event.target, 7, 'int')" width=4%>oBase</th>
|
|
<th onclick="sortTable(event.target, 8, 'int')" width=4%>iRate</th>
|
|
<th onclick="sortTable(event.target, 9, 'int')" width=4%>iBase</th>
|
|
<th width=5%>Local Commit</th>
|
|
<th width=5%>Local Reserve</th>
|
|
<th width=5%>Downtime</th>
|
|
<th width=5%>Opener</th>
|
|
<th width=4%>iTarget%</th>
|
|
<th width=4%>AR</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="inactive_channels"></tbody>
|
|
</table>
|
|
</div>
|
|
<div id="private_channels_container" style="display:none" class="w3-container w3-padding-small">
|
|
<table class="w3-table-all w3-centered w3-hoverable">
|
|
<thead>
|
|
<tr>
|
|
<th>Channel ID</th>
|
|
<th>Peer Alias</th>
|
|
<th width=9%>Outbound Liquidity</th>
|
|
<th>Capacity</th>
|
|
<th width=9%>Inbound Liquidity</th>
|
|
<th width=6%>Unsettled</th>
|
|
<th width=4%>oRate</th>
|
|
<th width=4%>oBase</th>
|
|
<th width=4%>oLife</th>
|
|
<th width=4%>iLife</th>
|
|
<th width=5%>Local Commit</th>
|
|
<th width=5%>Local Reserve</th>
|
|
<th width=5%>Opener</th>
|
|
<th width=5%>Status</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="private_channels"></tbody>
|
|
</table>
|
|
</div>
|
|
<div id="pending_open_container" style="display:none" class="w3-container w3-padding-small">
|
|
<table class="w3-table-all w3-centered w3-hoverable">
|
|
<thead>
|
|
<tr>
|
|
<th>Peer Alias</th>
|
|
<th width=20%>Channel Point</th>
|
|
<th>Capacity</th>
|
|
<th>Local Balance</th>
|
|
<th>Remote Balance</th>
|
|
<th>AF</th>
|
|
<th>Fee Rate</th>
|
|
<th>Base Fee</th>
|
|
<th>CLTV</th>
|
|
<th>Amt</th>
|
|
<th>Max Cost%</th>
|
|
<th>oTarget%</th>
|
|
<th>iTarget%</th>
|
|
<th>AR</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="pending_open"></tbody>
|
|
</table>
|
|
</div>
|
|
<div id="pending_closed_container" style="display:none" class="w3-container w3-padding-small">
|
|
<table class="w3-table-all w3-centered w3-hoverable">
|
|
<thead>
|
|
<tr>
|
|
<th>Channel ID</th>
|
|
<th>Peer Alias</th>
|
|
<th>Capacity</th>
|
|
<th>Local Balance</th>
|
|
<th>Remote Balance</th>
|
|
<th>Balance In Limbo</th>
|
|
<th>Local Commit Fee</th>
|
|
<th width=20%>Closing TX</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="pending_closed"></tbody>
|
|
</table>
|
|
</div>
|
|
<div id="pending_force_closed_container" style="display:none" class="w3-container w3-padding-small">
|
|
<table class="w3-table-all w3-centered w3-hoverable">
|
|
<thead>
|
|
<tr>
|
|
<th>Channel ID</th>
|
|
<th>Peer Alias</th>
|
|
<th>Capacity</th>
|
|
<th>Local Balance</th>
|
|
<th>Remote Balance</th>
|
|
<th>Balance In Limbo</th>
|
|
<th>Maturity</th>
|
|
<th width=20%>Closing TX</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="pending_force_closed"></tbody>
|
|
</table>
|
|
</div>
|
|
<div id="waiting_for_close_container" style="display:none" class="w3-container w3-padding-small">
|
|
<table class="w3-table-all w3-centered w3-hoverable">
|
|
<thead>
|
|
<tr>
|
|
<th>Channel ID</th>
|
|
<th>Peer Alias</th>
|
|
<th>Capacity</th>
|
|
<th>Local Balance</th>
|
|
<th>Remote Balance</th>
|
|
<th>Balance In Limbo</th>
|
|
<th>Local Commit Fee</th>
|
|
<th width=20%>Closing TX</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="waiting_for_close"></tbody>
|
|
</table>
|
|
</div>
|
|
<div id="routed_container" style="display:none" class="w3-container w3-padding-small">
|
|
<table id="forwardsTable" class="w3-table-all w3-centered w3-hoverable">
|
|
<thead>
|
|
<tr>
|
|
<th>Timestamp</th>
|
|
<th>Channel In Id</th>
|
|
<th>Channel In Alias</th>
|
|
<th colspan="2">Amount In</th>
|
|
<th colspan="2">Amount Out</th>
|
|
<th>Channel Out Alias</th>
|
|
<th>Channel Out Id</th>
|
|
<th>Fees Earned</th>
|
|
<th>PPM Earned</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="routed">
|
|
</tbody>
|
|
<tfoot id="loadMoreForwards">
|
|
<tr style="background-color:transparent;cursor:pointer;">
|
|
<td colspan="11">
|
|
<a onclick="loadForwards()">Load More</a>
|
|
</td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
{% include 'rebalances_table.html' with count=10 load_count=10 title='<a href="/rebalances" target="_blank">Rebalance Requests</a>' %}
|
|
<div id="payments_container" style="display:none" class="w3-container w3-padding-small">
|
|
<table id="paymentsTable" class="w3-table-all w3-centered w3-hoverable">
|
|
<thead>
|
|
<tr>
|
|
<th>Timestamp</th>
|
|
<th>Value</th>
|
|
<th>Fee Paid</th>
|
|
<th>PPM Paid</th>
|
|
<th>Status</th>
|
|
<th>Chan Out Alias</th>
|
|
<th>Chan Out ID</th>
|
|
<th>Hash</th>
|
|
<th>Rebalance</th>
|
|
<th>Keysend</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="payments"></tbody>
|
|
<tfoot id="loadMorePayments">
|
|
<tr style="background-color:transparent;cursor:pointer;">
|
|
<td colspan="10">
|
|
<a onclick="loadPayments()">Load More</a>
|
|
</td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
<div id="invoices_container" style="display:none" class="w3-container w3-padding-small">
|
|
<table id="invoicesTable" class="w3-table-all w3-centered w3-hoverable">
|
|
<thead>
|
|
<tr>
|
|
<th>Created</th>
|
|
<th>Settled</th>
|
|
<th>Value</th>
|
|
<th>Amount Paid</th>
|
|
<th>State</th>
|
|
<th>Channel In Alias</th>
|
|
<th>Channel In</th>
|
|
<th>Hash</th>
|
|
<th><a href="/keysends" target="_blank">Keysend</a></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="invoices"></tbody>
|
|
<tfoot id="loadMoreInvoices">
|
|
<tr style="background-color:transparent;cursor:pointer;">
|
|
<td colspan="9">
|
|
<a onclick="loadInvoices()">Load More</a>
|
|
</td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
<div id="failed_htlcs_container" style="display:none" class="w3-container w3-padding-small">
|
|
<table id="failedhtlcsTable" class="w3-table-all w3-centered w3-hoverable">
|
|
<thead>
|
|
<tr>
|
|
<th>Timestamp</th>
|
|
<th>Chan In ID</th>
|
|
<th>Chan In Alias</th>
|
|
<th colspan="2">Forward Amount</th>
|
|
<th colspan="2">Actual Outbound</th>
|
|
<th>Chan Out Alias</th>
|
|
<th>Chan Out ID</th>
|
|
<th>Potential Fee</th>
|
|
<th>HTLC Failure</th>
|
|
<th>Failure Detail</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="failed_htlcs"></tbody>
|
|
<tfoot id="loadMoreFailedHTLCs">
|
|
<tr style="background-color:transparent;cursor:pointer;">
|
|
<td colspan="12">
|
|
<a onclick="loadFailedHTLCs()">Load More</a>
|
|
</td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
{% include 'local_settings.html' with settings=local_settings title="Auto-Rebalancer" url='rebalancing' %}
|
|
<div class="w3-container w3-padding-small">
|
|
<h2>Connect to a Peer</h2>
|
|
<div class="w3-container w3-padding-small">
|
|
<form action="/connectpeer/" method="post">
|
|
{% csrf_token %}
|
|
<input style="min-width:70%" placeholder="PK or PK@host:port" id="peer_id" type="text" name="peer_id">
|
|
<input type="submit" value="OK">
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div class="w3-container w3-padding-small">
|
|
<h2>Open a Channel</h2>
|
|
<div class="w3-container w3-padding-small">
|
|
<form action="/openchannel/" method="post">
|
|
{% csrf_token %}
|
|
<span class="input" style="width:55%">
|
|
<input style="width:100%" placeholder="Public Key" id="peer_pubkey" type="text" name="peer_pubkey">
|
|
</span>
|
|
<span class="input" data-unit="sats">
|
|
<input id="local_amt" placeholder="0" type="number" name="local_amt">
|
|
</span>
|
|
<span class="input" data-unit="sats/vbyte" title="Opening tx fee">
|
|
<input id="sat_per_byte" placeholder="0" type="number" min="1" name="sat_per_byte">
|
|
</span>
|
|
<input type="submit" value="OK">
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div class="w3-container w3-padding-small">
|
|
<h2>Close a Channel</h2>
|
|
<div class="w3-container w3-padding-small">
|
|
<form action="/closechannel/" method="post">
|
|
{% csrf_token %}
|
|
<span class="input">
|
|
<input placeholder="Channel ID" id="chan_id" type="text" name="chan_id">
|
|
</span>
|
|
<div class="input" data-unit="sats/vbyte" title="Closing tx fee">
|
|
<input id="target_fee" placeholder="0" type="number" min="1" name="target_fee">
|
|
</div>
|
|
<label style="top:-4px;position:relative" title="Force Close" for="force">FC</label>
|
|
<input class="w3-check" id="force" style="top:2px" title="Force Close?" type="checkbox" name="force">
|
|
<input type="submit" style="position:relative;top:-4px" value="OK">
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div class="w3-container w3-padding-small">
|
|
<h2>Create Invoice</h2>
|
|
<div class="w3-container w3-padding-small">
|
|
<form action="/createinvoice/" method="post">
|
|
{% csrf_token %}
|
|
<span class="input" data-unit="sats">
|
|
<input placeholder="0" id="value" type="number" name="value">
|
|
</span>
|
|
<input type="submit" value="OK">
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div class="w3-container w3-padding-small">
|
|
<h2>Sign a Message</h2>
|
|
<div id="sign" class="w3-container w3-padding-small">
|
|
<input placeholder="Message" id="signmsg" style="min-width:800px" id="value" type="text" name="message">
|
|
<input type="submit" value="OK" onclick="Sync.POST('sign_message/', {body: {message:byId('signmsg').value}}, r => {showBannerMsg('Signature:', r.data, true, r.data);qr_addr(r.data);})">
|
|
</div>
|
|
</div>
|
|
<script>
|
|
const now = new Date(),
|
|
_1day_ago = new Date(new Date().setDate(now.getDate()-1)).getTime(),
|
|
_7days_ago = new Date(new Date().setDate(now.getDate()-7)).toISOString().substring(0,19)
|
|
|
|
const home_ch_base = Object.assign({}, base_ch_template, {
|
|
"unsettled": ch => (ch.unsettled_balance > 0 ? {innerHTML: `${ch.unsettled_balance.intcomma()} <small class="w3-round w3-border-small w3-border-grey">${ch.htlc_count}</small>`} : {innerHTML: `${ch.unsettled_balance.intcomma()}`, style: {color: 'gray'} }),
|
|
"oRate": ch => ({innerHTML: `<span class="input" style="min-width:100px;max-width:120px;width:100%" data-unit="ₚₚₘ"><input style="text-align:left;width:100%" type="number" min="0" size="6" max="100000" value="${ch.local_fee_rate}" onkeydown="event.keyCode != 13? null : (Sync.POST('chanpolicy/', {body: {chan_id:'${ch.chan_id}', fee_rate: this.value}}, r => {flash(this, r.fee_rate);showBannerMsg('Out Fee Rate', r.fee_rate)}));"/></span>`, style: {backgroundColor: ch.local_disabled ? red : null} }),
|
|
"oBase": ch => ({innerHTML: ch.local_base_fee.intcomma(), style: {backgroundColor: ch.local_disabled ? red : null} }),
|
|
})
|
|
const detailed_ch_template = Object.assign({}, home_ch_base, {
|
|
"o1D": ch => ({innerHTML: `${(ch.amt_routed_out_1day/1000000).toFixed(2)}M <small class="w3-round w3-border-small w3-border-grey">${ch.routed_out_1day}</small>`, style: {color: ch.routed_out_1day > 0 ? `` : 'gray'} }),
|
|
"i1D": ch => ({innerHTML: `${(ch.amt_routed_in_1day/1000000).toFixed(2)}M <small class="w3-round w3-border-small w3-border-grey">${ch.routed_in_1day}</small>`, style: {color: ch.routed_in_1day > 0 ? `` : 'gray'} }),
|
|
"o7D": ch => ({innerHTML: `${(ch.amt_routed_out_7day/1000000).toFixed(2)}M <small class="w3-round w3-border-small w3-border-grey">${ch.routed_out_7day}</small>`, style: {color: ch.routed_out_7day > 0 ? `` : 'gray'} }),
|
|
"i7D": ch => ({innerHTML: `${(ch.amt_routed_in_7day/1000000).toFixed(2)}M <small class="w3-round w3-border-small w3-border-grey">${ch.routed_in_7day}</small>`, style: {color: ch.routed_in_7day > 0 ? `` : 'gray'} }),
|
|
"iRate": ch => ({innerHTML: ch.remote_fee_rate.intcomma(), style: {backgroundColor: ch.remote_disabled ? red : null} }),
|
|
"iBase": ch => ({innerHTML: ch.remote_base_fee.intcomma(), style: {backgroundColor: ch.remote_disabled ? red : null} }),
|
|
"ar_in_target": ch => ({innerHTML: !ch.auto_rebalance ? '---' : `<input inputmode="numeric" style="min-width:50px;max-width:75px;text-align:center;width:100%" id="target" type="text" min="1" max="100" name="target" value="${ch.ar_in_target}" onChange="Sync.PUT('channels/${ch.chan_id}', {body: {ar_in_target: this.value}}, r => {flash(this, r.ar_in_target)});">`, style: ch.auto_rebalance ? {backgroundColor: green} : {color: 'gray', backgroundColor:''} }),
|
|
"auto_rebalance": ch => ({innerHTML: `<input type="submit" value="${ch.auto_rebalance ? 'Dis' : 'En'}able" onclick="Sync.PUT('channels/${ch.chan_id}', {body:{auto_rebalance: ${!ch.auto_rebalance} }}, ch => this.parentElement.changed(ch))">`, style: {backgroundColor: ch.auto_rebalance ? green : null},
|
|
changed: function(newVal) {
|
|
['ar_in_target', 'auto_rebalance'].forEach(col => this.parentElement.cells[col].render(detailed_ch_template[col](newVal)))
|
|
}
|
|
}),
|
|
})
|
|
let {iRate, iBase, ar_in_target, auto_rebalance} = detailed_ch_template
|
|
const inactive_ch_template = Object.assign({}, home_ch_base, {iRate, iBase}, {
|
|
"local_commit": ch => ({innerHTML: ch.local_commit.intcomma()}),
|
|
"local_chan_reserve": ch => ({innerHTML: ch.local_chan_reserve.intcomma()}),
|
|
"last_update": ch => ({innerHTML: formatDate(ch.last_update).replace(" ago", ""), title: adjustTZ(ch.last_update)}),
|
|
"initiator": ch => ({innerHTML: ch.initiator ? 'Local': 'Remote'}),
|
|
}, {ar_in_target, auto_rebalance})
|
|
|
|
let {local_commit, local_chan_reserve, initiator} = inactive_ch_template
|
|
const private_ch_template = Object.assign({}, home_ch_base, {
|
|
"total_sent": ch => ({innerHTML: ch.total_sent.intcomma()}),
|
|
"total_received": ch => ({innerHTML: ch.total_received.intcomma()})
|
|
}, {local_commit, local_chan_reserve, initiator}, {
|
|
"is_active": ch => ({innerHTML: ch.is_active, title: (ch.is_active ? 'Up' : 'Down') + 'time: ' + formatDate(ch.last_update).replace(" ago", ""), style: {backgroundColor: ch.is_active ? green : red} })
|
|
})
|
|
const pending_open_template = {
|
|
"alias": ch => ({innerHTML: `<a href="{{graph_links}}/{{network}}node/${ch.remote_node_pub}" target="_blank">${ch.alias == '' ? ch.remote_node_pub.substr(0,12) : ch.alias }</a>`, title: ch.remote_node_pub}),
|
|
"channel_point": ch => ({innerHTML: `<a href='{{ network_links }}/{{ network }}tx/${ch.funding_txid}' target="_blank">${ch.channel_point}</a>`}),
|
|
"capacity": ch => ({innerHTML: ch.capacity.intcomma(), title: `Commit Fee: ${ch.commit_fee}`}),
|
|
"local_balance": ch => ({innerHTML: ch.local_balance.intcomma()}),
|
|
"remote_balance": ch => ({innerHTML: ch.remote_balance.intcomma()}),
|
|
"af": ch => ({innerHTML: `<form action="/update_pending/" method="post">
|
|
{% csrf_token %}
|
|
<input type="submit" value="${ch.auto_fees ? 'Dis' : 'En'}able">
|
|
<input type="hidden" name="funding_txid" value="${ch.funding_txid}">
|
|
<input type="hidden" name="output_index" value="${ch.output_index}">
|
|
<input type="hidden" name="update_target" value="8">
|
|
<input type="hidden" name="target" value="0">
|
|
</form>`, style: {backgroundColor: ch.auto_fees? 'rgba(46,160,67,0.15)' : 'rgba(248,81,73,0.15)'} }),
|
|
"fee_rate": ch => ({innerHTML: `<form action="/update_pending/" method="post">
|
|
{% csrf_token %}
|
|
<input style="text-align:center" id="target" type="number" min="0" max="100000" name="target" value="${ch.local_fee_rate}">
|
|
<input type="hidden" name="funding_txid" value="${ch.funding_txid}">
|
|
<input type="hidden" name="output_index" value="${ch.output_index}">
|
|
<input type="hidden" name="update_target" value="1">
|
|
</form>`}),
|
|
"base_fee": ch => ({innerHTML: `<form action="/update_pending/" method="post">
|
|
{% csrf_token %}
|
|
<input style="text-align:center" id="target" type="number" min="0" max="100000" name="target" value="${ch.local_base_fee}">
|
|
<input type="hidden" name="funding_txid" value="${ch.funding_txid}">
|
|
<input type="hidden" name="output_index" value="${ch.output_index}">
|
|
<input type="hidden" name="update_target" value="0">
|
|
</form>`}),
|
|
"cltv": ch => ({innerHTML: `<form action="/update_pending/" method="post">
|
|
{% csrf_token %}
|
|
<input style="text-align:center" id="target" type="number" min="18" max="1000" name="target" value="${ch.local_cltv}">
|
|
<input type="hidden" name="funding_txid" value="${ch.funding_txid}">
|
|
<input type="hidden" name="output_index" value="${ch.output_index}">
|
|
<input type="hidden" name="update_target" value="9">
|
|
</form>`}),
|
|
"amt": ch => ({innerHTML: `<form action="/update_pending/" method="post">
|
|
{% csrf_token %}
|
|
<input style="text-align:center" id="target" type="number" min="1" max="100000000" name="target" value="${ch.ar_amt_target}">
|
|
<input type="hidden" name="funding_txid" value="${ch.funding_txid}">
|
|
<input type="hidden" name="output_index" value="${ch.output_index}">
|
|
<input type="hidden" name="update_target" value="2">
|
|
</form>`}),
|
|
"max_cost": ch => ({innerHTML: `<form action="/update_pending/" method="post">
|
|
{% csrf_token %}
|
|
<input style="text-align:center" id="target" type="number" min="1" max="100" name="target" value="${ch.ar_max_cost}">
|
|
<input type="hidden" name="funding_txid" value="${ch.funding_txid}">
|
|
<input type="hidden" name="output_index" value="${ch.output_index}">
|
|
<input type="hidden" name="update_target" value="6">
|
|
</form>`}),
|
|
"otarget": ch => ({innerHTML: `<form action="/update_pending/" method="post">
|
|
{% csrf_token %}
|
|
<input style="text-align:center" id="target" type="number" min="1" max="100" name="target" value="${ch.ar_out_target}">
|
|
<input type="hidden" name="funding_txid" value="${ch.funding_txid}">
|
|
<input type="hidden" name="output_index" value="${ch.output_index}">
|
|
<input type="hidden" name="update_target" value="4">
|
|
</form>`, style: {backgroundColor: !ch.auto_rebalance ? 'rgba(248,81,73,0.15)' : 'rgba(46,160,67,0.15)'} }),
|
|
"itarget": ch => ({innerHTML: `<form action="/update_pending/" method="post">
|
|
{% csrf_token %}
|
|
<input style="text-align:center" id="target" type="number" min="1" max="100" name="target" value="${ch.ar_in_target}">
|
|
<input type="hidden" name="funding_txid" value="${ch.funding_txid}">
|
|
<input type="hidden" name="output_index" value="${ch.output_index}">
|
|
<input type="hidden" name="update_target" value="3">
|
|
</form>`, style: {backgroundColor: !ch.auto_rebalance ? 'rgba(248,81,73,0.15)' : 'rgba(46,160,67,0.15)'} }),
|
|
"ar": ch => ({innerHTML: `<form action="/update_pending/" method="post">
|
|
{% csrf_token %}
|
|
<input type="submit" value="${ch.auto_rebalance ? 'Dis': 'En'}able">
|
|
<input type="hidden" name="funding_txid" value="${ch.funding_txid}">
|
|
<input type="hidden" name="output_index" value="${ch.output_index}">
|
|
<input type="hidden" name="update_target" value="5">
|
|
<input type="hidden" name="target" value="0">
|
|
</form>`, style: {backgroundColor: !ch.auto_rebalance ? 'rgba(248,81,73,0.15)' : 'rgba(46,160,67,0.15)'} }),
|
|
}
|
|
|
|
let {capacity, local_balance, remote_balance} = pending_open_template
|
|
const pending_closed_template = Object.assign({}, {
|
|
"channel_id" : ch => ({innerHTML: `<a href="/channel?=${ch.chan_id}" target="_blank">${ch.short_chan_id}</a>`, title: ch.channel_point}),
|
|
"peer_alias" : ch => ({innerHTML: `<a href="{{ graph_links }}/{{ network }}node/${ch.remote_node_pub}" target="_blank">${ch.alias == '' ? ch.remote_node_pub.substr(0,12) : ch.alias }</a>`, title: ch.remote_node_pub})
|
|
}, {capacity, local_balance, remote_balance },
|
|
{
|
|
"limbo_bal" : ch => ({innerHTML: ch.limbo_balance.intcomma() }),
|
|
"local_commit" : ch => ({innerHTML: ch.local_commit_fee_sat.intcomma() }),
|
|
"closing_tx" : ch => ({innerHTML: `<a href='{{ network_links }}/{{ network }}tx/${ch.closing_txid}' target="_blank">${ch.closing_txid}</a>`}),
|
|
})
|
|
const pending_fclosed_template = Object.assign({}, pending_closed_template)
|
|
pending_fclosed_template["local_commit"] = ch => ({innerHTML: adjustTZ(ch.maturity_datetime), title: ch.blocks_til_maturity.intcomma() })
|
|
|
|
function addTitle(id, results, title = null){
|
|
const container = byId(id.split(" ").join("_").toLowerCase() + '_container')
|
|
if (results.length == 0) {
|
|
container.style.display = 'none'
|
|
return false
|
|
}
|
|
container.style.display = 'block'
|
|
if (container.querySelector('h2')) container.querySelector('h2').innerHTML = null
|
|
container.insertAdjacentHTML('afterbegin', `<h2>${title || id}</h2>`)
|
|
return true
|
|
}
|
|
function build_active(channels, forwards_sum){
|
|
let sum = {capacity: 0, inbound: 0, outbound: 0, unsettled: 0, earned:{d1:0, d7:0}, rOUTed:{d1:{amt:0, count:0}, d7:{amt:0, count:0} } }
|
|
if(!addTitle("Active Channels", channels)) return sum
|
|
|
|
const [activeChannels, template] = [byId("active_channels"), use(detailed_ch_template)]
|
|
|
|
activeChannels.innerHTML = null
|
|
channels.map(ch => { //derived properties
|
|
ch.inbound_can = ch.percent_inbound / ch.ar_in_target
|
|
ch.fee_ratio = ch.local_fee_rate == 0 ? 100 : parseInt(ch.remote_fee_rate*100/ch.local_fee_rate)
|
|
ch.fee_check = parseInt(ch.fee_ratio*100/ch.ar_max_cost)
|
|
|
|
sum.capacity += ch.capacity
|
|
sum.inbound += ch.remote_balance
|
|
sum.outbound += ch.local_balance
|
|
sum.unsettled += ch.unsettled_balance
|
|
|
|
if(forwards_sum[ch.chan_id]!=undefined){
|
|
ch['routed_in_1day'] = forwards_sum[ch.chan_id].i1dc
|
|
ch['amt_routed_in_1day'] = forwards_sum[ch.chan_id].i1d
|
|
ch['routed_out_1day'] = forwards_sum[ch.chan_id].o1dc
|
|
ch['amt_routed_out_1day'] = forwards_sum[ch.chan_id].o1d
|
|
ch['routed_in_7day'] = forwards_sum[ch.chan_id].i7dc
|
|
ch['amt_routed_in_7day'] = forwards_sum[ch.chan_id].i7d
|
|
ch['routed_out_7day'] = forwards_sum[ch.chan_id].o7dc
|
|
ch['amt_routed_out_7day'] = forwards_sum[ch.chan_id].o7d
|
|
}else{
|
|
ch['routed_in_1day'] = 0
|
|
ch['amt_routed_in_1day'] = 0
|
|
ch['routed_out_1day'] = 0
|
|
ch['amt_routed_out_1day'] = 0
|
|
ch['routed_in_7day'] = 0
|
|
ch['amt_routed_in_7day'] = 0
|
|
ch['routed_out_7day'] = 0
|
|
ch['amt_routed_out_7day'] = 0
|
|
}
|
|
|
|
return ch
|
|
}).sort((ch1, ch2) => ch1.percent_outbound - ch2.percent_outbound)
|
|
.forEach(ch => activeChannels.append(template.render(ch)))
|
|
|
|
byId('public_capacity').innerHTML = sum.capacity.intcomma()
|
|
return update_liq('active_', sum)
|
|
}
|
|
function build_private(channels){
|
|
let act = {outbound: 0, inbound: 0}, ina = {outbound: 0, inbound: 0}
|
|
let sum = {earned:{d1:0, d7:0}, rOUTed:{d1:{amt:0, count:0}, d7:{amt:0, count:0} } }
|
|
if(!addTitle("Private Channels", channels)) return Object.assign({}, sum, {outbound: act.outbound + ina.outbound, inbound: act.inbound + ina.inbound})
|
|
|
|
let [capacity, active_count, table, template] = [0,0,byId('private_channels'),use(private_ch_template)]
|
|
table.innerHTML = null
|
|
channels.forEach(ch => {
|
|
capacity += ch.capacity
|
|
if(ch.is_active) {
|
|
active_count += 1
|
|
act.outbound += ch.local_balance
|
|
act.inbound += ch.remote_balance
|
|
}
|
|
else {
|
|
ina.outbound += ch.local_balance
|
|
ina.inbound += ch.remote_balance
|
|
}
|
|
|
|
table.append(template.render(ch))
|
|
})
|
|
|
|
byId("total_channels").innerHTML = (byId("total_channels").innerHTML.toInt() - channels.length)
|
|
byId("public_channels_count").innerHTML = (byId("public_channels_count").innerHTML.toInt() - active_count)
|
|
byId("private_capacity").innerHTML = capacity.intcomma()
|
|
byId("private_liquidity").innerHTML = (act.outbound + ina.outbound).intcomma()
|
|
byId("private_active").innerHTML = active_count
|
|
byId("private_count").innerHTML = channels.length
|
|
byId("private_stats").style = {'display': 'block'}
|
|
|
|
return Object.assign({}, sum, {outbound: act.outbound + ina.outbound, inbound: act.inbound + ina.inbound})
|
|
}
|
|
function build_inactive(channels){
|
|
let sum = {capacity: 0, inbound: 0, outbound: 0, unsettled: 0}
|
|
if(!addTitle("Inactive Channels", channels)) return sum
|
|
|
|
const table = byId('inactive_channels'), template = use(inactive_ch_template), public_capacity = byId('public_capacity')
|
|
table.innerHTML = null
|
|
channels.forEach(ch => {
|
|
sum.capacity += ch.capacity
|
|
sum.inbound += ch.remote_balance
|
|
sum.outbound += ch.local_balance
|
|
sum.unsettled += ch.unsettled_balance
|
|
table.append(template.render(ch))
|
|
})
|
|
|
|
public_capacity.innerHTML = (public_capacity.innerHTML.toInt() + sum.capacity).intcomma()
|
|
return update_liq('inactive_', sum)
|
|
}
|
|
function update_liq(status, sum){
|
|
for (id of ['inbound', 'outbound', 'unsettled']){
|
|
const [value, total] = [sum[id], byId('total_'+id)]
|
|
|
|
byId(status+id).innerHTML = value.intcomma()
|
|
}
|
|
return sum
|
|
}
|
|
async function update_summary(node_info, payments7d, onchain_task, closures_task, active, inactive, private, forwards_summary, inv_rev_task){
|
|
let offChain = {d1: {fee:0, amt:0, inv_rev:0}, d7: {fee:0, amt:0, inv_rev:0} }
|
|
for(p of payments7d){
|
|
if(adjustTZ(p.creation_date) >= _1day_ago){
|
|
offChain.d1.fee += p.fee
|
|
offChain.d1.amt += p.value
|
|
}
|
|
offChain.d7.fee += p.fee
|
|
offChain.d7.amt += p.value
|
|
}
|
|
for(inv of (await inv_rev_task).results){
|
|
if(adjustTZ(inv.creation_date) >= _1day_ago){
|
|
offChain.d1.inv_rev += inv.amt_paid
|
|
}
|
|
offChain.d7.inv_rev += inv.amt_paid
|
|
}
|
|
let chainCosts = {d1: 0, d7:0}
|
|
for(tx of (await onchain_task).results){
|
|
if(adjustTZ(tx.time_stamp) >= _1day_ago){
|
|
chainCosts.d1 += tx.fee
|
|
}
|
|
chainCosts.d7 += tx.fee
|
|
}
|
|
for(close of (await closures_task).results){
|
|
if(close.close_height >= (node_info.block.height - 144)){
|
|
chainCosts.d1 += close.closing_costs
|
|
}
|
|
chainCosts.d7 += close.closing_costs
|
|
}
|
|
function merge(obj, other){
|
|
const result = Object.assign({}, obj)
|
|
|
|
for(let [key, value] of Object.entries(other)){
|
|
if(value instanceof Object) result[key] = merge(result[key], value) //deep sum
|
|
else result[key] = (result[key] || 0) + value
|
|
}
|
|
return result
|
|
}
|
|
|
|
byId('total_inbound').innerHTML = (active.inbound + inactive.inbound).intcomma()
|
|
byId('total_outbound').innerHTML = (active.outbound + inactive.outbound).intcomma()
|
|
byId('total_unsettled').innerHTML = (active.unsettled + inactive.unsettled).intcomma()
|
|
|
|
const public = merge(active, inactive), sum = merge(public, private)
|
|
const [total_outbound, total_bal] = [sum.outbound || 1, byId('total_balance')]
|
|
byId('liq_ratio').innerHTML = (public.inbound*100/public.outbound||1).intcomma()+'%'
|
|
total_bal.innerHTML = (total_bal.innerHTML.toInt() + sum.outbound).intcomma()
|
|
|
|
for(d of [1,7]){ //build 1 & 7 days table statistics
|
|
const earned = forwards_summary[`earned${d}d`]+offChain[`d${d}`].inv_rev, routed = {'amt':forwards_summary[`o${d}d`],'count':forwards_summary[`o${d}dc`]}, chaincosts = chainCosts[`d${d}`], offchain = offChain[`d${d}`], costs = chaincosts + offchain.fee
|
|
byId(`routed_${d}day`).innerHTML = `${routed.amt.intcomma()} <small class="w3-round w3-border-small w3-border-grey">${routed.count.intcomma()}</small>`
|
|
byId(`earned_${d}day`).innerHTML = `${earned.intcomma()} <small class="w3-round w3-border-small w3-border-grey">${(earned*1000000/(routed.amt||1)).intcomma()}ₚₚₘ</small>`
|
|
byId(`fees_${d}day`).innerHTML = `${offchain.fee.intcomma()} <small class="w3-round w3-border-small w3-border-grey">${(offchain.fee*1000000/(offchain.amt||1)).intcomma()}ₚₚₘ</small>`
|
|
byId(`onchain_costs_${d}day`).innerHTML = chaincosts.intcomma()
|
|
byId(`percent_cost_${d}day`).innerHTML = (earned ? costs*100/earned : 0).intcomma()+'%'
|
|
byId(`profit_per_outbound_${d}d`).innerHTML = `<span class="w3-round w3-border-small w3-border-grey">⚡${((earned - offchain.fee)*1000000/total_outbound).intcomma()}ₚₚₘ</span>
|
|
<span class="w3-round w3-border-small w3-border-grey">⛓️${((earned - costs)*1000000/total_outbound).intcomma()}ₚₚₘ</span>`
|
|
byId(`routed_${d}day_percent`).innerHTML = parseFloat((routed.amt*100/total_outbound).toFixed(1)).toLocaleString()+'%'
|
|
}
|
|
}
|
|
async function loadForwards(){
|
|
const lastId = forwardsTable.tBodies[0].lastChild.objId
|
|
const forwards_task = GET('forwards', {data: {id__lt: lastId, limit: 10} })
|
|
build_next_routed(forwards_task)
|
|
}
|
|
async function build_next_routed(forwards_task){
|
|
let next_forwards = (await forwards_task).results
|
|
if(next_forwards.length==0){
|
|
byId("loadMoreForwards").style.display = "none"
|
|
return
|
|
}
|
|
const tableBody = forwardsTable.querySelector("tbody")
|
|
for (f of next_forwards){
|
|
f.amt_in = f.amt_in_msat/1000
|
|
f.amt_out = f.amt_out_msat/1000
|
|
f.ppm = f.fee*1000000/f.amt_out
|
|
tableBody.appendChild(use(routed_template).render(f))
|
|
}
|
|
}
|
|
async function build_routed(forwards7d){
|
|
let remaining = 20-forwards7d.length, template = use(routed_template), forwards = forwards7d
|
|
if(remaining > 0)
|
|
forwards = [...forwards, ...(await GET('forwards', {data: {forward_date__lt: _7days_ago, limit: remaining} })).results]
|
|
|
|
if(!addTitle('Routed', forwards, `Last <a href="/forwards" target="_blank">Payments Routed</a>`)) return
|
|
const table = byId('routed')
|
|
table.innerHTML = null
|
|
for(f of forwards.slice(0,20)){
|
|
f.amt_in = f.amt_in_msat/1000
|
|
f.amt_out = f.amt_out_msat/1000
|
|
f.ppm = f.fee*1000000/f.amt_out
|
|
table.append(template.render(f))
|
|
}
|
|
}
|
|
async function loadPayments(){
|
|
const lastId = paymentsTable.tBodies[0].lastChild.objId
|
|
const payments_task = GET('payments', {data: {status__lt: 3, index__lt: lastId, limit: 10} })
|
|
let next_payments = (await payments_task).results
|
|
if(next_payments.length==0){
|
|
byId("loadMorePayments").style.display = "none"
|
|
return
|
|
}
|
|
build_next_payments(next_payments)
|
|
}
|
|
async function build_next_payments(next_payments){
|
|
const tableBody = paymentsTable.querySelector("tbody")
|
|
for (p of next_payments){
|
|
p.ppm = p.fee*1000000/p.value
|
|
tableBody.appendChild(use(payments_template).render(p))
|
|
}
|
|
}
|
|
async function build_payments(payments7d){
|
|
let remaining = 10-payments7d.length, template = use(payments_template), payments = payments7d
|
|
if(remaining > 0) payments = [...payments, ...(await GET('payments', {data: {creation_date__lt: _7days_ago, status__lt:3, limit: remaining} })).results]
|
|
|
|
if(!addTitle('Payments', payments, `Last 10 <a href="/payments" target="_blank">Payments</a>`)) return
|
|
const table = byId('payments')
|
|
table.innerHTML = null
|
|
for(p of payments.slice(0,10)){
|
|
p.ppm = p.fee*1000000/p.value
|
|
table.append(template.render(p))
|
|
}
|
|
}
|
|
async function loadInvoices(){
|
|
const lastId = invoicesTable.tBodies[0].lastChild.objId
|
|
const invoices_task = GET('invoices', {data: {state__lt: 2, index__lt: lastId, limit: 10} })
|
|
let next_invoices = (await invoices_task).results
|
|
if(next_invoices.length==0){
|
|
byId("loadMoreInvoices").style.display = "none"
|
|
return
|
|
}
|
|
build_next_invoices(next_invoices)
|
|
}
|
|
async function build_next_invoices(next_invoices){
|
|
const tableBody = invoicesTable.querySelector("tbody")
|
|
for (inv of next_invoices){
|
|
tableBody.appendChild(use(invoices_template).render(inv))
|
|
}
|
|
}
|
|
function build_invoices(invoices){
|
|
if(!addTitle('Invoices', invoices, `Last 10 <a href="/invoices" target="_blank">Invoices</a>`)) return
|
|
const table = byId('invoices'), template = use(invoices_template)
|
|
for(inv of invoices) {
|
|
table.append(template.render(inv))
|
|
}
|
|
}
|
|
async function loadFailedHTLCs(){
|
|
const lastId = failedhtlcsTable.tBodies[0].lastChild.objId
|
|
const failedhtlcs_task = GET('failedhtlcs', {data: {wire_failure__lt: 99, id__lt: lastId, limit: 10} })
|
|
let next_failed_htlcs = (await failedhtlcs_task).results
|
|
if(next_failed_htlcs.length==0){
|
|
byId("loadMoreFailedHTLCs").style.display = "none"
|
|
return
|
|
}
|
|
build_failedhtlcs(next_failed_htlcs)
|
|
}
|
|
async function build_failedhtlcs(failed_htlcs){
|
|
const tableBody = failedhtlcsTable.querySelector("tbody")
|
|
for (f of failed_htlcs){
|
|
tableBody.appendChild(use(failedHTLCs_template).render(f))
|
|
}
|
|
}
|
|
function build_failedHTLC(fHTLCs){
|
|
if(!addTitle('failed_htlcs', fHTLCs, `Last <a href="/failed_htlcs" target="_blank">Failed HTLCs</a>`)) return
|
|
const table = byId('failed_htlcs'), template = use(failedHTLCs_template)
|
|
for(htlc of fHTLCs){
|
|
table.append(template.render(htlc))
|
|
}
|
|
}
|
|
function process_forwards(forwards7d){
|
|
let forwards_sum = {}
|
|
let forwards_summary = { o1d: 0, o1dc: 0, o7d: 0, o7dc: 0, earned1d: 0, earned7d: 0 }
|
|
for(f of forwards7d){
|
|
chan_out = f.chan_id_out
|
|
chan_in = f.chan_id_in
|
|
if(forwards_sum[chan_out] === undefined){ forwards_sum[chan_out]={o1d:0, o1dc:0, o7d:0, o7dc:0, i1d:0, i1dc:0, i7d:0, i7dc:0} }
|
|
if(forwards_sum[chan_in] === undefined){ forwards_sum[chan_in]={o1d:0, o1dc:0, o7d:0, o7dc:0, i1d:0, i1dc:0, i7d:0, i7dc:0} }
|
|
if(adjustTZ(f.forward_date) >= _1day_ago){
|
|
forwards_sum[chan_out].o1d += f.amt_out_msat/1000
|
|
forwards_sum[chan_out].o1dc += 1
|
|
forwards_sum[chan_in].i1d += f.amt_in_msat/1000
|
|
forwards_sum[chan_in].i1dc += 1
|
|
forwards_summary.o1d += f.amt_out_msat/1000
|
|
forwards_summary.o1dc += 1
|
|
forwards_summary.earned1d += f.fee
|
|
}
|
|
forwards_sum[chan_out].o7d += f.amt_out_msat/1000
|
|
forwards_sum[chan_out].o7dc += 1
|
|
forwards_sum[chan_in].i7d += f.amt_in_msat/1000
|
|
forwards_sum[chan_in].i7dc += 1
|
|
forwards_summary.o7d += f.amt_out_msat/1000
|
|
forwards_summary.o7dc += 1
|
|
forwards_summary.earned7d += f.fee
|
|
}
|
|
return [forwards_sum, forwards_summary]
|
|
}
|
|
|
|
function buildMainStats(node_info) {
|
|
const lnd = byId('lnd')
|
|
lnd.classList.remove('w3-red')
|
|
lnd.classList.remove('w3-green')
|
|
lnd.classList.toggle('w3-red', !node_info.synced_to_graph)
|
|
lnd.classList.toggle('w3-green', node_info.synced_to_graph)
|
|
|
|
const timechain = byId('timechain')
|
|
timechain.classList.remove('w3-border-red')
|
|
timechain.classList.remove('w3-border-green')
|
|
timechain.classList.toggle('w3-border-red', !node_info.synced_to_chain)
|
|
timechain.classList.toggle('w3-border-green', node_info.synced_to_chain)
|
|
timechain.title = `Chain ${!node_info.synced_to_chain ? 'Not' : ''} Synced`
|
|
timechain.innerHTML = node_info.chains.join(', ') + ` | ${node_info.block.height} #${node_info.block.hash}`
|
|
|
|
byId('public_channels_count').innerHTML = node_info.num_active_channels
|
|
byId('total_channels').innerHTML = node_info.num_active_channels + node_info.num_inactive_channels
|
|
byId('peers').innerHTML = `<a href="/peers" target="_blank">Peers</a>: ${node_info.num_peers}`
|
|
if(node_info.db_size > 0) byId('dbSize').innerHTML = `DB Size: ${node_info.db_size} GB`
|
|
|
|
byId('total_balance').innerHTML = node_info.balance.total.intcomma()
|
|
byId('onChain').innerHTML = `Onchain: ${node_info.balance.onchain.intcomma()}`
|
|
byId('confirmed').innerHTML = `Confirmed: ${node_info.balance.confirmed.intcomma()}`
|
|
byId('unconfirmed').innerHTML = `Unconfirmed: ${node_info.balance.unconfirmed.intcomma()}`
|
|
byId('limbo').innerHTML = `Limbo: ${node_info.balance.limbo.intcomma()}`
|
|
}
|
|
function build(id, template_arg, channels){
|
|
if(!addTitle(id, channels)){
|
|
return;
|
|
}
|
|
const table = byId(id.split(" ").join("_").toLowerCase()), template = use(template_arg)
|
|
table.innerHTML = null
|
|
channels.forEach(ch => table.append(template.render(ch)))
|
|
}
|
|
async function buildMain() {
|
|
const node_info = await GET('node_info', {data: {limit:1}})
|
|
const inv_rev_task = GET('invoices', {data: {state__lt: 2, is_revenue: true, settle_date__gte: _7days_ago}})
|
|
const onchain_task = GET('onchain', {data: {time_stamp__gte: _7days_ago} })
|
|
const forwards7d_task = GET('forwards', {data: {forward_date__gte: _7days_ago} })
|
|
const payments_task = GET('payments', {data: {creation_date__gte: _7days_ago, status__lt: 3} })
|
|
buildMainStats(node_info)
|
|
const closures_task = GET('closures', {data: {close_height__gte: (node_info.block.height - 1008) } })
|
|
|
|
let [updates, pending_htlcs, active, inactive, private] = [0,0,[],[],[]]
|
|
for (ch of (await GET('channels', {data: {is_open:true} })).results){ //basic setup
|
|
updates += ch.num_updates
|
|
pending_htlcs += ch.htlc_count
|
|
ch.local_balance += ch.pending_outbound
|
|
ch.remote_balance += ch.pending_inbound
|
|
ch.percent_inbound = parseInt(ch.remote_balance*100/ch.capacity)
|
|
ch.percent_outbound = parseInt(ch.local_balance*100/ch.capacity)
|
|
|
|
if(ch.private) private.push(ch)
|
|
else (ch.is_active ? active : inactive).push(ch)
|
|
}
|
|
|
|
byId("updates").innerHTML = updates.intcomma()
|
|
byId("pending_htlcs").innerHTML = pending_htlcs.intcomma()
|
|
|
|
let [forwards7d, payments7d] = [(await forwards7d_task).results, (await payments_task).results]
|
|
let [forwards_sum, forwards_summary] = process_forwards(forwards7d)
|
|
let [activeSum, privateSum, inactiveSum] = [build_active(active, forwards_sum), build_private(private), build_inactive(inactive)]
|
|
update_summary(node_info, payments7d, onchain_task, closures_task, activeSum, inactiveSum, privateSum, forwards_summary, inv_rev_task)
|
|
build('Pending Open', pending_open_template, node_info.pending_open||[])
|
|
build('Pending Closed', pending_closed_template, node_info.pending_closed||[])
|
|
build('Waiting For Close', pending_closed_template, node_info.waiting_for_close||[])
|
|
build('Pending Force Closed', pending_fclosed_template, node_info.pending_force_closed||[])
|
|
build_routed(forwards7d)
|
|
build_payments(payments7d)
|
|
byId("datetime").innerHTML = new Date().toLocaleTimeString();
|
|
await auto_refresh(buildMain)
|
|
}
|
|
document.addEventListener('DOMContentLoaded', async () => {
|
|
const builder = buildMain()
|
|
const invoices_task = GET('invoices', {data: {state__lt: 2, limit: 10} })
|
|
const failedHTLCs_task = GET('failedhtlcs', {data: {wire_failure__lt: 99, limit:10} })
|
|
build_invoices((await invoices_task).results)
|
|
build_failedHTLC((await failedHTLCs_task).results)
|
|
await builder
|
|
})
|
|
</script>
|
|
{% endblock %}
|