mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-07 17:18:13 -07:00
130 lines
3.2 KiB
JavaScript
130 lines
3.2 KiB
JavaScript
import { createBtcAmount } from "../../amount/index.js";
|
|
import { createElement } from "../../dom.js";
|
|
import { redaction } from "../../redaction/index.js";
|
|
import { createAddressCellContent } from "../address/index.js";
|
|
import { utxoCache } from "./cache.js";
|
|
import { readWalletUtxo } from "./utxo.js";
|
|
|
|
/**
|
|
* @typedef {import("../../scan/index.js").WalletAddress} WalletAddress
|
|
* @typedef {import("../../scan/index.js").WalletScan} WalletScan
|
|
* @typedef {import("./utxo.js").WalletUtxo} WalletUtxo
|
|
* @typedef {Parameters<typeof utxoCache.load>[0]} UtxoClient
|
|
*/
|
|
|
|
/**
|
|
* @param {string} txid
|
|
*/
|
|
function formatTxid(txid) {
|
|
return txid.length > 16 ? `${txid.slice(0, 8)}...${txid.slice(-8)}` : txid;
|
|
}
|
|
|
|
/**
|
|
* @param {WalletUtxo} utxo
|
|
*/
|
|
function createUtxoRow(utxo) {
|
|
const row = document.createElement("li");
|
|
const main = document.createElement("header");
|
|
const label = document.createElement("strong");
|
|
const amount = createBtcAmount("span", utxo.value);
|
|
const detail = document.createElement("p");
|
|
const outpoint = document.createElement("code");
|
|
|
|
redaction.setTitle(outpoint, `${utxo.txid}:${utxo.vout}`);
|
|
redaction.setValue(outpoint, `${formatTxid(utxo.txid)}:${utxo.vout}`);
|
|
label.append(utxo.confirmed ? "Confirmed" : "Mempool");
|
|
detail.append(outpoint);
|
|
row.append(main, detail, createAddressCellContent(utxo.address));
|
|
main.append(label, amount);
|
|
|
|
return row;
|
|
}
|
|
|
|
/**
|
|
* @param {WalletUtxo} a
|
|
* @param {WalletUtxo} b
|
|
*/
|
|
function compareUtxos(a, b) {
|
|
return b.value - a.value || a.txid.localeCompare(b.txid) || a.vout - b.vout;
|
|
}
|
|
|
|
/**
|
|
* @param {UtxoClient} client
|
|
* @param {readonly WalletAddress[]} addresses
|
|
*/
|
|
async function loadWalletUtxos(client, addresses) {
|
|
const utxos = /** @type {WalletUtxo[]} */ ([]);
|
|
|
|
for (const address of addresses) {
|
|
const loaded = await utxoCache.load(client, address);
|
|
|
|
for (const utxo of loaded.utxos) {
|
|
const walletUtxo = readWalletUtxo(address, utxo);
|
|
|
|
if (walletUtxo) {
|
|
utxos.push(walletUtxo);
|
|
}
|
|
}
|
|
}
|
|
|
|
return utxos.sort(compareUtxos);
|
|
}
|
|
|
|
/**
|
|
* @param {HTMLElement} panel
|
|
* @param {readonly WalletUtxo[]} utxos
|
|
*/
|
|
function renderHoldings(panel, utxos) {
|
|
const section = createElement("section", "holdings");
|
|
const title = document.createElement("h2");
|
|
const list = document.createElement("ol");
|
|
|
|
title.append("Holdings");
|
|
section.append(title);
|
|
|
|
if (utxos.length === 0) {
|
|
const empty = document.createElement("p");
|
|
|
|
empty.append("No UTXOs");
|
|
section.append(empty);
|
|
panel.replaceChildren(section);
|
|
return;
|
|
}
|
|
|
|
for (const utxo of utxos) {
|
|
list.append(createUtxoRow(utxo));
|
|
}
|
|
|
|
section.append(list);
|
|
panel.replaceChildren(section);
|
|
}
|
|
|
|
/**
|
|
* @param {WalletScan} scan
|
|
* @param {UtxoClient} client
|
|
*/
|
|
export function createHoldingsTab(scan, client) {
|
|
const panel = document.createElement("section");
|
|
let loaded = false;
|
|
|
|
return {
|
|
id: "holdings",
|
|
label: "Holdings",
|
|
panel,
|
|
mount() {
|
|
if (loaded) return;
|
|
|
|
loaded = true;
|
|
panel.replaceChildren("Loading holdings");
|
|
void loadWalletUtxos(client, scan.addresses).then(
|
|
(utxos) => {
|
|
renderHoldings(panel, utxos);
|
|
},
|
|
() => {
|
|
panel.replaceChildren("Holdings unavailable");
|
|
},
|
|
);
|
|
},
|
|
};
|
|
}
|