Add launcher wallet balance and address utility actions
This commit is contained in:
parent
18a0d6fe29
commit
96cbba87ca
@ -37,6 +37,7 @@ Top-level control surface:
|
||||
6. Pull-first updates feed + support ticket action
|
||||
7. Identity assurance visibility (`none` / `crypto_direct_unattested` / `sponsored_unattested` / `onramp_attested`)
|
||||
8. Explicit operator-visible mode toggles (`Human mode` / `Auto mode`) synced to governance `operation_mode`
|
||||
9. Wallet utility actions (`Refresh balances`, `Copy address`) with native + USDC balance visibility
|
||||
|
||||
Advanced integration controls (collapsible):
|
||||
|
||||
|
||||
111
app/app.js
111
app/app.js
@ -12,6 +12,8 @@ const state = {
|
||||
walletSessionToken: "",
|
||||
walletSessionExpiresAt: "",
|
||||
walletSessionRefreshInFlight: null,
|
||||
walletBalanceNative: "",
|
||||
walletBalanceUSDC: "",
|
||||
};
|
||||
|
||||
function nowISO() {
|
||||
@ -77,6 +79,12 @@ function sessionSummary() {
|
||||
return `active (exp ${state.walletSessionExpiresAt})`;
|
||||
}
|
||||
|
||||
function walletBalanceSummary() {
|
||||
const native = state.walletBalanceNative || "-- ETH";
|
||||
const usdc = state.walletBalanceUSDC || "-- USDC";
|
||||
return `${native} | ${usdc}`;
|
||||
}
|
||||
|
||||
function clearWalletSession(reason, payload = {}) {
|
||||
if (!state.walletSessionToken && !state.walletSessionExpiresAt) {
|
||||
return;
|
||||
@ -184,6 +192,41 @@ function utf8ToHex(value) {
|
||||
.join("")}`;
|
||||
}
|
||||
|
||||
function normalizeHexQuantity(value) {
|
||||
const raw = String(value || "").trim().toLowerCase();
|
||||
if (!raw.startsWith("0x")) {
|
||||
return null;
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
function formatUnitsHex(hexValue, decimals, precision = 6) {
|
||||
const hex = normalizeHexQuantity(hexValue);
|
||||
if (!hex) {
|
||||
return null;
|
||||
}
|
||||
let bigint;
|
||||
try {
|
||||
bigint = BigInt(hex);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
const unit = 10n ** BigInt(Math.max(0, Number(decimals) || 0));
|
||||
const whole = bigint / unit;
|
||||
const fraction = bigint % unit;
|
||||
if (fraction === 0n) {
|
||||
return whole.toString();
|
||||
}
|
||||
const padded = fraction.toString().padStart(Number(decimals), "0");
|
||||
const trimmed = padded.slice(0, Math.max(1, precision)).replace(/0+$/, "");
|
||||
return trimmed ? `${whole.toString()}.${trimmed}` : whole.toString();
|
||||
}
|
||||
|
||||
function encodeAddressWord(address) {
|
||||
const normalized = normalizedAddress(address).replace(/^0x/, "");
|
||||
return normalized.padStart(64, "0");
|
||||
}
|
||||
|
||||
function logLine(label, payload) {
|
||||
const log = $("log");
|
||||
const line = `[${nowISO()}] ${label}\n${JSON.stringify(payload, null, 2)}\n\n`;
|
||||
@ -216,6 +259,7 @@ function refreshActionLocks(statusPayload) {
|
||||
function refreshOverview(statusPayload) {
|
||||
const currentWallet = wallet();
|
||||
setSummary("summaryWallet", currentWallet || "not connected");
|
||||
setSummary("summaryFunds", walletBalanceSummary());
|
||||
setSummary("summarySession", sessionSummary());
|
||||
refreshModeUI();
|
||||
if (statusPayload && typeof statusPayload === "object") {
|
||||
@ -453,9 +497,74 @@ async function onConnectWallet() {
|
||||
provider_chain_id: providerChainID,
|
||||
});
|
||||
}
|
||||
try {
|
||||
await onRefreshBalances();
|
||||
} catch (err) {
|
||||
logLine("wallet balance refresh warning", { error: String(err) });
|
||||
}
|
||||
refreshOverview();
|
||||
}
|
||||
|
||||
async function onRefreshBalances() {
|
||||
const address = normalizedAddress(requireWallet());
|
||||
const provider = await requireProvider();
|
||||
const nativeHex = await provider.request({
|
||||
method: "eth_getBalance",
|
||||
params: [address, "latest"],
|
||||
});
|
||||
const nativeDisplay = formatUnitsHex(nativeHex, 18, 6);
|
||||
if (nativeDisplay) {
|
||||
state.walletBalanceNative = `${nativeDisplay} ETH`;
|
||||
}
|
||||
|
||||
const usdcToken = normalizedAddress($("usdcTokenAddress")?.value || "");
|
||||
if (usdcToken && /^0x[a-f0-9]{40}$/.test(usdcToken)) {
|
||||
const balanceOfSelector = "0x70a08231";
|
||||
const decimalsSelector = "0x313ce567";
|
||||
const balanceCallData = `${balanceOfSelector}${encodeAddressWord(address)}`;
|
||||
const usdcHex = await provider.request({
|
||||
method: "eth_call",
|
||||
params: [{ to: usdcToken, data: balanceCallData }, "latest"],
|
||||
});
|
||||
let decimals = 6;
|
||||
try {
|
||||
const decimalsHex = await provider.request({
|
||||
method: "eth_call",
|
||||
params: [{ to: usdcToken, data: decimalsSelector }, "latest"],
|
||||
});
|
||||
const parsed = Number(BigInt(String(decimalsHex || "0x6")));
|
||||
if (Number.isFinite(parsed) && parsed >= 0 && parsed <= 36) {
|
||||
decimals = parsed;
|
||||
}
|
||||
} catch {
|
||||
// keep default 6 when token decimals call is unavailable
|
||||
}
|
||||
const usdcDisplay = formatUnitsHex(usdcHex, decimals, 2);
|
||||
if (usdcDisplay) {
|
||||
state.walletBalanceUSDC = `${usdcDisplay} USDC`;
|
||||
}
|
||||
} else if (usdcToken) {
|
||||
state.walletBalanceUSDC = "invalid token";
|
||||
}
|
||||
|
||||
refreshOverview();
|
||||
logLine("wallet balances refreshed", {
|
||||
wallet: address,
|
||||
native: state.walletBalanceNative || "",
|
||||
usdc: state.walletBalanceUSDC || "",
|
||||
});
|
||||
}
|
||||
|
||||
async function onCopyWallet() {
|
||||
const address = requireWallet();
|
||||
if (!navigator?.clipboard?.writeText) {
|
||||
throw new Error("clipboard API unavailable");
|
||||
}
|
||||
await navigator.clipboard.writeText(address);
|
||||
setFlowStatus("wallet address copied");
|
||||
logLine("wallet copied", { wallet: address });
|
||||
}
|
||||
|
||||
async function onSignIntent() {
|
||||
if (!state.lastIntent) {
|
||||
throw new Error("create intent before signing");
|
||||
@ -1119,6 +1228,8 @@ bind("btnQuickConnect", onQuickConnect);
|
||||
bind("btnQuickActivate", onQuickActivate);
|
||||
bind("btnQuickRefresh", onQuickRefresh);
|
||||
bind("btnQuickInstallStatus", onQuickInstallStatus);
|
||||
bind("btnRefreshBalances", onRefreshBalances);
|
||||
bind("btnCopyWallet", onCopyWallet);
|
||||
bind("btnModeHuman", onModeHuman);
|
||||
bind("btnModeAuto", onModeAuto);
|
||||
bind("btnConnectWallet", onConnectWallet);
|
||||
|
||||
@ -20,6 +20,8 @@
|
||||
<button id="btnQuickActivate">Activate membership</button>
|
||||
<button id="btnQuickRefresh">Refresh status + feed</button>
|
||||
<button id="btnQuickInstallStatus">Governance status</button>
|
||||
<button id="btnRefreshBalances">Refresh balances</button>
|
||||
<button id="btnCopyWallet">Copy address</button>
|
||||
<button id="btnModeHuman" class="mode-active">Human mode</button>
|
||||
<button id="btnModeAuto">Auto mode</button>
|
||||
</div>
|
||||
@ -33,6 +35,10 @@
|
||||
<h3>Session</h3>
|
||||
<p id="summarySession">none</p>
|
||||
</article>
|
||||
<article class="stat">
|
||||
<h3>Wallet Funds</h3>
|
||||
<p id="summaryFunds">-- ETH | -- USDC</p>
|
||||
</article>
|
||||
<article class="stat">
|
||||
<h3>Membership</h3>
|
||||
<p id="summaryMembership">unknown</p>
|
||||
@ -96,6 +102,10 @@
|
||||
Chain ID
|
||||
<input id="chainId" type="number" value="84532" />
|
||||
</label>
|
||||
<label>
|
||||
USDC token address
|
||||
<input id="usdcTokenAddress" value="0x036cbd53842c5426634e7929541ec2318f3dcf7e" />
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user