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

274 lines
7.8 KiB
JavaScript

const BURN_FEE_ATOMIC = 10000n;
let burnBalancesRefreshPromise = null;
function setBurnMessage(message, type = "") {
setScopedTransactionMessage("Burn", burnMessage, message, type);
}
function burnableBalances() {
return (Array.isArray(activeWalletBalances) ? activeWalletBalances : [])
.filter((balance) => {
const asset = String(balance.asset || "").trim();
return asset
&& !isBaseTransferAsset(asset)
&& balanceAtomicValue(balance) > 0n;
});
}
function burnAssetOptionLabel(balance) {
const amount = formatAtomicBalance(balance.balance || "0");
const series = Number(balance.nft_series || 0);
if (series > 0) {
return `${balance.asset} #${series} (${amount})`;
}
return `${balance.asset} (${amount})`;
}
function selectedBurnAsset() {
const option = burnAssetSelect?.selectedOptions?.[0];
if (!option?.value) {
return null;
}
return {
asset: option.dataset.asset || "",
nftSeries: Number(option.dataset.nftSeries || 0),
balance: BigInt(option.dataset.balance || "0")
};
}
function populateBurnAssets() {
if (!burnAssetSelect) {
return;
}
const previous = burnAssetSelect.value;
burnAssetSelect.innerHTML = "";
const balances = burnableBalances();
if (balances.length) {
const placeholder = document.createElement("option");
placeholder.value = "";
placeholder.textContent = "Select an asset";
burnAssetSelect.appendChild(placeholder);
}
balances.forEach((balance) => {
const option = document.createElement("option");
option.value = transferAssetKey(balance);
option.dataset.asset = balance.asset || "";
option.dataset.nftSeries = String(balance.nft_series || 0);
option.dataset.balance = String(balance.balance || "0");
option.textContent = burnAssetOptionLabel(balance);
burnAssetSelect.appendChild(option);
});
if (!balances.length) {
const option = document.createElement("option");
option.value = "";
option.textContent = "No burnable assets";
burnAssetSelect.appendChild(option);
} else if ([...burnAssetSelect.options].some((option) => option.value === previous)) {
burnAssetSelect.value = previous;
}
updateBurnDerivedFields();
}
async function ensureBurnBalances() {
if (activeWalletBalances.length || !walletLoaded || !registrationRegistered) {
return;
}
const invoke = window.__TAURI__?.core?.invoke;
const broadcastNode = activeBroadcastNode();
if (!invoke || !broadcastNode) {
return;
}
burnBalancesRefreshPromise ||= invoke("get_active_wallet_balances", { broadcastNode })
.then((balances) => {
activeWalletBalances = Array.isArray(balances) ? balances : [];
})
.finally(() => {
burnBalancesRefreshPromise = null;
});
await burnBalancesRefreshPromise;
}
function updateBurnDerivedFields() {
const selected = selectedBurnAsset();
if (burnFeeInput) {
burnFeeInput.value = atomicToDecimal(BURN_FEE_ATOMIC);
}
if (burnFeeLabel) {
burnFeeLabel.textContent = `${atomicToDecimal(BURN_FEE_ATOMIC)} ${activeBaseCoin()}`;
}
if (burnAddress) {
burnAddress.textContent = activeWalletAddressShort || activeWalletAddressHex || "--";
}
if (!selected) {
if (burnAvailable) {
burnAvailable.textContent = "--";
}
if (burnType) {
burnType.textContent = "--";
}
if (burnAmountInput) {
burnAmountInput.disabled = false;
burnAmountInput.value = "";
}
return;
}
const isNft = selected.nftSeries > 0;
if (burnAvailable) {
burnAvailable.textContent = `${atomicToDecimal(selected.balance)} ${selected.asset}`;
}
if (burnType) {
burnType.textContent = isNft ? `NFT #${selected.nftSeries}` : "Token";
}
if (burnAmountInput) {
burnAmountInput.disabled = isNft;
if (isNft) {
burnAmountInput.value = "1.00000000";
}
}
}
function validateBurnForm() {
if (!walletLoaded) {
return "Load a wallet before burning an asset.";
}
if (!registrationRegistered) {
return "Address registration is required before burning an asset.";
}
const selected = selectedBurnAsset();
if (!selected) {
return "Select an asset to burn.";
}
const amount = decimalToAtomic(burnAmountInput?.value);
if (amount === null || amount <= 0n) {
return "Enter a valid burn amount.";
}
if (selected.nftSeries > 0 && amount !== ATOMIC_UNITS_PER_COIN) {
return "NFT burns must destroy exactly 1.00000000 units.";
}
if (amount > selected.balance) {
return "Burn amount cannot exceed the selected asset balance.";
}
if (baseCoinBalanceAtomic() < BURN_FEE_ATOMIC) {
return `Burning requires ${atomicToDecimal(BURN_FEE_ATOMIC)} ${activeBaseCoin()} for the transaction fee.`;
}
return "";
}
function burnPayload() {
const selected = selectedBurnAsset();
return {
coin: selected.asset,
nftSeries: selected.nftSeries,
value: String(decimalToAtomic(burnAmountInput.value)),
txfee: String(BURN_FEE_ATOMIC)
};
}
function clearBurnForm() {
if (burnAssetSelect) {
burnAssetSelect.value = "";
}
if (burnAmountInput) {
burnAmountInput.value = "";
burnAmountInput.disabled = false;
}
updateBurnDerivedFields();
}
function setBurnButtonsDisabled(disabled) {
document.querySelector("[data-burn-save]")?.toggleAttribute("disabled", disabled);
document.querySelector("[data-burn-broadcast]")?.toggleAttribute("disabled", disabled);
}
async function handleBurnAction(action) {
try {
await ensureBurnBalances();
populateBurnAssets();
} catch (error) {
setBurnMessage(String(error), "error");
return;
}
const validationError = validateBurnForm();
if (validationError) {
setBurnMessage(validationError, "error");
return;
}
const invoke = window.__TAURI__?.core?.invoke;
if (!invoke) {
setBurnMessage("Wallet bridge is not available.", "error");
return;
}
const walletKey = await requestSigningWalletKey();
if (guiRuntimeSettings.require_key_before_signing !== false && !walletKey) {
setBurnMessage("Signing cancelled.");
return;
}
setBurnButtonsDisabled(true);
try {
if (action === "save") {
setBurnMessage("Saving burn transaction...");
const review = await invoke("save_burn_transaction", {
...burnPayload(),
walletKey
});
clearBurnForm();
setBurnMessage(`Burn transaction saved: ${review.file_name}`, "success");
if (typeof refreshUnbroadcastTransactions === "function") {
refreshUnbroadcastTransactions();
}
} else {
const broadcastNode = activeBroadcastNode();
if (!broadcastNode) {
throw new Error("Select a broadcast node before broadcasting.");
}
setBurnMessage("Broadcasting burn transaction...");
const result = await invoke("broadcast_burn_transaction", {
...burnPayload(),
broadcastNode,
walletKey
});
if (typeof invalidateAssetsCatalog === "function") {
invalidateAssetsCatalog();
}
activeWalletBalances = [];
clearBurnForm();
setBurnMessage(result.response || "Burn transaction broadcast.", "success");
}
} catch (error) {
setBurnMessage(String(error), "error");
} finally {
setBurnButtonsDisabled(false);
}
}
async function prepareBurnForm() {
setBurnMessage("");
try {
await ensureBurnBalances();
} catch (error) {
setBurnMessage(String(error), "error");
}
populateBurnAssets();
}
function resetBurnSession() {
burnBalancesRefreshPromise = null;
clearBurnForm();
setBurnMessage("");
}
burnAssetSelect?.addEventListener("change", () => {
setBurnMessage("");
updateBurnDerivedFields();
});
burnAmountInput?.addEventListener("input", () => setBurnMessage(""));
document
.querySelector("[data-burn-save]")
?.addEventListener("click", () => handleBurnAction("save"));
document
.querySelector("[data-burn-broadcast]")
?.addEventListener("click", () => handleBurnAction("broadcast"));