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

293 lines
8.6 KiB
JavaScript
Raw Normal View History

2026-07-01 16:01:30 +00:00
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),
2026-07-25 23:52:48 +00:00
ownershipType: Number(option.dataset.ownershipType || -1),
isNft: option.dataset.isNft === "true",
2026-07-01 16:01:30 +00:00
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");
2026-07-25 23:52:48 +00:00
const metadata = nftOwnershipMetadata(balance.asset, balance.nft_series);
option.dataset.isNft = String(Boolean(metadata));
option.dataset.ownershipType = String(metadata?.ownershipType ?? -1);
2026-07-01 16:01:30 +00:00
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() {
2026-07-25 23:52:48 +00:00
if (!walletLoaded || !registrationRegistered) {
2026-07-01 16:01:30 +00:00
return;
}
const invoke = window.__TAURI__?.core?.invoke;
const broadcastNode = activeBroadcastNode();
if (!invoke || !broadcastNode) {
return;
}
2026-07-25 23:52:48 +00:00
burnBalancesRefreshPromise ||= Promise.all([
activeWalletBalances.length
? Promise.resolve(activeWalletBalances)
: invoke("get_active_wallet_balances", { broadcastNode }),
ensureNftOwnershipRegistry()
])
.then(([balances]) => {
2026-07-01 16:01:30 +00:00
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;
}
2026-07-25 23:52:48 +00:00
const isNft = selected.isNft;
const isIndivisible = isNft && selected.ownershipType === 0;
2026-07-01 16:01:30 +00:00
if (burnAvailable) {
burnAvailable.textContent = `${atomicToDecimal(selected.balance)} ${selected.asset}`;
}
if (burnType) {
2026-07-25 23:52:48 +00:00
burnType.textContent = isNft
? (selected.nftSeries > 0 ? `NFT #${selected.nftSeries}` : "NFT / RWA")
: "Token";
2026-07-01 16:01:30 +00:00
}
if (burnAmountInput) {
2026-07-25 23:52:48 +00:00
burnAmountInput.disabled = isIndivisible;
if (isIndivisible) {
2026-07-01 16:01:30 +00:00
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.";
}
2026-07-25 23:52:48 +00:00
if (selected.isNft && selected.ownershipType === 0 && amount !== ATOMIC_UNITS_PER_COIN) {
2026-07-01 16:01:30 +00:00
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") {
2026-07-25 23:52:48 +00:00
setBurnMessage("Signing and saving burn transaction...");
2026-07-01 16:01:30 +00:00
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.");
}
2026-07-25 23:52:48 +00:00
setBurnMessage("Signing and broadcasting burn transaction...");
2026-07-01 16:01:30 +00:00
const result = await invoke("broadcast_burn_transaction", {
...burnPayload(),
broadcastNode,
walletKey
});
if (typeof invalidateAssetsCatalog === "function") {
invalidateAssetsCatalog();
}
2026-07-25 23:52:48 +00:00
if (typeof invalidateDashboardNftBalanceKeys === "function") {
invalidateDashboardNftBalanceKeys();
}
if (typeof resetNftsPage === "function") {
resetNftsPage();
}
2026-07-01 16:01:30 +00:00
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"));