172 lines
5.6 KiB
JavaScript
172 lines
5.6 KiB
JavaScript
const CREATE_TOKEN_FEE_ATOMIC = 50000000000n;
|
|
const TOKEN_ATOMIC_UNITS = 100000000n;
|
|
|
|
function setCreateTokenMessage(message, type = "") {
|
|
setScopedTransactionMessage("Create Token", createTokenMessage, message, type);
|
|
}
|
|
|
|
function tokenDecimalToAtomic(value) {
|
|
const text = String(value || "").trim();
|
|
if (!/^\d+(\.\d{0,8})?$/.test(text)) {
|
|
return null;
|
|
}
|
|
|
|
const [whole, fraction = ""] = text.split(".");
|
|
const paddedFraction = fraction.padEnd(8, "0");
|
|
return BigInt(whole || "0") * TOKEN_ATOMIC_UNITS + BigInt(paddedFraction || "0");
|
|
}
|
|
|
|
function tokenAtomicToDecimal(value) {
|
|
const atomic = BigInt(value || 0);
|
|
const whole = atomic / TOKEN_ATOMIC_UNITS;
|
|
const fraction = String(atomic % TOKEN_ATOMIC_UNITS).padStart(8, "0");
|
|
return `${whole}.${fraction}`;
|
|
}
|
|
|
|
function updateCreateTokenDerivedFields() {
|
|
if (createTokenFeeInput) {
|
|
createTokenFeeInput.value = tokenAtomicToDecimal(CREATE_TOKEN_FEE_ATOMIC);
|
|
}
|
|
if (createTokenFeeLabel) {
|
|
createTokenFeeLabel.textContent = `${tokenAtomicToDecimal(CREATE_TOKEN_FEE_ATOMIC)} ${activeBaseCoin()}`;
|
|
}
|
|
if (createTokenCap) {
|
|
createTokenCap.textContent = createTokenAllowMoreInput?.checked ? "Future issuance allowed" : "Hard capped";
|
|
}
|
|
if (createTokenCreator) {
|
|
createTokenCreator.textContent = activeWalletAddressShort || activeWalletAddressHex || "--";
|
|
}
|
|
}
|
|
|
|
function validateCreateTokenForm() {
|
|
if (!walletLoaded) {
|
|
return "Load a wallet before creating a token.";
|
|
}
|
|
if (!registrationRegistered) {
|
|
return "Address registration is required before creating a token.";
|
|
}
|
|
|
|
const ticker = String(createTokenTickerInput?.value || "").trim();
|
|
if (!ticker) {
|
|
return "Token ticker is required.";
|
|
}
|
|
if (!/^[A-Za-z0-9\s]+$/.test(ticker)) {
|
|
return "Token ticker can only contain letters, numbers, and spaces.";
|
|
}
|
|
const canonicalTicker = ticker.replace(/\s+/g, "").toLowerCase();
|
|
if (canonicalTicker.length < 3 || canonicalTicker.length > 15) {
|
|
return "Token ticker must normalize to 3 to 15 letters or numbers.";
|
|
}
|
|
if (canonicalTicker.toUpperCase() === activeBaseCoin()) {
|
|
return "Token ticker is reserved for the base coin.";
|
|
}
|
|
|
|
const number = tokenDecimalToAtomic(createTokenNumberInput?.value);
|
|
if (number === null || number <= 0n) {
|
|
return "Enter a valid initial supply with up to 8 decimal places.";
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
function createTokenPayload() {
|
|
return {
|
|
ticker: String(createTokenTickerInput?.value || "").trim(),
|
|
number: String(tokenDecimalToAtomic(createTokenNumberInput?.value) || 0n),
|
|
allowMore: Boolean(createTokenAllowMoreInput?.checked),
|
|
txfee: String(CREATE_TOKEN_FEE_ATOMIC)
|
|
};
|
|
}
|
|
|
|
function clearCreateTokenForm() {
|
|
if (createTokenTickerInput) {
|
|
createTokenTickerInput.value = "";
|
|
}
|
|
if (createTokenNumberInput) {
|
|
createTokenNumberInput.value = "";
|
|
}
|
|
if (createTokenAllowMoreInput) {
|
|
createTokenAllowMoreInput.checked = false;
|
|
}
|
|
updateCreateTokenDerivedFields();
|
|
}
|
|
|
|
function setCreateTokenButtonsDisabled(disabled) {
|
|
document.querySelector("[data-create-token-save]")?.toggleAttribute("disabled", disabled);
|
|
document.querySelector("[data-create-token-broadcast]")?.toggleAttribute("disabled", disabled);
|
|
}
|
|
|
|
async function handleCreateTokenAction(action) {
|
|
const error = validateCreateTokenForm();
|
|
if (error) {
|
|
setCreateTokenMessage(error, "error");
|
|
return;
|
|
}
|
|
|
|
const invoke = window.__TAURI__?.core?.invoke;
|
|
if (!invoke) {
|
|
setCreateTokenMessage("Wallet bridge is not available.", "error");
|
|
return;
|
|
}
|
|
|
|
const walletKey = await requestSigningWalletKey();
|
|
if (guiRuntimeSettings.require_key_before_signing !== false && !walletKey) {
|
|
setCreateTokenMessage("Signing cancelled.");
|
|
return;
|
|
}
|
|
|
|
setCreateTokenButtonsDisabled(true);
|
|
try {
|
|
if (action === "save") {
|
|
setCreateTokenMessage("Saving token transaction...");
|
|
const review = await invoke("save_create_token_transaction", {
|
|
...createTokenPayload(),
|
|
walletKey
|
|
});
|
|
clearCreateTokenForm();
|
|
setCreateTokenMessage(`Token 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.");
|
|
}
|
|
setCreateTokenMessage("Broadcasting token transaction...");
|
|
const result = await invoke("broadcast_create_token_transaction", {
|
|
...createTokenPayload(),
|
|
broadcastNode,
|
|
walletKey
|
|
});
|
|
if (typeof invalidateAssetsCatalog === "function") {
|
|
invalidateAssetsCatalog();
|
|
}
|
|
clearCreateTokenForm();
|
|
setCreateTokenMessage(result.response || "Token transaction broadcast.", "success");
|
|
}
|
|
} catch (error) {
|
|
setCreateTokenMessage(String(error), "error");
|
|
} finally {
|
|
setCreateTokenButtonsDisabled(false);
|
|
}
|
|
}
|
|
|
|
function prepareCreateTokenForm() {
|
|
updateCreateTokenDerivedFields();
|
|
setCreateTokenMessage("");
|
|
}
|
|
|
|
createTokenTickerInput?.addEventListener("input", () => {
|
|
updateCreateTokenDerivedFields();
|
|
setCreateTokenMessage("");
|
|
});
|
|
createTokenNumberInput?.addEventListener("input", () => setCreateTokenMessage(""));
|
|
createTokenAllowMoreInput?.addEventListener("change", () => {
|
|
updateCreateTokenDerivedFields();
|
|
setCreateTokenMessage("");
|
|
});
|
|
document.querySelector("[data-create-token-save]")?.addEventListener("click", () => handleCreateTokenAction("save"));
|
|
document.querySelector("[data-create-token-broadcast]")?.addEventListener("click", () => handleCreateTokenAction("broadcast"));
|
|
|