10 Commits

Author SHA1 Message Date
8a9b1b21d1 original page 2026-07-17 10:43:25 +00:00
e71051d869 Added details of data update times
Added details of data update times
2026-07-17 10:10:46 +00:00
e4707b7fd7 Merge pull request 'Align index.html with yearly-listing terms' (#3) from donkey-ai/bal-welist-website:fix/index-yearly-model-consistency into main
Reviewed-on: bitcoinafterlife/bal-welist-website#3
2026-07-16 22:32:47 +00:00
Claude (draft, do not push as-is)
c297fb30db Match panel heights in Add-server / How-ranking-works grid
align-items:start made each .panel size to its own content, so the
two side-by-side cards in .add-grid ended up with different heights
whenever their text length differed. Switch to align-items:stretch
(+ flex column on the panels) so both cards always match height.
2026-07-17 00:32:27 +02:00
Claude (draft, do not push as-is)
7be9959241 Align index.html with yearly-listing terms
- Sidebar: replace vague 'Bid-based ranking' lead with the actual
  4-criteria ordering (excess donated, total paid, base fee, points)
- Sidebar: drop 'Early-adopter guarantee 12 months' (contradicts
  Terms 2.04 pro-rata conversion of legacy balances); add
  'Listing period 365 days' / 'Renewal window' facts instead
- Info block 03: rename 'Early Adopter Incentives' -> 'Listing &
  Renewal', remove the unconditional 12-month promise, describe the
  real pay -> listed -> renewal -> expiry lifecycle from Terms Sec. 2
- Rename 'Next deposit' -> 'Next renewal' (label + status text)
- Fix s-block stat: use max(last_block) across all servers instead of
  getFiltered()[0].last_block, since the list order no longer tracks
  block height
- Clarify 'Server added' -> 'Server verified... listed after 3
  confirmations', matching Terms 2.01 (listing starts at 3 confs,
  not at submission)

Note: result.donation (singular) confirmed correct against the
current API by backend — left untouched, no change needed here.
2026-07-17 00:08:06 +02:00
57347e87e7 Update listing terms and UI copy for yearly-fee model; fix API field reference 2026-07-16 17:33:33 -04:00
a8d1c5b214 Fix donations field: use singular donation from backend 2026-07-16 13:36:05 -04:00
0298ec737e docs: clarify payment affects list position 2026-07-16 12:58:04 -04:00
add0b2cddc add expiry date to weinfo page, fix last update population, move last update to first column 2026-07-14 08:28:19 -04:00
74fe167137 refactor: move fetch after terms accept, add donations/nextdep/unconfirmed balance, fix download blob 2026-07-14 08:01:03 -04:00
3 changed files with 101 additions and 67 deletions

View File

