const COLLATERAL_CLAIM_FEE_ATOMIC = 300000000n; let collateralClaimContracts = []; let collateralClaimLoadPromise = null; let collateralClaimContractsKey = ""; function setCollateralClaimMessage(message, type = "") { setScopedTransactionMessage("Collateral Claim", collateralClaimMessage, message, type); } function selectedCollateralClaimContract() { const hash = String(collateralClaimContractSelect?.value || ""); return collateralClaimContracts.find((contract) => contract.contract === hash) || null; } function claimAssetLabel(asset, amount) { return `${atomicToDecimal(BigInt(amount || "0"))} ${String(asset || "").trim()}`.trim(); } function collateralClaimOptionLabel(contract) { const role = String(contract.role || "").toLowerCase() === "borrower" ? "reclaim" : "claim"; return `${role} ${claimAssetLabel(contract.collateral, contract.collateral_amount)} from ${truncateMiddle(contract.contract, 8, 8)}`; } function populateCollateralClaimContracts() { if (!collateralClaimContractSelect) { return; } const previous = collateralClaimContractSelect.value; collateralClaimContractSelect.innerHTML = ""; if (collateralClaimContracts.length) { const placeholder = document.createElement("option"); placeholder.value = ""; placeholder.textContent = "Select claimable collateral"; collateralClaimContractSelect.appendChild(placeholder); } collateralClaimContracts.forEach((contract) => { const option = document.createElement("option"); option.value = contract.contract; option.textContent = collateralClaimOptionLabel(contract); collateralClaimContractSelect.appendChild(option); }); if (!collateralClaimContracts.length) { const option = document.createElement("option"); option.value = ""; option.textContent = "No collateral is currently claimable"; collateralClaimContractSelect.appendChild(option); } else if ([...collateralClaimContractSelect.options].some((option) => option.value === previous)) { collateralClaimContractSelect.value = previous; } updateCollateralClaimDetails(); } async function loadCollateralClaimContracts(force = false) { if (collateralClaimLoadPromise) { return collateralClaimLoadPromise; } const invoke = window.__TAURI__?.core?.invoke; const broadcastNode = activeBroadcastNode(); if (!invoke || !broadcastNode || !walletLoaded || !registrationRegistered) { return; } const key = `${activeWalletAddressShort || activeWalletAddressHex}|${broadcastNode}`; if (!force && collateralClaimContractsKey === key) { populateCollateralClaimContracts(); return; } collateralClaimLoadPromise = (async () => { setCollateralClaimMessage("Loading claimable collateral..."); const contracts = await invoke("claimable_collateral_contracts", { broadcastNode }); collateralClaimContracts = Array.isArray(contracts) ? contracts : []; collateralClaimContractsKey = key; populateCollateralClaimContracts(); setCollateralClaimMessage( collateralClaimContracts.length ? "" : "No collateral is currently claimable." ); })().catch((error) => { collateralClaimContracts = []; populateCollateralClaimContracts(); setCollateralClaimMessage(String(error), "error"); throw error; }).finally(() => { collateralClaimLoadPromise = null; }); return collateralClaimLoadPromise; } function updateCollateralClaimDetails() { const contract = selectedCollateralClaimContract(); if (!contract) { collateralClaimRole.textContent = "--"; collateralClaimReason.textContent = "--"; collateralClaimLender.textContent = "--"; collateralClaimBorrower.textContent = "--"; collateralClaimLoan.textContent = "--"; collateralClaimCollateral.textContent = "--"; collateralClaimConfirmed.textContent = "--"; collateralClaimRemaining.textContent = "--"; collateralClaimFeeInput.value = atomicToDecimal(COLLATERAL_CLAIM_FEE_ATOMIC); return; } collateralClaimRole.textContent = contract.role === "borrower" ? "Borrower reclaim" : "Lender claim"; collateralClaimReason.textContent = contract.reason || "Collateral claim is currently allowed."; collateralClaimLender.textContent = contract.lender; collateralClaimBorrower.textContent = contract.borrower; collateralClaimLoan.textContent = claimAssetLabel(contract.loan_asset, contract.loan_amount); collateralClaimCollateral.textContent = claimAssetLabel(contract.collateral, contract.collateral_amount); collateralClaimConfirmed.textContent = claimAssetLabel(contract.loan_asset, contract.confirmed_paid); collateralClaimRemaining.textContent = claimAssetLabel(contract.loan_asset, contract.remaining_balance); collateralClaimFeeInput.value = atomicToDecimal(BigInt(contract.fee || COLLATERAL_CLAIM_FEE_ATOMIC)); } function validateCollateralClaimForm() { if (!walletLoaded) { return "Load a wallet before claiming collateral."; } if (!registrationRegistered) { return "Address registration is required before claiming collateral."; } if (!selectedCollateralClaimContract()) { return "Select claimable collateral."; } if (baseCoinBalanceAtomic() < COLLATERAL_CLAIM_FEE_ATOMIC) { return `Claiming collateral requires ${atomicToDecimal(COLLATERAL_CLAIM_FEE_ATOMIC)} ${activeBaseCoin()} for the transaction fee.`; } return ""; } function collateralClaimPayload() { return { broadcastNode: activeBroadcastNode(), contractHash: selectedCollateralClaimContract().contract }; } function setCollateralClaimButtonsDisabled(disabled) { document.querySelector("[data-collateral-claim-save]")?.toggleAttribute("disabled", disabled); document.querySelector("[data-collateral-claim-broadcast]")?.toggleAttribute("disabled", disabled); } function clearCollateralClaimForm() { collateralClaimContractSelect.value = ""; updateCollateralClaimDetails(); } async function handleCollateralClaimAction(action) { const validationError = validateCollateralClaimForm(); if (validationError) { setCollateralClaimMessage(validationError, "error"); return; } const invoke = window.__TAURI__?.core?.invoke; if (!invoke) { setCollateralClaimMessage("Wallet bridge is not available.", "error"); return; } const walletKey = await requestSigningWalletKey(); if (guiRuntimeSettings.require_key_before_signing !== false && !walletKey) { setCollateralClaimMessage("Signing cancelled."); return; } setCollateralClaimButtonsDisabled(true); try { if (action === "save") { setCollateralClaimMessage("Saving collateral claim transaction..."); const review = await invoke("save_collateral_claim_transaction", { ...collateralClaimPayload(), walletKey }); clearCollateralClaimForm(); setCollateralClaimMessage(`Collateral claim saved: ${review.file_name}`, "success"); if (typeof refreshUnbroadcastTransactions === "function") { refreshUnbroadcastTransactions(); } } else { setCollateralClaimMessage("Broadcasting collateral claim..."); const result = await invoke("broadcast_collateral_claim_transaction", { ...collateralClaimPayload(), walletKey }); await loadCollateralClaimContracts(true); clearCollateralClaimForm(); setCollateralClaimMessage(result.response || "Collateral claim broadcast.", "success"); } } catch (error) { setCollateralClaimMessage(String(error), "error"); await loadCollateralClaimContracts(true).catch(() => {}); } finally { setCollateralClaimButtonsDisabled(false); } } function prepareCollateralClaimForm() { setCollateralClaimMessage(""); loadCollateralClaimContracts(false).catch(() => {}); } function resetCollateralClaimSession() { collateralClaimContracts = []; collateralClaimLoadPromise = null; collateralClaimContractsKey = ""; collateralClaimContractSelect.innerHTML = ""; updateCollateralClaimDetails(); setCollateralClaimMessage(""); } collateralClaimContractSelect?.addEventListener("change", () => { setCollateralClaimMessage(""); updateCollateralClaimDetails(); }); document .querySelector("[data-collateral-claim-save]") ?.addEventListener("click", () => handleCollateralClaimAction("save")); document .querySelector("[data-collateral-claim-broadcast]") ?.addEventListener("click", () => handleCollateralClaimAction("broadcast"));