242 lines
7.1 KiB
JavaScript
242 lines
7.1 KiB
JavaScript
|
|
const VANITY_ADDRESS_FEE_ATOMIC = 500000000n;
|
||
|
|
|
||
|
|
function setVanityMessage(message, type = "") {
|
||
|
|
setScopedTransactionMessage("Vanity Address", vanityMessage, message, type);
|
||
|
|
}
|
||
|
|
|
||
|
|
function vanityNetworkSuffix() {
|
||
|
|
return activeBaseCoin().toLowerCase() || "cltc";
|
||
|
|
}
|
||
|
|
|
||
|
|
function normalizedVanityName() {
|
||
|
|
return String(vanityNameInput?.value || "").trim().toLowerCase();
|
||
|
|
}
|
||
|
|
|
||
|
|
function vanityPreviewValue() {
|
||
|
|
const name = normalizedVanityName();
|
||
|
|
return name ? `${name}.${vanityNetworkSuffix()}` : "--";
|
||
|
|
}
|
||
|
|
|
||
|
|
function displayVanityAddress(vanityAddress) {
|
||
|
|
const value = String(vanityAddress || "").trim();
|
||
|
|
const separator = value.lastIndexOf(".");
|
||
|
|
if (separator < 1) {
|
||
|
|
return value || "N/A";
|
||
|
|
}
|
||
|
|
const payload = value.slice(0, separator).trimStart();
|
||
|
|
const suffix = value.slice(separator + 1);
|
||
|
|
return payload ? `${payload}.${suffix}` : "N/A";
|
||
|
|
}
|
||
|
|
|
||
|
|
function applyActiveVanityDisplay(vanityAddress) {
|
||
|
|
const display = displayVanityAddress(vanityAddress);
|
||
|
|
document.querySelectorAll("[data-active-vanity]").forEach((item) => {
|
||
|
|
item.textContent = display;
|
||
|
|
});
|
||
|
|
document.querySelectorAll("[data-copy-vanity]").forEach((button) => {
|
||
|
|
button.disabled = display === "N/A";
|
||
|
|
button.textContent = "Copy";
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
function applyActiveVanityDisplayFromTransaction(transaction) {
|
||
|
|
const vanityAddress = transaction?.vanity_address;
|
||
|
|
const owner = transaction?.address;
|
||
|
|
const active = activeWalletAddressShort || activeWalletAddressHex || "";
|
||
|
|
if (vanityAddress && owner && String(owner).trim().toLowerCase() === active.toLowerCase()) {
|
||
|
|
applyActiveVanityDisplay(vanityAddress);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function updateVanityDerivedFields() {
|
||
|
|
if (vanityFeeInput) {
|
||
|
|
vanityFeeInput.value = atomicToDecimal(VANITY_ADDRESS_FEE_ATOMIC);
|
||
|
|
}
|
||
|
|
if (vanityOwner) {
|
||
|
|
vanityOwner.textContent = activeWalletAddressShort || activeWalletAddressHex || "--";
|
||
|
|
}
|
||
|
|
if (vanityPreview) {
|
||
|
|
vanityPreview.textContent = vanityPreviewValue();
|
||
|
|
}
|
||
|
|
if (vanityFeeLabel) {
|
||
|
|
vanityFeeLabel.textContent = `${atomicToDecimal(VANITY_ADDRESS_FEE_ATOMIC)} ${activeBaseCoin()}`;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function validateVanityForm() {
|
||
|
|
if (!walletLoaded) {
|
||
|
|
return "Load a wallet before creating a vanity address.";
|
||
|
|
}
|
||
|
|
if (!registrationRegistered) {
|
||
|
|
return "Address registration is required before creating a vanity address.";
|
||
|
|
}
|
||
|
|
const name = String(vanityNameInput?.value || "").trim();
|
||
|
|
if (!name) {
|
||
|
|
return "Vanity name is required.";
|
||
|
|
}
|
||
|
|
if (name.length > 20) {
|
||
|
|
return "Vanity name cannot exceed 20 letters.";
|
||
|
|
}
|
||
|
|
if (!/^[A-Za-z]+$/.test(name)) {
|
||
|
|
return "Vanity name can only contain letters.";
|
||
|
|
}
|
||
|
|
if (baseCoinBalanceAtomic() < VANITY_ADDRESS_FEE_ATOMIC) {
|
||
|
|
return `Vanity address registration requires ${atomicToDecimal(VANITY_ADDRESS_FEE_ATOMIC)} ${activeBaseCoin()} for the fee.`;
|
||
|
|
}
|
||
|
|
return "";
|
||
|
|
}
|
||
|
|
|
||
|
|
function validateVanityAvailabilityCheck() {
|
||
|
|
if (!walletLoaded) {
|
||
|
|
return "Load a wallet before checking a vanity address.";
|
||
|
|
}
|
||
|
|
if (!activeBroadcastNode()) {
|
||
|
|
return "Select a broadcast node before checking a vanity address.";
|
||
|
|
}
|
||
|
|
const name = String(vanityNameInput?.value || "").trim();
|
||
|
|
if (!name) {
|
||
|
|
return "Vanity name is required.";
|
||
|
|
}
|
||
|
|
if (name.length > 20) {
|
||
|
|
return "Vanity name cannot exceed 20 letters.";
|
||
|
|
}
|
||
|
|
if (!/^[A-Za-z]+$/.test(name)) {
|
||
|
|
return "Vanity name can only contain letters.";
|
||
|
|
}
|
||
|
|
return "";
|
||
|
|
}
|
||
|
|
|
||
|
|
function vanityPayload() {
|
||
|
|
return {
|
||
|
|
vanityName: String(vanityNameInput.value || "").trim(),
|
||
|
|
txfee: String(VANITY_ADDRESS_FEE_ATOMIC)
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
function clearVanityForm() {
|
||
|
|
if (vanityNameInput) {
|
||
|
|
vanityNameInput.value = "";
|
||
|
|
}
|
||
|
|
updateVanityDerivedFields();
|
||
|
|
}
|
||
|
|
|
||
|
|
function setVanityButtonsDisabled(disabled) {
|
||
|
|
document.querySelector("[data-vanity-save]")?.toggleAttribute("disabled", disabled);
|
||
|
|
document.querySelector("[data-vanity-broadcast]")?.toggleAttribute("disabled", disabled);
|
||
|
|
document.querySelector("[data-vanity-check]")?.toggleAttribute("disabled", disabled);
|
||
|
|
}
|
||
|
|
|
||
|
|
async function checkVanityAvailability() {
|
||
|
|
const validationError = validateVanityAvailabilityCheck();
|
||
|
|
if (validationError) {
|
||
|
|
setVanityMessage(validationError, "error");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const invoke = window.__TAURI__?.core?.invoke;
|
||
|
|
if (!invoke) {
|
||
|
|
setVanityMessage("Wallet bridge is not available.", "error");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
setVanityButtonsDisabled(true);
|
||
|
|
try {
|
||
|
|
setVanityMessage("Checking vanity address availability...");
|
||
|
|
const result = await invoke("check_vanity_address_availability", {
|
||
|
|
broadcastNode: activeBroadcastNode(),
|
||
|
|
vanityName: String(vanityNameInput.value || "").trim()
|
||
|
|
});
|
||
|
|
|
||
|
|
if (result?.available) {
|
||
|
|
setVanityMessage(`${result.vanity_address} is available.`, "success");
|
||
|
|
} else {
|
||
|
|
setVanityMessage(`${result?.vanity_address || vanityPreviewValue()} is already registered.`, "error");
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
setVanityMessage(String(error), "error");
|
||
|
|
} finally {
|
||
|
|
setVanityButtonsDisabled(false);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function handleVanityAction(action) {
|
||
|
|
const validationError = validateVanityForm();
|
||
|
|
if (validationError) {
|
||
|
|
setVanityMessage(validationError, "error");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const invoke = window.__TAURI__?.core?.invoke;
|
||
|
|
if (!invoke) {
|
||
|
|
setVanityMessage("Wallet bridge is not available.", "error");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const walletKey = await requestSigningWalletKey();
|
||
|
|
if (guiRuntimeSettings.require_key_before_signing !== false && !walletKey) {
|
||
|
|
setVanityMessage("Signing cancelled.");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
setVanityButtonsDisabled(true);
|
||
|
|
try {
|
||
|
|
if (action === "save") {
|
||
|
|
setVanityMessage("Saving vanity address transaction...");
|
||
|
|
const review = await invoke("save_vanity_address_transaction", {
|
||
|
|
...vanityPayload(),
|
||
|
|
walletKey
|
||
|
|
});
|
||
|
|
clearVanityForm();
|
||
|
|
setVanityMessage(`Vanity address 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.");
|
||
|
|
}
|
||
|
|
setVanityMessage("Broadcasting vanity address transaction...");
|
||
|
|
const result = await invoke("broadcast_vanity_address_transaction", {
|
||
|
|
...vanityPayload(),
|
||
|
|
broadcastNode,
|
||
|
|
walletKey
|
||
|
|
});
|
||
|
|
applyActiveVanityDisplay(vanityPreviewValue());
|
||
|
|
clearVanityForm();
|
||
|
|
setVanityMessage(result.response || "Vanity address transaction broadcast.", "success");
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
setVanityMessage(String(error), "error");
|
||
|
|
} finally {
|
||
|
|
setVanityButtonsDisabled(false);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function prepareVanityAddressForm() {
|
||
|
|
updateVanityDerivedFields();
|
||
|
|
setVanityMessage("");
|
||
|
|
}
|
||
|
|
|
||
|
|
function resetVanityAddressSession() {
|
||
|
|
clearVanityForm();
|
||
|
|
setVanityMessage("");
|
||
|
|
}
|
||
|
|
|
||
|
|
vanityNameInput?.addEventListener("input", () => {
|
||
|
|
setVanityMessage("");
|
||
|
|
updateVanityDerivedFields();
|
||
|
|
});
|
||
|
|
|
||
|
|
document
|
||
|
|
.querySelector("[data-vanity-save]")
|
||
|
|
?.addEventListener("click", () => handleVanityAction("save"));
|
||
|
|
|
||
|
|
document
|
||
|
|
.querySelector("[data-vanity-broadcast]")
|
||
|
|
?.addEventListener("click", () => handleVanityAction("broadcast"));
|
||
|
|
|
||
|
|
document
|
||
|
|
.querySelector("[data-vanity-check]")
|
||
|
|
?.addEventListener("click", checkVanityAvailability);
|