@@ -117,7 +117,8 @@ tbody tr:hover{background:color-mix(in srgb,var(--accent-soft) 50%,transparent)}
.num{font-family:var(--mono);font-variant-numeric:tabular-nums}
.score{font-family:var(--display);font-weight:600;font-size:15px}
.add-grid{display:grid;grid-template-columns:1.3fr 1fr;gap:24px;margin:40px 0;align-items:start}
.add-grid{display:grid;grid-template-columns:1.3fr 1fr;gap:24px;margin:40px 0;align-items:stretch}
.add-grid>.panel{display:flex;flex-direction:column}
.panel{
background:var(--panel);border:1px solid var(--line);
border-radius:var(--radius);padding:24px;box-shadow:var(--shadow);
@@ -286,7 +287,7 @@ footer a{color:var(--accent);text-decoration:none}
<div class="add-grid">
<div class="panel">
<h2>Add your Will Executor</h2>
<p class="lead">Enter your server URL. It will be checked online before it appears in the list.</p>
<p class="lead">Enter your server URL. It will appear in the list once your deposit is confirmed (typically about 30 minutes).</p>
<div class="form-row">
<input type="text" id="url" placeholder="https://we.bitcoin-after.life">
</div>
@@ -300,7 +301,7 @@ footer a{color:var(--accent);text-decoration:none}
<div class="terms-box" id="terms-box">
<p class="lead">You are about to add this server:</p>
<p><a id="terms-url" href="#" target="_blank" rel="noopener" class="mono"></a></p>
<p style="font-size:13px;margin-top:4px"><strong>Make sure the link above works correctly before paying. No refunds or URL changes after payment.</strong></p>
<p style="font-size:13px;margin-top:4px"><strong>Make sure the link above works correctly before paying. No refunds after payment. URL changes require proof of ownership — see the <a href="/terms.html" target="_blank">Terms</a>.</strong></p>
<p style="font-size:14px">
<label>
<input type="checkbox" id="terms-check">
@@ -315,10 +316,11 @@ footer a{color:var(--accent);text-decoration:none}
<div class="row"><span>Chain</span><span id="iv-chain"></span></div>
<div class="row"><span>Address</span><span id="iv-addr"></span></div>
<div class="row"><span>Balance</span><span id="iv-balance"></span></div>
<div class="row"><span>Status</span><span id="iv-status"></span></div>
<div class="row"><span>Donations</span><span id="iv-donations"></span></div>
<div class="row"><span>Contact</span><span id="iv-contact"></span></div>
<div class="row"><span>Server</span><span id="iv-url"></span></div>
<div class="row"><span>Will Executor</span><span><a id="iv-weinfo" href="#" target="_blank"></a></span></div>
<div class="row" id="iv-nextdep-row" style="display:none"><span>Next renewal</span><span id="iv-nextdep"></span></div>
<div id="qrcode"></div>
<button class="btn-share" id="share-btn">Share invoice</button>
<p class="note">
@@ -330,10 +332,11 @@ footer a{color:var(--accent);text-decoration:none}
<div class="panel">
<h2>How ranking works</h2>
<p class="lead">Bid-based ranking: higher contributions rank higher.</p>
<div style="display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--line);font-size:13px"><span style="color:var(--muted)">Deposit threshold</span><b class="mono"><span id="thr-min">50,000</span> sats</b></div>
<div style="display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--line);font-size:13px"><span style="color:var(--muted)">Plugin integration</span><b>automatic</b></div>
<div style="display:flex;justify-content:space-between;padding:8px 0;font-size:13px"><span style="color:var(--muted)">Early-adopter guarantee</span><b>12 months</b></div>
<p class="lead">Ordered by excess donated, then total paid, then base fee (lower first), then WeTime points.</p>
<div style="display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--line);font-size:13px"><span style="color:var(--muted)">Yearly fee</span><b class="mono"><span id="thr-min">50,000</span> sats</b></div>
<div style="display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--line);font-size:13px"><span style="color:var(--muted)">Listing period</span><b>365 days</b></div>
<div style="display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--line);font-size:13px"><span style="color:var(--muted)">Renewal window</span><b>from 6 months before expiry</b></div>
<div style="display:flex;justify-content:space-between;padding:8px 0;font-size:13px"><span style="color:var(--muted)">Plugin integration</span><b>automatic</b></div>
</div>
</div>
@@ -348,13 +351,13 @@ footer a{color:var(--accent);text-decoration:none}
<div class="info-block">
<span class="ic">02</span>
<h3>Ranking System</h3>
<p>This leaderboard operates on a bid-based ranking system where higher contributions result in better positioning.</p>
<p>This leaderboard is ordered by excess donated (over the yearly fee), then total amount paid, then lower base fee, then WeTime points.</p>
</div>
<div class="info-block">
<span class="ic">03</span>
<h3>Early Adopter Incentives</h3>
<p>To support the project's initial launch phase, we plan to maintain the first Will Executor servers for a minimum of 12 months, rewarding early participants. Subsequently, premium positions will be available through an auction system based on protocol revenue generation.</p>
<h3>Listing &amp; Renewal</h3>
<p>A flat yearly fee buys 365 days of presence in the list, starting when your payment reaches 3 confirmations. You can renew for one additional year at any time from 6 months before expiry; listings that are not renewed are removed at expiry. See the <a href="/terms.html" target="_blank">Terms</a> for details.</p>
</div>
<div class="info-block">
@@ -371,6 +374,7 @@ footer a{color:var(--accent);text-decoration:none}
<div class="wrap">
<span>Bitcoin After Life — Will Executor List</span>
<a href="mailto:welist@bitcoin-after.life">welist@bitcoin-after.life</a>
<a href="/terms.html">Terms</a>
<a class="mono" id="commit-link" href="https://bitcoin-after.life/gitea/bitcoinafterlife/bal-welist-website/commit/COMMIT_HASH_PLACEHOLDER">COMMIT_HASH_PLACEHOLDER</a>
</div>
</footer>
@@ -383,18 +387,6 @@ let minAmount = 50000;
const fmt = n => (n != null ? Number(n).toLocaleString('en-US') : '—');
const trunc = (s, m=40) => s && s.length > m ? s.slice(0, m) + '…' : s;
const statusMap = {
'OK': 'Health check passed',
'TIMEOUT': 'Connection timeout',
'CONNECTION': 'Connection error',
'STATUS': 'HTTP error',
'WRONG REPLY': 'Invalid JSON response',
'REQUEST': 'Request error',
'BODY': 'Body read error',
'DECODE': 'Decode error',
'KO': 'Generic error'
};
function getChain() {
const btn = document.querySelector('#chainSeg [aria-pressed="true"]');
return btn ? btn.dataset.c : 'bitcoin';
@@ -402,9 +394,7 @@ function getChain() {
function getFiltered() {
return allServers
.filter(d => withTor ? true : !d.onion)
.filter(d => (d.url + " " + (d.info||"")).toLowerCase().includes(q.toLowerCase()))
.sort((a, b) => (b.last_block||0) - (a.last_block||0) || (b.points||0) - (a.points||0) || (b.count_win||0) - (a.count_win||0) || (a.base_fee||0) - (b.base_fee||0) || (b.version||'').localeCompare(a.version||''));
.filter(d => (d.url + " " + (d.info||"")).toLowerCase().includes(q.toLowerCase()));
}
function render() {
@@ -448,11 +438,12 @@ function render() {
</div>`);
});
const online = allServers.filter(d => d.status === "online" || d.status === "OK").length;
const online = allServers.filter(d => d.status === "OK").length;
document.getElementById('s-online').textContent = online;
document.getElementById('s-total').textContent = allServers.length;
const top = getFiltered()[0];
document.getElementById('s-block').textContent = top ? fmt(top.last_block) : '—';
// Highest known block across all servers (the list is ranked by fee/points, not by height)
const maxBlock = allServers.reduce((m, d) => Math.max(m, d.last_block || 0), 0);
document.getElementById('s-block').textContent = maxBlock ? fmt(maxBlock) : '—';
}
async function fetchData() {
@@ -521,18 +512,45 @@ document.getElementById('submit').addEventListener('click', async () => {
document.getElementById('iv-chain').textContent = '—';
document.getElementById('iv-addr').textContent = '—';
document.getElementById('iv-balance').textContent = '—';
document.getElementById('iv-donations').textContent = '—';
document.getElementById('iv-url').textContent = '—';
document.getElementById('iv-contact').textContent = '—';
document.getElementById('iv-weinfo').href = '#';
document.getElementById('iv-weinfo').textContent = '—';
document.getElementById('iv-nextdep-row').style.display = 'none';
document.getElementById('iv-nextdep').textContent = '—';
document.getElementById('iv-min').parentElement.style.display = '';
document.getElementById('iv-addr').parentElement.style.display = '';
document.getElementById('qrcode').style.display = '';
document.getElementById('share-btn').style.display = '';
document.getElementById('qrcode').innerHTML = '';
document.getElementById('qrcode').style.cursor = '';
document.getElementById('qr-overlay').classList.remove('show');
document.getElementById('share-btn').textContent = 'Share invoice';
document.getElementById('terms-box').classList.remove('show');
document.getElementById('terms-box').style.display = '';
document.getElementById('terms-check').checked = false;
document.getElementById('terms-accept').disabled = true;
const chain = getChain();
const infoUrl = url + '/' + chain + '/info';
document.getElementById('terms-url').href = infoUrl;
document.getElementById('terms-url').textContent = url;
document.getElementById('terms-box').classList.add('show');
document.getElementById('terms-box').scrollIntoView({ behavior: 'smooth', block: 'nearest' });
});
document.getElementById('terms-check').addEventListener('change', function () {
document.getElementById('terms-accept').disabled = !this.checked;
});
document.getElementById('terms-accept').addEventListener('click', async function () {
document.getElementById('terms-box').classList.remove('show');
const urlInput = document.getElementById('url');
let url = urlInput.value.trim();
if (!url.startsWith('http://') && !url.startsWith('https://')) {
url = 'https://' + url;
}
const chain = getChain();
try {
const response = await fetch('/data', {
method: 'POST',
@@ -548,10 +566,10 @@ document.getElementById('submit').addEventListener('click', async () => {
document.getElementById('iv-min').textContent = fmt(result.min_amount != null ? result.min_amount : minAmount) + ' sats';
document.getElementById('iv-chain').textContent = result.chain || chain;
document.getElementById('iv-addr').textContent = result.address || '—';
document.getElementById('iv-balance').textContent = result.balance != null ? fmt(result.balance) : '—';
const statusVal = result.status || '—';
const st = (statusVal||'').split(':')[0];
document.getElementById('iv-status').textContent = statusMap[st] || '—';
const balStr = result.balance != null ? fmt(result.balance) : null;
const uncStr = result.unconfirmed_balance != null ? fmt(result.unconfirmed_balance) : null;
document.getElementById('iv-balance').textContent = balStr ? (uncStr ? balStr + ' (' + uncStr + ')' : balStr) : '—';
document.getElementById('iv-donations').textContent = result.donation != null ? fmt(result.donation) : '—';
document.getElementById('iv-contact').textContent = document.getElementById('contact').value.trim() || '—';
const weLink = result.id ? `/weinfo/${result.chain || chain}/${result.id}` : null;
const weinfoEl = document.getElementById('iv-weinfo');
@@ -562,7 +580,23 @@ document.getElementById('submit').addEventListener('click', async () => {
weinfoEl.href = '#';
weinfoEl.textContent = '—';
}
const amountSats = result.amount != null ? result.amount : minAmount;
const serverUrl = result.url || url;
document.getElementById('iv-url').textContent = serverUrl;
if (result.payment_needed === false) {
document.getElementById('iv-min').parentElement.style.display = 'none';
document.getElementById('iv-addr').parentElement.style.display = 'none';
document.getElementById('qrcode').style.display = 'none';
document.getElementById('share-btn').style.display = 'none';
document.getElementById('iv-nextdep').textContent = result.next_deposit_date || '—';
document.getElementById('iv-nextdep-row').style.display = 'flex';
document.getElementById('iv-status').className = 'ok-line';
document.getElementById('iv-status').textContent = 'Server is already listed. Next renewal: ' + (result.next_deposit_date || '—');
const inv = document.getElementById('invoice');
inv.classList.add('show');
inv.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
return;
}
const amountSats = result.min_amount != null ? result.min_amount : minAmount;
const amountBtc = (amountSats / 1e8).toFixed(8).replace(/\.?0+$/, '');
const invoiceuri = `bitcoin:${result.address}?amount=${amountBtc}&label=BAL&message=Order+For+url+%26+${encodeURIComponent(url)}`;
const qrEl = document.getElementById('qrcode');
@@ -591,41 +625,25 @@ document.getElementById('submit').addEventListener('click', async () => {
});
}
};
const serverUrl = result.url || url;
const infoUrl = serverUrl + '/' + (result.chain || chain) + '/info';
document.getElementById('iv-url').textContent = serverUrl;
document.getElementById('terms-url').href = infoUrl;
document.getElementById('terms-url').textContent = serverUrl;
document.getElementById('terms-box').classList.add('show');
document.getElementById('terms-box').scrollIntoView({ behavior: 'smooth', block: 'nearest' });
document.getElementById('iv-status').className = 'ok-line';
document.getElementById('iv-status').textContent = '✓ Server verified: ' + serverUrl + '. Pay the invoice — it will be listed after 3 confirmations.';
const inv = document.getElementById('invoice');
inv.classList.add('show');
inv.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
} catch (err) {
document.getElementById('iv-status').className = 'err-line';
document.getElementById('iv-status').textContent = 'Network error. Please try again.';
}
});
document.getElementById('terms-check').addEventListener('change', function () {
document.getElementById('terms-accept').disabled = !this.checked;
});
document.getElementById('terms-accept').addEventListener('click', function () {
document.getElementById('terms-box').classList.remove('show');
const inv = document.getElementById('invoice');
document.getElementById('iv-status').className = 'ok-line';
document.getElementById('iv-status').textContent = '✓ Server added: ' + document.getElementById('iv-url').textContent + '. Pay the invoice to complete.';
inv.classList.add('show');
inv.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
});
document.getElementById('url').addEventListener('keydown', e => {
if (e.key === 'Enter') document.getElementById('submit').click();
});
(function init() {
(async function init() {
await Promise.all([fetchData(), fetchMinAmount()]);
const blob = new Blob([JSON.stringify(allServers, null, 2)], { type: 'application/json' });
document.getElementById('download').href = URL.createObjectURL(blob);
fetchData();
fetchMinAmount();
})();
</script>
<div id="qr-overlay"><div id="qr-big"></div></div>

View File

@@ -7,9 +7,12 @@
<link rel="icon" href="Logo_nero.ico" type="image/x-icon">
<!--
Sections 2-4 rewritten on 2026-07 for the yearly-listing model
(flat yearly fee, renewal from 6 months before expiry, list
ordered by points). Section 1, header, footer and the dynamic
min-deposit script are unchanged from the previous version.
(flat yearly fee, renewal from 6 months before expiry). The list
is ordered by total excess donated, then total amount paid, then
base fee, then WeTime points as a tie-breaker (see Section 3),
matching the server-side get_list() ordering. Section 1, header,
footer and the dynamic min-deposit script are unchanged from the
previous version.
-->
<style>
:root{
@@ -235,7 +238,7 @@ l-49 -25 -880 0 -880 0 -3 704 c-2 557 0 706 10 713 7 4 407 7 888 5 818 -2
<section class="info-section">
<h2>2. Listing &amp; Renewal</h2>
<p class="sub">A listing works like a hosting plan: a flat yearly fee buys your server one year of presence in the list. No balances, no ranking by deposit, no daily decay. Payments are non-refundable (see Section&nbsp;1) — double-check your server URL before paying.</p>
<p class="sub">A listing works like a hosting plan: a flat yearly fee buys your server one year of presence in the list. Your paid amount accumulates and never decays — it is preserved across renewals and also determines your position in the list (see Section&nbsp;3). Payments are non-refundable (see Section&nbsp;1) — double-check your server URL before paying.</p>
<div class="info-grid">
<div class="info-block">
<span class="ic">01 · PAYMENT</span>
@@ -252,7 +255,7 @@ l-49 -25 -880 0 -880 0 -3 704 c-2 557 0 706 10 713 7 4 407 7 888 5 818 -2
<div class="info-block">
<span class="ic">03 · CAPACITY</span>
<h3>No Cap on Listings</h3>
<p>There is no fixed number of slots: every server with remaining listing time is shown. Paying buys presence in the list, not a position in it (see Section&nbsp;3).</p>
<p>There is no fixed number of slots: every server with remaining listing time is shown. Paying buys presence in the list; the total amount paid also affects your position in it (see Section&nbsp;3).</p>
<p>Removal from the list does not affect users who already have your server in their plugin: the plugin's server list is add-only. Being listed matters for reaching new users.</p>
</div>
<div class="info-block">
@@ -283,13 +286,19 @@ l-49 -25 -880 0 -880 0 -3 704 c-2 557 0 706 10 713 7 4 407 7 888 5 818 -2
<section class="info-section">
<h2>3. List Order &amp; Server Page</h2>
<p class="sub">The list is ordered by points, earned by proving your server is online and synchronized. Payment does not affect position.</p>
<p class="sub">The list is ordered by four criteria: first by total excess donated (higher first), then by total amount paid in balance (higher first), then by the base fee charged (lower first), and finally by WeTime points (higher first) as a tie-breaker.</p>
<div class="info-grid">
<div class="info-block">
<span class="ic">ORDER</span>
<h3>Points &amp; WeTime</h3>
<h3>How Position Is Determined</h3>
<p>Servers are sorted by four criteria, in order: first by the total excess donated (higher first), then by the total paid amount (higher first), then by the base fee they charge (lower first), and finally by WeTime points (higher first) to break any remaining ties.</p>
<p>Because excess donated comes first and never decays, over-paying or tipping more — on top of any payment — directly and permanently moves a server up the list. Paying more than the exact yearly fee is the fastest way to improve position.</p>
</div>
<div class="info-block">
<span class="ic">POINTS</span>
<h3>WeTime Points</h3>
<p>Points are earned by participating in WeTime rounds. Each executor periodically submits its block hash; the faster the response within the same block, the more points awarded. The win count tracks how many times an executor was the fastest responder.</p>
<p>Points are not transferable, not redeemable and have no monetary value: they exist only to order this list. Points measure presence and reactivity — they do not certify how a server will handle inheritance transactions.</p>
<p>Points are not transferable, not redeemable and have no monetary value: within this list they act only as a tie-breaker. Points measure presence and reactivity — they do not certify how a server will handle inheritance transactions.</p>
</div>
<div class="info-block">
<span class="ic">DETAILS</span>

View File

@@ -41,6 +41,7 @@
<div><span>Version:</span><span id="weversion">&nbsp;</span></div>
<div><span>Fees:</span><span id="wefees">&nbsp;</span></div>
<div><span>Tld:</span><span id="wetld">&nbsp;</span></div>
<div><span>Last Update:</span><span id="weupdate">&nbsp;</span></div>
</div>
<div style="float:left;border:1;margin-right:50px;">
<div><span>Height:</span><span id="weheight">&nbsp;</span></div>
@@ -49,8 +50,9 @@
</div>
<div style="border:1;margin-right:50px;">
<div><span>Balance:</span><span id="webalance">&nbsp;</span></div>
<div><span>Donations:</span><span id="wedonations">&nbsp;</span></div>
<div><span>Status:</span><span id="westatus">&nbsp;</span></div>
<div><span>Last Update:</span><span id="weupdate">&nbsp;</span></div>
<div><span>Expiry:</span><span id="weexpiry">&nbsp;</span></div>
</div>
<div>
<table id="wetimes">
@@ -89,8 +91,13 @@
document.getElementById('wepoints').innerHTML = data.points != null ? data.points : '—';
document.getElementById('wewin').innerHTML = data.count_win != null ? data.count_win : '—';
document.getElementById('wetld').innerHTML = DOMPurify.sanitize(data.tld||'—');
document.getElementById('webalance').innerHTML = data.balance != null ? data.balance : '—';
const bal = data.balance != null ? data.balance : null;
const unc = data.unconfirmed_balance != null ? data.unconfirmed_balance : null;
document.getElementById('webalance').innerHTML = bal ? (unc ? bal + ' (' + unc + ')' : bal) : '—';
document.getElementById('wedonations').innerHTML = data.donation != null ? data.donation : '—';
document.getElementById('weversion').innerHTML = data.version||'—';
document.getElementById('weexpiry').innerHTML = '<b>' + (data.expiry_date || '—') + '</b>';
document.getElementById('weupdate').innerHTML = data.last_update || '—';
const statusMap = {
'OK': 'Health check passed',
'TIMEOUT': 'Connection timeout',