diff --git a/README.md b/README.md index d4aaeef..21b4be7 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ Launcher never contains private kernel internals. It verifies and installs signe 4. Governance install token/confirm/status 5. Lease heartbeat + offline renew 6. Injected wallet automation for intent signing and membership tx send +7. One-click `Run membership flow` path (intent -> verify -> quote -> tx -> confirm) Wallet automation shortcuts in the shell: diff --git a/app/app.js b/app/app.js index d321538..e45f47b 100644 --- a/app/app.js +++ b/app/app.js @@ -52,6 +52,17 @@ function logLine(label, payload) { log.textContent = line + log.textContent; } +function setFlowStatus(message) { + const el = $("flowStatus"); + if (el) { + el.textContent = message; + } +} + +function sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + async function request(method, path, body) { const opts = { method, @@ -330,6 +341,7 @@ async function onStatus() { $("designationCode").value = out.designation_code; } logLine("membership status", out); + return out; } async function onQuote() { @@ -356,6 +368,7 @@ async function onQuote() { $("quoteValue").value = out.value || ""; $("quotePayer").value = out.payer_wallet || ""; logLine("membership quote", out); + return out; } async function onConfirmMembership() { @@ -367,6 +380,81 @@ async function onConfirmMembership() { chain_id: chainID(), }); logLine("membership confirm", out); + return out; +} + +async function waitForTxMined(txHash, timeoutMs = 120000, intervalMs = 3000) { + const provider = await requireProvider(); + const started = Date.now(); + while (Date.now() - started < timeoutMs) { + const receipt = await provider.request({ + method: "eth_getTransactionReceipt", + params: [txHash], + }); + if (receipt) { + const statusHex = String(receipt.status || "").toLowerCase(); + if (statusHex === "0x1" || statusHex === "1") { + return receipt; + } + throw new Error(`membership transaction reverted: status=${receipt.status}`); + } + await sleep(intervalMs); + } + throw new Error(`membership transaction not mined within ${timeoutMs}ms`); +} + +async function confirmMembershipWithRetry(maxAttempts = 8, intervalMs = 2500) { + let lastErr = null; + for (let attempt = 1; attempt <= maxAttempts; attempt += 1) { + try { + const out = await onConfirmMembership(); + return out; + } catch (err) { + lastErr = err; + const message = String(err || ""); + if (!message.includes("tx verification pending/failed")) { + throw err; + } + setFlowStatus(`confirm pending (${attempt}/${maxAttempts})`); + await sleep(intervalMs); + } + } + throw lastErr || new Error("membership confirm failed"); +} + +async function onRunMembershipFlow() { + setFlowStatus("connecting wallet"); + await onConnectWallet(); + + setFlowStatus("checking membership"); + const status = await onStatus(); + if (String(status.status || "").toLowerCase() === "active") { + setFlowStatus("membership already active"); + return; + } + + setFlowStatus("creating intent"); + await onIntent(); + setFlowStatus("signing intent"); + await onSignIntent(); + setFlowStatus("verifying intent"); + await onVerify(); + setFlowStatus("quoting membership"); + await onQuote(); + setFlowStatus("sending membership transaction"); + await onSendMembershipTx(); + + const txHash = $("confirmTxHash").value.trim(); + if (!txHash) { + throw new Error("missing tx hash after send"); + } + setFlowStatus("waiting for chain confirmation"); + await waitForTxMined(txHash); + setFlowStatus("confirming membership with API"); + await confirmMembershipWithRetry(); + setFlowStatus("refreshing status"); + await onStatus(); + setFlowStatus("membership flow complete"); } async function onRegisterChannel() { @@ -500,6 +588,7 @@ function bind(id, handler) { } bind("btnConnectWallet", onConnectWallet); +bind("btnRunMembershipFlow", onRunMembershipFlow); bind("btnIntent", onIntent); bind("btnSignIntent", onSignIntent); bind("btnVerify", onVerify); diff --git a/app/index.html b/app/index.html index 55e204a..da189bb 100644 --- a/app/index.html +++ b/app/index.html @@ -18,7 +18,7 @@