let assetsCatalog = []; let assetsLoadedForNode = ""; let assetsLoadInFlight = false; let assetsRefreshId = 0; let activeAssetFilter = "all"; let assetsEmptyStateMessage = ""; function invalidateAssetsCatalog() { assetsLoadedForNode = ""; } function assetCatalogBalance(asset) { const ticker = String(asset || "").trim().toLowerCase(); const match = activeWalletBalances.find((balance) => { return ( String(balance.asset || "").trim().toLowerCase() === ticker && Number(balance.nft_series || 0) === 0 ); }); return match ? String(match.balance || "0") : "0"; } function formatAssetSupply(value) { return formatAtomicBalance(value || 0); } function assetCreatorLabel(asset, isCreator) { const creator = String(asset.creator || "").trim(); if (isCreator) { return "You"; } return creator ? shortenAddress(creator) : "Unknown"; } function formatAssetDate(timestamp) { const numeric = Number(timestamp || 0); if (!numeric) { return "--"; } return new Date(numeric * 1000).toLocaleDateString(); } function escapeAssetText(value) { return String(value ?? "") .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """) .replaceAll("'", "'"); } function compareAssetBalance(left, right) { const leftBalance = BigInt(assetCatalogBalance(left.ticker)); const rightBalance = BigInt(assetCatalogBalance(right.ticker)); if (rightBalance !== leftBalance) { return rightBalance > leftBalance ? 1 : -1; } return String(left.ticker || "").localeCompare(String(right.ticker || "")); } function sortedAssetCards() { const query = String(assetsSearch?.value || "").trim().toLowerCase(); const sortMode = assetsSort?.value || "default"; const activeShortAddress = String(activeWalletAddressShort || "").toLowerCase(); const sourceRows = assetsCatalog; let rows = sourceRows.filter((asset) => { const ticker = String(asset.ticker || "").trim(); if (!ticker) { return false; } const balance = BigInt(assetCatalogBalance(ticker)); const creator = String(asset.creator || "").toLowerCase(); const searchText = `${ticker} ${asset.origin_txid || ""} ${asset.creator || ""}`.toLowerCase(); if (activeAssetFilter === "holding" && balance <= 0n) { return false; } if (activeAssetFilter === "created" && creator !== activeShortAddress) { return false; } return !query || searchText.includes(query); }); if (sortMode === "balance" || sortMode === "default") { rows.sort(compareAssetBalance); if (sortMode === "default") { rows.sort((left, right) => { const leftHolding = BigInt(assetCatalogBalance(left.ticker)) > 0n ? 0 : 1; const rightHolding = BigInt(assetCatalogBalance(right.ticker)) > 0n ? 0 : 1; return leftHolding - rightHolding || compareAssetBalance(left, right); }); } } else if (sortMode === "created") { rows.sort((left, right) => Number(right.created_timestamp || 0) - Number(left.created_timestamp || 0)); } else { rows.sort((left, right) => String(left.ticker || "").localeCompare(String(right.ticker || ""))); } return rows; } function renderAssetCard(asset) { const ticker = String(asset.ticker || "").trim(); const balance = assetCatalogBalance(ticker); const isHolding = BigInt(balance || "0") > 0n; const isCreator = String(asset.creator || "").toLowerCase() === String(activeWalletAddressShort || "").toLowerCase(); const policy = asset.hard_limit ? "Fixed Supply" : "Issuable"; const escapedTicker = escapeAssetText(ticker); const escapedPolicy = escapeAssetText(policy); const creatorLabel = assetCreatorLabel(asset, isCreator); return `
${escapeAssetText(assetInitial(ticker))}
${escapedTicker} ${escapedPolicy}
${escapeAssetText(formatAtomicBalance(balance))} ${escapedTicker}
${isHolding ? 'Holding' : ""} ${isCreator ? 'Created by me' : ""} ${escapedPolicy}
Current Supply${escapeAssetText(formatAssetSupply(asset.current_supply))}
Total Burned${escapeAssetText(formatAssetSupply(asset.total_burned))}
Holders${escapeAssetText(formatNumber(asset.holder_count))}
Created${escapeAssetText(formatAssetDate(asset.created_timestamp))}
Initial Supply${escapeAssetText(formatAssetSupply(asset.initial_supply))}
Creator${escapeAssetText(creatorLabel)}
${escapeAssetText(asset.origin_txid || "")}
`; } function setAssetsState(message) { if (assetsState) { assetsState.textContent = message || ""; } } function renderAssetsPage() { if (!assetsCards) { return; } const rows = sortedAssetCards(); assetsCards.innerHTML = rows.map(renderAssetCard).join(""); if (!walletLoaded) { setAssetsState("Load a wallet to view assets."); } else if (assetsLoadInFlight && !assetsCatalog.length) { setAssetsState("Loading assets..."); } else if (!assetsCatalog.length) { setAssetsState(assetsEmptyStateMessage || "Token catalog returned no rows."); } else if (!rows.length) { setAssetsState("No assets match the current filter."); } else { setAssetsState(`${rows.length} token${rows.length === 1 ? "" : "s"} shown`); } } async function loadAssetsCatalog({ force = false } = {}) { const invoke = window.__TAURI__?.core?.invoke; const initialBroadcastNode = activeBroadcastNode(); if (!walletLoaded) { assetsCatalog = []; assetsEmptyStateMessage = "Asset catalog skipped: no wallet is loaded."; renderAssetsPage(); return; } if (!invoke) { assetsCatalog = []; assetsEmptyStateMessage = "Asset catalog skipped: Tauri invoke is unavailable."; renderAssetsPage(); return; } if (!initialBroadcastNode) { assetsCatalog = []; assetsEmptyStateMessage = "Asset catalog skipped: no broadcast node is selected."; renderAssetsPage(); return; } const broadcastNode = initialBroadcastNode; if (!force && assetsCatalog.length && assetsLoadedForNode === broadcastNode) { renderAssetsPage(); return; } if (assetsLoadInFlight) { return; } const refreshId = ++assetsRefreshId; assetsLoadInFlight = true; assetsEmptyStateMessage = "Calling token catalog RPC command 48..."; renderAssetsPage(); try { const catalog = await invokeWalletRpcWithBackoff( "asset catalog", () => invoke("token_catalog", { broadcastNode }), () => refreshId === assetsRefreshId && activeBroadcastNode() === broadcastNode ); if (refreshId !== assetsRefreshId || activeBroadcastNode() !== broadcastNode) { return; } assetsCatalog = Array.isArray(catalog) ? catalog : []; assetsEmptyStateMessage = assetsCatalog.length ? "" : "Token catalog command 48 returned 0 rows from the selected node."; assetsLoadedForNode = broadcastNode; } catch (error) { if (refreshId === assetsRefreshId) { assetsCatalog = []; assetsEmptyStateMessage = `Token catalog command 48 failed: ${String(error)}`; setAssetsState(String(error)); } } finally { if (refreshId === assetsRefreshId) { assetsLoadInFlight = false; renderAssetsPage(); } } } function prepareAssetsPage() { renderAssetsPage(); loadAssetsCatalog({ force: true }); } function resetAssetsPage() { assetsRefreshId += 1; assetsCatalog = []; assetsEmptyStateMessage = ""; assetsLoadedForNode = ""; assetsLoadInFlight = false; renderAssetsPage(); } assetsSearch?.addEventListener("input", renderAssetsPage); assetsSort?.addEventListener("change", renderAssetsPage); assetsFilterButtons.forEach((button) => { button.addEventListener("click", () => { activeAssetFilter = button.dataset.assetsFilter || "all"; assetsFilterButtons.forEach((item) => { item.classList.toggle("active", item === button); }); renderAssetsPage(); }); });