Contractless-GUI-Wallet/frontend/js/issue_token_form.js

282 lines
8.7 KiB
JavaScript
Raw Normal View History

2026-07-01 16:01:30 +00:00
const ISSUE_TOKEN_FEE_ATOMIC = 10000000000n;
let issueTokenEligibility = null;
let issueTokenLookupId = 0;
function setIssueTokenMessage(message, type = "") {
setScopedTransactionMessage("Issue More Tokens", issueTokenMessage, message, type);
}
function setIssueTokenButtonsDisabled(disabled) {
document.querySelector("[data-issue-token-save]")?.toggleAttribute("disabled", disabled);
document.querySelector("[data-issue-token-broadcast]")?.toggleAttribute("disabled", disabled);
}
function updateIssueTokenFee() {
const displayFee = tokenAtomicToDecimal(ISSUE_TOKEN_FEE_ATOMIC);
if (issueTokenFeeInput) {
issueTokenFeeInput.value = displayFee;
}
if (issueTokenFeeLabel) {
issueTokenFeeLabel.textContent = `${displayFee} ${activeBaseCoin()}`;
}
}
function resetIssueTokenDetails() {
issueTokenEligibility = null;
if (issueTokenCreator) {
issueTokenCreator.textContent = "--";
}
if (issueTokenPolicy) {
issueTokenPolicy.textContent = "--";
}
setIssueTokenButtonsDisabled(true);
}
async function refreshIssueTokenEligibility() {
const invoke = window.__TAURI__?.core?.invoke;
const broadcastNode = activeBroadcastNode();
const walletAddress = activeWalletAddressShort || activeWalletAddressHex || "";
const ticker = String(issueTokenTickerSelect?.value || "").trim();
const lookupId = ++issueTokenLookupId;
resetIssueTokenDetails();
if (!ticker || !invoke || !broadcastNode || !walletLoaded) {
return;
}
setIssueTokenMessage("Checking token issuance policy...");
try {
const details = await invokeWalletRpcWithBackoff(
"Token details lookup",
() => invoke("issue_token_details", { broadcastNode, ticker }),
() =>
lookupId === issueTokenLookupId &&
walletLoaded &&
(activeWalletAddressShort || activeWalletAddressHex || "") === walletAddress &&
activeBroadcastNode() === broadcastNode &&
issueTokenTickerSelect?.value === ticker
);
if (lookupId !== issueTokenLookupId) {
return;
}
issueTokenEligibility = details;
if (issueTokenCreator) {
issueTokenCreator.textContent = details.creator || "--";
}
if (issueTokenPolicy) {
issueTokenPolicy.textContent = details.hard_limit
? "Hard capped"
: "Future issuance allowed";
}
if (!details.eligible) {
setIssueTokenMessage(
details.reason || "This token cannot be issued by the active wallet.",
"error"
);
return;
}
setIssueTokenMessage("The active wallet may issue more of this token.", "success");
setIssueTokenButtonsDisabled(false);
} catch (error) {
if (lookupId !== issueTokenLookupId) {
return;
}
setIssueTokenMessage(String(error), "error");
}
}
async function refreshIssueTokenList() {
issueTokenLookupId += 1;
const invoke = window.__TAURI__?.core?.invoke;
const broadcastNode = activeBroadcastNode();
const walletAddress = activeWalletAddressShort || activeWalletAddressHex || "";
resetIssueTokenDetails();
if (!issueTokenTickerSelect) {
return;
}
issueTokenTickerSelect.innerHTML = "";
const loadingOption = document.createElement("option");
loadingOption.value = "";
loadingOption.textContent = "Loading tokens...";
issueTokenTickerSelect.appendChild(loadingOption);
if (!invoke || !walletLoaded || !broadcastNode) {
loadingOption.textContent = "Load a wallet and select a node";
return;
}
try {
const tokens = await invokeWalletRpcWithBackoff(
"Token list lookup",
() => invoke("token_list", { broadcastNode }),
() =>
walletLoaded &&
(activeWalletAddressShort || activeWalletAddressHex || "") === walletAddress &&
activeBroadcastNode() === broadcastNode
);
if (
!walletLoaded ||
(activeWalletAddressShort || activeWalletAddressHex || "") !== walletAddress ||
activeBroadcastNode() !== broadcastNode
) {
return;
}
issueTokenTickerSelect.innerHTML = "";
const tokenItems = Array.isArray(tokens) ? tokens : [];
if (!tokenItems.length) {
const option = document.createElement("option");
option.value = "";
option.textContent = "No tokens exist";
issueTokenTickerSelect.appendChild(option);
setIssueTokenMessage("No tokens are available to issue.", "error");
return;
}
tokenItems.forEach((token) => {
const ticker = String(token?.ticker || "").trim();
if (!ticker) {
return;
}
const option = document.createElement("option");
option.value = ticker;
option.textContent = ticker;
issueTokenTickerSelect.appendChild(option);
});
await refreshIssueTokenEligibility();
} catch (error) {
issueTokenTickerSelect.innerHTML = "";
const option = document.createElement("option");
option.value = "";
option.textContent = "Token list unavailable";
issueTokenTickerSelect.appendChild(option);
setIssueTokenMessage(String(error), "error");
}
}
function validateIssueTokenForm() {
if (!walletLoaded) {
return "Load a wallet before issuing tokens.";
}
if (!registrationRegistered) {
return "Address registration is required before issuing tokens.";
}
if (!issueTokenEligibility?.eligible) {
return issueTokenEligibility?.reason || "Select a token the active wallet may issue.";
}
const number = tokenDecimalToAtomic(issueTokenNumberInput?.value);
if (number === null || number <= 0n) {
return "Enter a valid additional supply with up to 8 decimal places.";
}
const baseCoin = activeBaseCoin();
const baseBalance = (Array.isArray(activeWalletBalances) ? activeWalletBalances : []).find(
(balance) =>
String(balance.asset || "").trim().toUpperCase() === baseCoin &&
!Number(balance.nft_series || 0)
);
if (baseBalance && balanceAtomicValue(baseBalance) < ISSUE_TOKEN_FEE_ATOMIC) {
return `This transaction requires ${tokenAtomicToDecimal(ISSUE_TOKEN_FEE_ATOMIC)} ${baseCoin}.`;
}
return "";
}
function issueTokenPayload() {
return {
broadcastNode: activeBroadcastNode(),
ticker: String(issueTokenTickerSelect?.value || "").trim(),
number: String(tokenDecimalToAtomic(issueTokenNumberInput?.value) || 0n),
txfee: String(ISSUE_TOKEN_FEE_ATOMIC)
};
}
function clearIssueTokenForm() {
if (issueTokenNumberInput) {
issueTokenNumberInput.value = "";
}
}
async function handleIssueTokenAction(action) {
const error = validateIssueTokenForm();
if (error) {
setIssueTokenMessage(error, "error");
return;
}
const invoke = window.__TAURI__?.core?.invoke;
if (!invoke) {
setIssueTokenMessage("Wallet bridge is not available.", "error");
return;
}
const walletKey = await requestSigningWalletKey();
if (guiRuntimeSettings.require_key_before_signing !== false && !walletKey) {
setIssueTokenMessage("Signing cancelled.");
return;
}
setIssueTokenButtonsDisabled(true);
try {
if (action === "save") {
setIssueTokenMessage("Saving issue-token transaction...");
const review = await invoke("save_issue_token_transaction", {
...issueTokenPayload(),
walletKey
});
clearIssueTokenForm();
setIssueTokenMessage(`Issue-token transaction saved: ${review.file_name}`, "success");
if (typeof refreshUnbroadcastTransactions === "function") {
refreshUnbroadcastTransactions();
}
} else {
setIssueTokenMessage("Broadcasting issue-token transaction...");
const result = await invoke("broadcast_issue_token_transaction", {
...issueTokenPayload(),
walletKey
});
if (typeof invalidateAssetsCatalog === "function") {
invalidateAssetsCatalog();
}
clearIssueTokenForm();
setIssueTokenMessage(result.response || "Issue-token transaction broadcast.", "success");
}
} catch (actionError) {
setIssueTokenMessage(String(actionError), "error");
} finally {
setIssueTokenButtonsDisabled(!issueTokenEligibility?.eligible);
}
}
function prepareIssueTokenForm() {
updateIssueTokenFee();
setIssueTokenMessage("");
refreshIssueTokenList();
}
function resetIssueTokenSession() {
issueTokenLookupId += 1;
resetIssueTokenDetails();
if (issueTokenTickerSelect) {
issueTokenTickerSelect.innerHTML = '<option value="">Load a wallet first</option>';
}
clearIssueTokenForm();
setIssueTokenMessage("");
}
issueTokenTickerSelect?.addEventListener("change", refreshIssueTokenEligibility);
issueTokenNumberInput?.addEventListener("input", () => {
if (issueTokenEligibility?.eligible) {
setIssueTokenMessage("");
}
});
document
.querySelector("[data-issue-token-save]")
?.addEventListener("click", () => handleIssueTokenAction("save"));
document
.querySelector("[data-issue-token-broadcast]")
?.addEventListener("click", () => handleIssueTokenAction("broadcast"));