viveworker 0.8.5 → 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +18 -10
- package/scripts/share-cli.mjs +322 -41
- package/scripts/viveworker-bridge.mjs +458 -39
- package/web/app.css +195 -0
- package/web/app.js +1017 -237
- package/web/build-id.js +1 -1
- package/web/i18n.js +110 -16
- package/web/sw.js +4 -0
package/web/app.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_LOCALE, SUPPORTED_LOCALES, localeDisplayName, normalizeLocale, resolveLocalePreference, t } from "./i18n.js";
|
|
1
|
+
import { DEFAULT_LOCALE, SUPPORTED_LOCALES, localeDisplayName, normalizeLocale, resolveLocalePreference, t } from "./i18n.js?v=__VIVEWORKER_APP_BUILD_ID__";
|
|
2
2
|
import { ensureIdentityKeypair, bytesToHex } from "./remote-pairing/keys.js";
|
|
3
3
|
import {
|
|
4
4
|
loadPairingState as loadRemotePairingState,
|
|
@@ -51,6 +51,7 @@ const TIMELINE_STICKY_LAN_PROBE_TIMEOUT_MS = 350;
|
|
|
51
51
|
const CLIENT_EVENT_REPORT_TIMEOUT_MS = 1_800;
|
|
52
52
|
const TIMELINE_LIVE_REFRESH_TIMEOUT_MS = 2_000;
|
|
53
53
|
const TIMELINE_LIVE_RETRY_MS = 5_000;
|
|
54
|
+
const HAZBASE_ACTION_TIMEOUT_MS = 30_000;
|
|
54
55
|
const timelineImageObjectUrlCache = new Map();
|
|
55
56
|
|
|
56
57
|
function wait(ms) {
|
|
@@ -141,12 +142,11 @@ const state = {
|
|
|
141
142
|
hazbaseStatus: null,
|
|
142
143
|
hazbaseNotice: "",
|
|
143
144
|
hazbaseError: "",
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
hazbaseMainnetOptIn: false,
|
|
145
|
+
hazbaseFormErrors: { email: "", otp: "", liquid: {} },
|
|
146
|
+
hazbasePendingAction: "",
|
|
147
|
+
hazbaseAgentPaymentDefaultsDraft: null,
|
|
148
|
+
agentPaymentDefaultsModalOpen: false,
|
|
149
|
+
toast: null,
|
|
150
150
|
// Sign-in OTP flow is a two-step send→verify dance. Showing both
|
|
151
151
|
// buttons side-by-side confused users; they clicked "Verify" before
|
|
152
152
|
// ever requesting a code. We gate the verify button behind a
|
|
@@ -202,6 +202,7 @@ let timelineLiveRefreshInFlight = false;
|
|
|
202
202
|
let timelineLiveRefreshPending = false;
|
|
203
203
|
let lastTimelineLiveRevision = 0;
|
|
204
204
|
const reportedTimelineRenderTokens = new Set();
|
|
205
|
+
let toastDismissTimer = 0;
|
|
205
206
|
|
|
206
207
|
async function loadHazbasePasskeyModule() {
|
|
207
208
|
if (!hazbasePasskeyModulePromise) {
|
|
@@ -1223,9 +1224,9 @@ async function fetchA2aShareStatus() {
|
|
|
1223
1224
|
}
|
|
1224
1225
|
|
|
1225
1226
|
|
|
1226
|
-
async function fetchHazbaseStatus() {
|
|
1227
|
+
async function fetchHazbaseStatus(opts = {}) {
|
|
1227
1228
|
try {
|
|
1228
|
-
state.hazbaseStatus = await apiGet("/api/hazbase/status");
|
|
1229
|
+
state.hazbaseStatus = await apiGet("/api/hazbase/status", opts);
|
|
1229
1230
|
} catch {
|
|
1230
1231
|
state.hazbaseStatus = null;
|
|
1231
1232
|
}
|
|
@@ -2363,6 +2364,8 @@ async function renderShell() {
|
|
|
2363
2364
|
${renderInstallGuideModal()}
|
|
2364
2365
|
${renderLogoutConfirmModal()}
|
|
2365
2366
|
${renderHazbaseLogoutConfirmModal()}
|
|
2367
|
+
${renderAgentPaymentDefaultsModal()}
|
|
2368
|
+
${renderToastLayer()}
|
|
2366
2369
|
</div>
|
|
2367
2370
|
${!desktop && detail ? renderCompletionReplySheet(detail) : ""}
|
|
2368
2371
|
`;
|
|
@@ -2919,6 +2922,37 @@ function renderMobileTopBar(detail) {
|
|
|
2919
2922
|
`;
|
|
2920
2923
|
}
|
|
2921
2924
|
|
|
2925
|
+
function showToast(message, { tone = "success" } = {}) {
|
|
2926
|
+
const text = String(message || "").trim();
|
|
2927
|
+
if (!text) return;
|
|
2928
|
+
const normalizedTone = tone === "error" ? "error" : "success";
|
|
2929
|
+
const id = `${Date.now()}:${Math.random().toString(36).slice(2)}`;
|
|
2930
|
+
state.toast = { id, message: text, tone: normalizedTone };
|
|
2931
|
+
if (toastDismissTimer) {
|
|
2932
|
+
clearTimeout(toastDismissTimer);
|
|
2933
|
+
}
|
|
2934
|
+
toastDismissTimer = setTimeout(() => {
|
|
2935
|
+
if (state.toast?.id !== id) {
|
|
2936
|
+
return;
|
|
2937
|
+
}
|
|
2938
|
+
state.toast = null;
|
|
2939
|
+
renderCurrentSurface();
|
|
2940
|
+
}, normalizedTone === "error" ? 5600 : 3800);
|
|
2941
|
+
}
|
|
2942
|
+
|
|
2943
|
+
function renderToastLayer() {
|
|
2944
|
+
if (!state.toast) {
|
|
2945
|
+
return "";
|
|
2946
|
+
}
|
|
2947
|
+
const tone = state.toast.tone === "error" ? "error" : "success";
|
|
2948
|
+
return `
|
|
2949
|
+
<div class="app-toast app-toast--${escapeHtml(tone)}" role="${tone === "error" ? "alert" : "status"}" aria-live="${tone === "error" ? "assertive" : "polite"}">
|
|
2950
|
+
<span class="app-toast__icon" aria-hidden="true">${renderIcon(tone === "success" ? "completed" : "lock")}</span>
|
|
2951
|
+
<span class="app-toast__message">${escapeHtml(state.toast.message)}</span>
|
|
2952
|
+
</div>
|
|
2953
|
+
`;
|
|
2954
|
+
}
|
|
2955
|
+
|
|
2922
2956
|
function renderableCurrentDetail(itemRef = state.currentItem) {
|
|
2923
2957
|
if (!itemRef) {
|
|
2924
2958
|
return null;
|
|
@@ -4764,7 +4798,7 @@ function settingsWalletRootValue(context) {
|
|
|
4764
4798
|
if (context.hazbase?.enabled !== true) {
|
|
4765
4799
|
return L("common.disabled");
|
|
4766
4800
|
}
|
|
4767
|
-
return
|
|
4801
|
+
return effectiveAgentPaymentDefaults(context.hazbase).length > 0
|
|
4768
4802
|
? L("common.enabled")
|
|
4769
4803
|
: settingsNeedsActionValue();
|
|
4770
4804
|
}
|
|
@@ -4925,6 +4959,34 @@ function settingsPageMeta(page) {
|
|
|
4925
4959
|
description: L("settings.wallet.copy"),
|
|
4926
4960
|
icon: "coin",
|
|
4927
4961
|
};
|
|
4962
|
+
case "walletInventory":
|
|
4963
|
+
return {
|
|
4964
|
+
id: "walletInventory",
|
|
4965
|
+
title: L("settings.wallet.inventory.title"),
|
|
4966
|
+
description: L("settings.wallet.inventory.copy"),
|
|
4967
|
+
icon: "coin",
|
|
4968
|
+
};
|
|
4969
|
+
case "walletInventoryBase":
|
|
4970
|
+
return {
|
|
4971
|
+
id: "walletInventoryBase",
|
|
4972
|
+
title: L("settings.wallet.chain.base.title"),
|
|
4973
|
+
description: L("settings.wallet.chain.base.copy"),
|
|
4974
|
+
icon: "coin",
|
|
4975
|
+
};
|
|
4976
|
+
case "walletInventoryLiquid":
|
|
4977
|
+
return {
|
|
4978
|
+
id: "walletInventoryLiquid",
|
|
4979
|
+
title: L("settings.wallet.chain.liquid.title"),
|
|
4980
|
+
description: L("settings.wallet.chain.liquid.copy"),
|
|
4981
|
+
icon: "coin",
|
|
4982
|
+
};
|
|
4983
|
+
case "walletInventoryPolygon":
|
|
4984
|
+
return {
|
|
4985
|
+
id: "walletInventoryPolygon",
|
|
4986
|
+
title: L("settings.wallet.chain.polygon.title"),
|
|
4987
|
+
description: L("settings.wallet.chain.polygon.copy"),
|
|
4988
|
+
icon: "coin",
|
|
4989
|
+
};
|
|
4928
4990
|
case "remotePairing":
|
|
4929
4991
|
return {
|
|
4930
4992
|
id: "remotePairing",
|
|
@@ -5102,6 +5164,18 @@ function renderSettingsSubpage(context, { mobile }) {
|
|
|
5102
5164
|
case "wallet":
|
|
5103
5165
|
content = renderSettingsWalletPage(context);
|
|
5104
5166
|
break;
|
|
5167
|
+
case "walletInventory":
|
|
5168
|
+
content = renderSettingsWalletInventoryPage(context);
|
|
5169
|
+
break;
|
|
5170
|
+
case "walletInventoryBase":
|
|
5171
|
+
content = renderSettingsWalletChainPage(context, "base");
|
|
5172
|
+
break;
|
|
5173
|
+
case "walletInventoryLiquid":
|
|
5174
|
+
content = renderSettingsWalletChainPage(context, "liquid");
|
|
5175
|
+
break;
|
|
5176
|
+
case "walletInventoryPolygon":
|
|
5177
|
+
content = renderSettingsWalletChainPage(context, "polygon");
|
|
5178
|
+
break;
|
|
5105
5179
|
case "remotePairing":
|
|
5106
5180
|
content = renderSettingsRemotePairingPage(context);
|
|
5107
5181
|
break;
|
|
@@ -6055,13 +6129,19 @@ function renderSettingsA2aSharePage(context) {
|
|
|
6055
6129
|
const lock = item.hasPassword
|
|
6056
6130
|
? `<span class="settings-compose-badge settings-compose-badge--reply" role="img" title="${escapeHtml(passwordLabel)}" aria-label="${escapeHtml(passwordLabel)}">${renderIcon("lock")}</span>`
|
|
6057
6131
|
: "";
|
|
6058
|
-
// Paid-share badge. `price` is atomic
|
|
6132
|
+
// Paid-share badge. `price` is atomic units for the primary payment option.
|
|
6059
6133
|
// Mutually exclusive with hasPassword at upload time, so the two badges
|
|
6060
6134
|
// never both render, but the HTML doesn't assume that — it just renders
|
|
6061
6135
|
// whichever are set.
|
|
6136
|
+
const primaryPaymentOption = Array.isArray(item.paymentOptions) && item.paymentOptions.length > 0
|
|
6137
|
+
? item.paymentOptions[0]
|
|
6138
|
+
: null;
|
|
6139
|
+
const paidAsset = paymentAssetLabel(primaryPaymentOption?.asset || item.asset || "usdc");
|
|
6140
|
+
const paidDecimals = Number(primaryPaymentOption?.decimals || paymentAssetDecimals(primaryPaymentOption?.asset || item.asset || "usdc"));
|
|
6062
6141
|
const paidLabel = item.price
|
|
6063
6142
|
? L("settings.a2aShare.paidShare", {
|
|
6064
|
-
price:
|
|
6143
|
+
price: formatAtomicAmount(item.price, paidDecimals),
|
|
6144
|
+
asset: paidAsset,
|
|
6065
6145
|
network: item.network || "?",
|
|
6066
6146
|
})
|
|
6067
6147
|
: "";
|
|
@@ -6388,51 +6468,26 @@ function renderSettingsWalletPage(context) {
|
|
|
6388
6468
|
</div>
|
|
6389
6469
|
`;
|
|
6390
6470
|
}
|
|
6391
|
-
const
|
|
6392
|
-
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
|
|
6399
|
-
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
state.hazbaseNotice
|
|
6411
|
-
? `<div class="settings-copy-block settings-copy-block--compact wallet-flow-message wallet-flow-message--notice"><p>${escapeHtml(state.hazbaseNotice)}</p></div>`
|
|
6412
|
-
: "",
|
|
6413
|
-
state.hazbaseError
|
|
6414
|
-
? `<div class="settings-copy-block settings-copy-block--compact wallet-flow-message wallet-flow-message--error"><p>${escapeHtml(state.hazbaseError)}</p></div>`
|
|
6415
|
-
: "",
|
|
6416
|
-
renderHazbaseWalletStepList(flow),
|
|
6417
|
-
].filter(Boolean);
|
|
6418
|
-
const canRefreshSession = Boolean(hazbase.sessionInvalid);
|
|
6419
|
-
const advancedActions = canRefreshSession || hazbase.signedIn
|
|
6420
|
-
? [
|
|
6421
|
-
canRefreshSession
|
|
6422
|
-
? `<button class="secondary secondary--wide" type="button" data-hazbase-action="refresh-session">${escapeHtml(L("settings.hazbase.action.refreshSession"))}</button>`
|
|
6423
|
-
: "",
|
|
6424
|
-
hazbase.signedIn
|
|
6425
|
-
? `<button class="secondary secondary--wide" type="button" data-hazbase-action="logout">${escapeHtml(L("settings.hazbase.action.signOut"))}</button>`
|
|
6426
|
-
: "",
|
|
6427
|
-
].filter(Boolean).join("")
|
|
6471
|
+
const configured = configuredPaymentCapabilities(hazbase);
|
|
6472
|
+
const configuredNetworkCount = configuredPaymentCapabilityNetworkCount(hazbase);
|
|
6473
|
+
const agentConfigured = agentEligiblePaymentCapabilities(hazbase);
|
|
6474
|
+
const effectiveDefaults = effectiveAgentPaymentDefaults(hazbase);
|
|
6475
|
+
const defaultsSummary = renderAgentPaymentDefaultsSummary(hazbase, effectiveDefaults);
|
|
6476
|
+
const agentEmptyKey = configured.length ? "settings.wallet.agent.emptyAvailable" : "settings.wallet.agent.empty";
|
|
6477
|
+
const defaultsRows = agentConfigured.length
|
|
6478
|
+
? agentConfigured.map((capability) => renderAgentPaymentDefaultChoice(hazbase, capability))
|
|
6479
|
+
: [`<div class="settings-copy-block settings-copy-block--compact"><p class="muted">${escapeHtml(L(agentEmptyKey))}</p></div>`];
|
|
6480
|
+
const defaultsDraftNotice = state.hazbaseAgentPaymentDefaultsDraft
|
|
6481
|
+
? `<div class="settings-copy-block settings-copy-block--compact wallet-agent-draft-notice"><p>${escapeHtml(L("settings.wallet.agent.unsaved"))}</p></div>`
|
|
6482
|
+
: "";
|
|
6483
|
+
const defaultsActions = agentConfigured.length
|
|
6484
|
+
? `
|
|
6485
|
+
<div class="wallet-agent-actions">
|
|
6486
|
+
<button class="primary primary--wide${state.hazbasePendingAction === "agent-defaults-save" ? " is-loading" : ""}" type="button" data-hazbase-agent-defaults-save ${state.hazbasePendingAction ? "disabled" : ""} ${state.hazbasePendingAction === "agent-defaults-save" ? 'aria-busy="true"' : ""}>${escapeHtml(L("settings.wallet.agent.save"))}</button>
|
|
6487
|
+
<button class="secondary secondary--wide${state.hazbasePendingAction === "agent-defaults-all" ? " is-loading" : ""}" type="button" data-hazbase-agent-defaults-all ${state.hazbasePendingAction ? "disabled" : ""} ${state.hazbasePendingAction === "agent-defaults-all" ? 'aria-busy="true"' : ""}>${escapeHtml(L("settings.wallet.agent.useAllConfigured"))}</button>
|
|
6488
|
+
</div>
|
|
6489
|
+
`
|
|
6428
6490
|
: "";
|
|
6429
|
-
// Render the wallet flow without `renderSettingsGroup`'s `.settings-list`
|
|
6430
|
-
// wrapper. The banner (`.settings-copy-block`), notice/error blocks, and
|
|
6431
|
-
// each step card (`.wallet-step-card`) already have their own rounded
|
|
6432
|
-
// frame — wrapping them in another `.settings-list` produced a visible
|
|
6433
|
-
// "box inside a box" nest. The title (`.settings-group__title`) still
|
|
6434
|
-
// sits above a flat stack of sibling cards via `.wallet-setup-stack`.
|
|
6435
|
-
const flowTitle = L("settings.wallet.flow.title");
|
|
6436
6491
|
// Brand attribution. The wallet stack (factory + validator + bundler +
|
|
6437
6492
|
// paymaster) is provided by hazBase; the link points to their LP so
|
|
6438
6493
|
// curious users can discover what's running the signing path.
|
|
@@ -6443,82 +6498,583 @@ function renderSettingsWalletPage(context) {
|
|
|
6443
6498
|
`;
|
|
6444
6499
|
return `
|
|
6445
6500
|
<div class="settings-page">
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6501
|
+
${renderHazbaseWalletMessages()}
|
|
6502
|
+
${renderHazbaseWalletBetaNotice()}
|
|
6503
|
+
${renderSettingsGroup(L("settings.wallet.summary.title"), [
|
|
6504
|
+
renderSettingsInfoRow(L("settings.row.hazbaseStatus"), hazbase.signedIn ? L("settings.hazbase.status.signedIn") : L("settings.hazbase.status.signedOut"), {
|
|
6505
|
+
valueTone: hazbase.signedIn ? "enabled" : "attention",
|
|
6506
|
+
}),
|
|
6507
|
+
renderSettingsInfoRow(L("settings.wallet.agent.currentDefaults"), defaultsSummary.value, {
|
|
6508
|
+
rawValue: defaultsSummary.rawValue,
|
|
6509
|
+
stacked: defaultsSummary.stacked,
|
|
6510
|
+
valueClassName: defaultsSummary.valueClassName,
|
|
6511
|
+
valueTone: defaultsSummary.valueTone,
|
|
6512
|
+
}),
|
|
6513
|
+
], { listClassName: "settings-list settings-list--compact" })}
|
|
6514
|
+
${renderSettingsGroup(L("settings.wallet.agent.title"), [
|
|
6515
|
+
`<div class="settings-copy-block settings-copy-block--compact"><p class="muted">${escapeHtml(L("settings.wallet.agent.copy"))}</p></div>`,
|
|
6516
|
+
...defaultsRows,
|
|
6517
|
+
defaultsDraftNotice,
|
|
6518
|
+
defaultsActions,
|
|
6519
|
+
], { listClassName: "settings-list settings-list--compact" })}
|
|
6520
|
+
${renderSettingsGroup(L("settings.wallet.inventory.navGroup"), [
|
|
6521
|
+
renderSettingsNavRow({
|
|
6522
|
+
page: "walletInventory",
|
|
6523
|
+
icon: "coin",
|
|
6524
|
+
title: L("settings.wallet.inventory.navTitle"),
|
|
6525
|
+
subtitle: L("settings.wallet.inventory.navCopy"),
|
|
6526
|
+
value: L("settings.wallet.inventory.navValue", { count: configuredNetworkCount }),
|
|
6527
|
+
valueTone: configuredNetworkCount ? "enabled" : "attention",
|
|
6528
|
+
}),
|
|
6529
|
+
], { listClassName: "settings-list settings-list--compact" })}
|
|
6453
6530
|
${poweredBy}
|
|
6454
6531
|
</div>
|
|
6455
6532
|
`;
|
|
6456
6533
|
}
|
|
6457
6534
|
|
|
6458
|
-
function
|
|
6459
|
-
const
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6535
|
+
function renderHazbaseAccountActions(hazbase) {
|
|
6536
|
+
const canRefreshSession = Boolean(hazbase?.sessionInvalid);
|
|
6537
|
+
const actions = canRefreshSession || hazbase?.signedIn
|
|
6538
|
+
? [
|
|
6539
|
+
canRefreshSession
|
|
6540
|
+
? `<button class="secondary secondary--wide${isHazbaseActionPending("refresh-session") ? " is-loading" : ""}" type="button" data-hazbase-action="refresh-session" ${state.hazbasePendingAction ? "disabled" : ""} ${isHazbaseActionPending("refresh-session") ? 'aria-busy="true"' : ""}>${escapeHtml(L("settings.hazbase.action.refreshSession"))}</button>`
|
|
6541
|
+
: "",
|
|
6542
|
+
hazbase?.signedIn
|
|
6543
|
+
? `<button class="secondary secondary--wide" type="button" data-hazbase-action="logout" ${state.hazbasePendingAction ? "disabled" : ""}>${escapeHtml(L("settings.hazbase.action.signOut"))}</button>`
|
|
6544
|
+
: "",
|
|
6545
|
+
].filter(Boolean).join("")
|
|
6546
|
+
: "";
|
|
6547
|
+
return actions ? renderSettingsActionPanel(actions, L("settings.wallet.advanced.title")) : "";
|
|
6548
|
+
}
|
|
6549
|
+
|
|
6550
|
+
function renderSettingsWalletInventoryPage(context) {
|
|
6551
|
+
const hazbase = context.hazbase || { enabled: false };
|
|
6552
|
+
if (!hazbase?.enabled) {
|
|
6553
|
+
return `
|
|
6554
|
+
<div class="settings-page">
|
|
6555
|
+
<p class="settings-page-copy muted">${escapeHtml(L("settings.wallet.unavailable"))}</p>
|
|
6556
|
+
</div>
|
|
6557
|
+
`;
|
|
6558
|
+
}
|
|
6559
|
+
const flow = deriveHazbaseWalletFlow(hazbase);
|
|
6560
|
+
const authCards = flow.steps
|
|
6561
|
+
.filter((step) => step.kind === "auth")
|
|
6562
|
+
.filter((step) => step.status !== "locked")
|
|
6563
|
+
.map((step) => renderHazbaseWalletStepCard(step, { mode: step.status === "complete" ? "compact" : "full" }));
|
|
6564
|
+
const chainRows = Object.entries(paymentCapabilityChainDefinitions()).map(([chain, definition]) => renderSettingsNavRow({
|
|
6565
|
+
page: definition.page,
|
|
6566
|
+
icon: "coin",
|
|
6567
|
+
title: L(definition.titleKey),
|
|
6568
|
+
subtitle: L(definition.copyKey),
|
|
6569
|
+
value: paymentCapabilityChainValue(hazbase, chain),
|
|
6570
|
+
valueTone: configuredPaymentCapabilityNetworkCount(hazbase, definition.networks) ? "enabled" : "attention",
|
|
6571
|
+
}));
|
|
6572
|
+
return `
|
|
6573
|
+
<div class="settings-page">
|
|
6574
|
+
${renderHazbaseWalletMessages()}
|
|
6575
|
+
${authCards.length ? renderSettingsGroup(L("settings.wallet.inventory.accountTitle"), [
|
|
6576
|
+
`<div class="wallet-setup-stack">${authCards.join("")}</div>`,
|
|
6577
|
+
], { listClassName: "settings-list settings-list--flat" }) : ""}
|
|
6578
|
+
${renderHazbaseAccountActions(hazbase)}
|
|
6579
|
+
${renderSettingsGroup(L("settings.wallet.inventory.issueTitle"), chainRows, { listClassName: "settings-list settings-list--compact" })}
|
|
6580
|
+
</div>
|
|
6581
|
+
`;
|
|
6582
|
+
}
|
|
6583
|
+
|
|
6584
|
+
function renderSettingsWalletChainPage(context, chain) {
|
|
6585
|
+
const hazbase = context.hazbase || { enabled: false };
|
|
6586
|
+
if (!hazbase?.enabled) {
|
|
6587
|
+
return `
|
|
6588
|
+
<div class="settings-page">
|
|
6589
|
+
<p class="settings-page-copy muted">${escapeHtml(L("settings.wallet.unavailable"))}</p>
|
|
6590
|
+
</div>
|
|
6591
|
+
`;
|
|
6592
|
+
}
|
|
6593
|
+
const definition = paymentCapabilityChainDefinition(chain);
|
|
6594
|
+
const networks = definition?.networks || [];
|
|
6595
|
+
const testnetCards = networks
|
|
6596
|
+
.filter((network) => paymentCapabilityDefinition(network)?.environment === "testnet")
|
|
6597
|
+
.map((network) => renderWalletInventoryCapabilityCard(hazbase, network));
|
|
6598
|
+
const mainnetCards = networks
|
|
6599
|
+
.filter((network) => paymentCapabilityDefinition(network)?.environment === "mainnet")
|
|
6600
|
+
.map((network) => renderWalletInventoryCapabilityCard(hazbase, network));
|
|
6601
|
+
return `
|
|
6602
|
+
<div class="settings-page">
|
|
6603
|
+
${renderHazbaseWalletMessages()}
|
|
6604
|
+
${testnetCards.length ? renderSettingsGroup(L("settings.wallet.inventory.testnetTitle"), [
|
|
6605
|
+
`<div class="wallet-setup-stack">${testnetCards.join("")}</div>`,
|
|
6606
|
+
], { listClassName: "settings-list settings-list--flat" }) : ""}
|
|
6607
|
+
${mainnetCards.length ? renderSettingsGroup(L("settings.wallet.inventory.mainnetTitle"), [
|
|
6608
|
+
`<div class="wallet-setup-stack">${mainnetCards.join("")}</div>`,
|
|
6609
|
+
], { listClassName: "settings-list settings-list--flat" }) : ""}
|
|
6610
|
+
</div>
|
|
6611
|
+
`;
|
|
6612
|
+
}
|
|
6613
|
+
|
|
6614
|
+
function renderHazbaseWalletMessages() {
|
|
6615
|
+
return "";
|
|
6616
|
+
}
|
|
6617
|
+
|
|
6618
|
+
function resetHazbaseFormErrors() {
|
|
6619
|
+
state.hazbaseFormErrors = { email: "", otp: "", liquid: {} };
|
|
6620
|
+
}
|
|
6621
|
+
|
|
6622
|
+
function setHazbaseFormError(field, message, network = "") {
|
|
6623
|
+
const formErrors = state.hazbaseFormErrors || { email: "", otp: "", liquid: {} };
|
|
6624
|
+
if (field === "email") {
|
|
6625
|
+
state.hazbaseFormErrors = { ...formErrors, email: message };
|
|
6626
|
+
return;
|
|
6627
|
+
}
|
|
6628
|
+
if (field === "otp") {
|
|
6629
|
+
state.hazbaseFormErrors = { ...formErrors, otp: message };
|
|
6630
|
+
return;
|
|
6631
|
+
}
|
|
6632
|
+
if (field === "liquid") {
|
|
6633
|
+
state.hazbaseFormErrors = {
|
|
6634
|
+
...formErrors,
|
|
6635
|
+
liquid: {
|
|
6636
|
+
...(formErrors.liquid || {}),
|
|
6637
|
+
[network]: message,
|
|
6638
|
+
},
|
|
6639
|
+
};
|
|
6640
|
+
}
|
|
6641
|
+
}
|
|
6642
|
+
|
|
6643
|
+
function clearHazbaseFormError(field, network = "") {
|
|
6644
|
+
setHazbaseFormError(field, "", network);
|
|
6645
|
+
}
|
|
6646
|
+
|
|
6647
|
+
function clearHazbaseFormErrorsForAction(action, network = "") {
|
|
6648
|
+
if (action === "request-otp") {
|
|
6649
|
+
clearHazbaseFormError("email");
|
|
6650
|
+
} else if (action === "verify-otp") {
|
|
6651
|
+
clearHazbaseFormError("email");
|
|
6652
|
+
clearHazbaseFormError("otp");
|
|
6653
|
+
} else if (action === "save-liquid-capability") {
|
|
6654
|
+
clearHazbaseFormError("liquid", network);
|
|
6655
|
+
}
|
|
6656
|
+
}
|
|
6657
|
+
|
|
6658
|
+
function hazbaseFormError(field, network = "") {
|
|
6659
|
+
const formErrors = state.hazbaseFormErrors || {};
|
|
6660
|
+
if (field === "liquid") {
|
|
6661
|
+
return formErrors.liquid?.[network] || "";
|
|
6662
|
+
}
|
|
6663
|
+
return formErrors[field] || "";
|
|
6664
|
+
}
|
|
6665
|
+
|
|
6666
|
+
function applyHazbaseInlineError(action, error, network = "") {
|
|
6667
|
+
const key = error?.errorKey || "";
|
|
6668
|
+
const message = error?.message || String(error);
|
|
6669
|
+
if (action === "request-otp" && (key === "email-required" || message === L("error.hazbaseEmailRequired"))) {
|
|
6670
|
+
setHazbaseFormError("email", L("error.hazbaseEmailRequired"));
|
|
6671
|
+
return true;
|
|
6672
|
+
}
|
|
6673
|
+
if (action === "verify-otp") {
|
|
6674
|
+
if (key === "email-required" || message === L("error.hazbaseEmailRequired")) {
|
|
6675
|
+
setHazbaseFormError("email", L("error.hazbaseEmailRequired"));
|
|
6676
|
+
return true;
|
|
6677
|
+
}
|
|
6678
|
+
if (key === "otp-required" || message === L("error.hazbaseOtpRequired")) {
|
|
6679
|
+
setHazbaseFormError("otp", L("error.hazbaseOtpRequired"));
|
|
6680
|
+
return true;
|
|
6681
|
+
}
|
|
6682
|
+
}
|
|
6683
|
+
if (action === "save-liquid-capability") {
|
|
6684
|
+
if (key === "invalid-liquid-address") {
|
|
6685
|
+
setHazbaseFormError("liquid", L("error.hazbaseLiquidAddressInvalid"), network);
|
|
6686
|
+
return true;
|
|
6466
6687
|
}
|
|
6688
|
+
if (message === L("error.hazbaseLiquidAddressRequired")) {
|
|
6689
|
+
setHazbaseFormError("liquid", L("error.hazbaseLiquidAddressRequired"), network);
|
|
6690
|
+
return true;
|
|
6691
|
+
}
|
|
6692
|
+
}
|
|
6693
|
+
return false;
|
|
6694
|
+
}
|
|
6467
6695
|
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6696
|
+
function paymentCapabilityDefinitions() {
|
|
6697
|
+
return {
|
|
6698
|
+
"base-sepolia": {
|
|
6699
|
+
environment: "testnet",
|
|
6700
|
+
family: "evm",
|
|
6701
|
+
asset: "usdc",
|
|
6702
|
+
titleKey: "settings.wallet.step.baseSepolia.title",
|
|
6703
|
+
copyKey: "settings.wallet.step.baseSepolia.copy",
|
|
6704
|
+
action: "bootstrap-base-sepolia",
|
|
6705
|
+
},
|
|
6706
|
+
base: {
|
|
6707
|
+
environment: "mainnet",
|
|
6708
|
+
family: "evm",
|
|
6709
|
+
asset: "usdc",
|
|
6710
|
+
titleKey: "settings.wallet.step.base.title",
|
|
6711
|
+
copyKey: "settings.wallet.step.base.copy",
|
|
6712
|
+
action: "bootstrap-base",
|
|
6713
|
+
releaseStatus: "comingSoon",
|
|
6714
|
+
},
|
|
6715
|
+
"polygon-amoy": {
|
|
6716
|
+
environment: "testnet",
|
|
6717
|
+
family: "evm",
|
|
6718
|
+
asset: "usdc",
|
|
6719
|
+
titleKey: "settings.wallet.step.polygonAmoy.title",
|
|
6720
|
+
copyKey: "settings.wallet.step.polygonAmoy.copy",
|
|
6721
|
+
action: "bootstrap-polygon-amoy",
|
|
6722
|
+
},
|
|
6723
|
+
polygon: {
|
|
6724
|
+
environment: "mainnet",
|
|
6725
|
+
family: "evm",
|
|
6726
|
+
asset: "usdc",
|
|
6727
|
+
titleKey: "settings.wallet.step.polygon.title",
|
|
6728
|
+
copyKey: "settings.wallet.step.polygon.copy",
|
|
6729
|
+
action: "bootstrap-polygon",
|
|
6730
|
+
releaseStatus: "comingSoon",
|
|
6731
|
+
},
|
|
6732
|
+
liquidtestnet: {
|
|
6733
|
+
environment: "testnet",
|
|
6734
|
+
family: "liquid",
|
|
6735
|
+
asset: "usdt",
|
|
6736
|
+
titleKey: "settings.wallet.step.liquidTestnet.title",
|
|
6737
|
+
copyKey: "settings.wallet.step.liquidTestnet.copy",
|
|
6738
|
+
},
|
|
6739
|
+
liquidv1: {
|
|
6740
|
+
environment: "mainnet",
|
|
6741
|
+
family: "liquid",
|
|
6742
|
+
asset: "usdt",
|
|
6743
|
+
titleKey: "settings.wallet.step.liquid.title",
|
|
6744
|
+
copyKey: "settings.wallet.step.liquid.copy",
|
|
6745
|
+
releaseStatus: "comingSoon",
|
|
6746
|
+
},
|
|
6747
|
+
};
|
|
6748
|
+
}
|
|
6749
|
+
|
|
6750
|
+
function paymentCapabilityChainDefinitions() {
|
|
6751
|
+
return {
|
|
6752
|
+
base: {
|
|
6753
|
+
page: "walletInventoryBase",
|
|
6754
|
+
titleKey: "settings.wallet.chain.base.title",
|
|
6755
|
+
copyKey: "settings.wallet.chain.base.copy",
|
|
6756
|
+
networks: ["base-sepolia", "base"],
|
|
6757
|
+
},
|
|
6758
|
+
liquid: {
|
|
6759
|
+
page: "walletInventoryLiquid",
|
|
6760
|
+
titleKey: "settings.wallet.chain.liquid.title",
|
|
6761
|
+
copyKey: "settings.wallet.chain.liquid.copy",
|
|
6762
|
+
networks: ["liquidtestnet", "liquidv1"],
|
|
6763
|
+
},
|
|
6764
|
+
polygon: {
|
|
6765
|
+
page: "walletInventoryPolygon",
|
|
6766
|
+
titleKey: "settings.wallet.chain.polygon.title",
|
|
6767
|
+
copyKey: "settings.wallet.chain.polygon.copy",
|
|
6768
|
+
networks: ["polygon-amoy", "polygon"],
|
|
6769
|
+
},
|
|
6770
|
+
};
|
|
6771
|
+
}
|
|
6772
|
+
|
|
6773
|
+
function paymentCapabilityChainDefinition(chain) {
|
|
6774
|
+
return paymentCapabilityChainDefinitions()[chain] || null;
|
|
6775
|
+
}
|
|
6776
|
+
|
|
6777
|
+
function paymentCapabilityDefinition(network) {
|
|
6778
|
+
return paymentCapabilityDefinitions()[network] || null;
|
|
6779
|
+
}
|
|
6780
|
+
|
|
6781
|
+
function allPaymentCapabilities(hazbase) {
|
|
6782
|
+
return Array.isArray(hazbase?.paymentCapabilities) ? hazbase.paymentCapabilities : [];
|
|
6783
|
+
}
|
|
6784
|
+
|
|
6785
|
+
function isPaymentCapabilityAvailable(network) {
|
|
6786
|
+
return paymentCapabilityDefinition(network)?.releaseStatus !== "comingSoon";
|
|
6787
|
+
}
|
|
6788
|
+
|
|
6789
|
+
function availablePaymentCapabilityNetworks(networks) {
|
|
6790
|
+
return (Array.isArray(networks) ? networks : []).filter((network) => isPaymentCapabilityAvailable(network));
|
|
6791
|
+
}
|
|
6792
|
+
|
|
6793
|
+
function configuredPaymentCapabilities(hazbase) {
|
|
6794
|
+
return allPaymentCapabilities(hazbase).filter((entry) => entry?.configured && entry?.enabled !== false && (entry.payTo || entry.payoutAddress));
|
|
6795
|
+
}
|
|
6796
|
+
|
|
6797
|
+
function agentEligiblePaymentCapabilities(hazbase) {
|
|
6798
|
+
return configuredPaymentCapabilities(hazbase).filter((entry) => isPaymentCapabilityAvailable(entry.network));
|
|
6799
|
+
}
|
|
6800
|
+
|
|
6801
|
+
function configuredPaymentCapabilityNetworkCount(hazbase, networks) {
|
|
6802
|
+
const shouldCountAllNetworks = !Array.isArray(networks);
|
|
6803
|
+
const networkSet = new Set(shouldCountAllNetworks ? [] : networks);
|
|
6804
|
+
const configuredNetworks = new Set();
|
|
6805
|
+
for (const entry of configuredPaymentCapabilities(hazbase)) {
|
|
6806
|
+
if (shouldCountAllNetworks || networkSet.has(entry?.network)) {
|
|
6807
|
+
configuredNetworks.add(entry.network);
|
|
6473
6808
|
}
|
|
6809
|
+
}
|
|
6810
|
+
return configuredNetworks.size;
|
|
6811
|
+
}
|
|
6812
|
+
|
|
6813
|
+
function paymentCapabilityChainValue(hazbase, chain) {
|
|
6814
|
+
const definition = paymentCapabilityChainDefinition(chain);
|
|
6815
|
+
const networks = definition?.networks || [];
|
|
6816
|
+
const availableNetworks = availablePaymentCapabilityNetworks(networks);
|
|
6817
|
+
const countedNetworks = availableNetworks.length ? availableNetworks : networks;
|
|
6818
|
+
return L("settings.wallet.chain.configuredValue", {
|
|
6819
|
+
count: configuredPaymentCapabilityNetworkCount(hazbase, countedNetworks),
|
|
6820
|
+
total: countedNetworks.length,
|
|
6821
|
+
});
|
|
6822
|
+
}
|
|
6823
|
+
|
|
6824
|
+
function resolveCapability(hazbase, network, asset = "") {
|
|
6825
|
+
const normalizedAsset = String(asset || paymentCapabilityDefinition(network)?.asset || "").toLowerCase();
|
|
6826
|
+
return allPaymentCapabilities(hazbase).find((entry) => (
|
|
6827
|
+
entry?.network === network &&
|
|
6828
|
+
String(entry?.asset || paymentCapabilityDefinition(network)?.asset || "").toLowerCase() === normalizedAsset
|
|
6829
|
+
)) || null;
|
|
6830
|
+
}
|
|
6831
|
+
|
|
6832
|
+
function agentPaymentDefaultRef(value) {
|
|
6833
|
+
const network = String(value?.network || "").trim();
|
|
6834
|
+
if (!network) return null;
|
|
6835
|
+
const asset = String(value?.asset || paymentCapabilityDefinition(network)?.asset || "").trim().toLowerCase();
|
|
6836
|
+
if (!asset) return null;
|
|
6837
|
+
return { network, asset };
|
|
6838
|
+
}
|
|
6839
|
+
|
|
6840
|
+
function agentPaymentDefaultKey(value) {
|
|
6841
|
+
const ref = agentPaymentDefaultRef(value);
|
|
6842
|
+
return ref ? `${ref.network}:${ref.asset}` : "";
|
|
6843
|
+
}
|
|
6844
|
+
|
|
6845
|
+
function normalizeAgentPaymentDefaultRefs(values) {
|
|
6846
|
+
const accepts = [];
|
|
6847
|
+
const seen = new Set();
|
|
6848
|
+
for (const value of Array.isArray(values) ? values : []) {
|
|
6849
|
+
const ref = agentPaymentDefaultRef(value);
|
|
6850
|
+
const key = ref ? `${ref.network}:${ref.asset}` : "";
|
|
6851
|
+
if (!ref || !isPaymentCapabilityAvailable(ref.network) || seen.has(key)) continue;
|
|
6852
|
+
seen.add(key);
|
|
6853
|
+
accepts.push(ref);
|
|
6854
|
+
}
|
|
6855
|
+
return accepts;
|
|
6856
|
+
}
|
|
6857
|
+
|
|
6858
|
+
function activeAgentPaymentDefaults(hazbase, { includeDraft = false } = {}) {
|
|
6859
|
+
return includeDraft && state.hazbaseAgentPaymentDefaultsDraft && typeof state.hazbaseAgentPaymentDefaultsDraft === "object"
|
|
6860
|
+
? state.hazbaseAgentPaymentDefaultsDraft
|
|
6861
|
+
: hazbase?.agentPaymentDefaults;
|
|
6862
|
+
}
|
|
6863
|
+
|
|
6864
|
+
function effectiveAgentPaymentDefaults(hazbase, { includeDraft = false } = {}) {
|
|
6865
|
+
const activeDefaults = activeAgentPaymentDefaults(hazbase, { includeDraft });
|
|
6866
|
+
const defaults = activeDefaults && typeof activeDefaults === "object"
|
|
6867
|
+
? activeDefaults
|
|
6868
|
+
: { mode: "configured", effectiveAccepts: [] };
|
|
6869
|
+
if (Array.isArray(defaults.effectiveAccepts)) {
|
|
6870
|
+
return normalizeAgentPaymentDefaultRefs(defaults.effectiveAccepts);
|
|
6871
|
+
}
|
|
6872
|
+
if (String(defaults.mode || "") === "custom") {
|
|
6873
|
+
const configuredKeys = new Set(agentEligiblePaymentCapabilities(hazbase).map(agentPaymentDefaultKey).filter(Boolean));
|
|
6874
|
+
return normalizeAgentPaymentDefaultRefs(defaults.accepts).filter((entry) => configuredKeys.has(agentPaymentDefaultKey(entry)));
|
|
6875
|
+
}
|
|
6876
|
+
return agentEligiblePaymentCapabilities(hazbase).map((entry) => ({
|
|
6877
|
+
network: entry.network,
|
|
6878
|
+
asset: entry.asset || paymentCapabilityDefinition(entry.network)?.asset || "",
|
|
6879
|
+
}));
|
|
6880
|
+
}
|
|
6881
|
+
|
|
6882
|
+
function isAgentPaymentDefaultEnabled(hazbase, capability) {
|
|
6883
|
+
if (!state.hazbaseAgentPaymentDefaultsDraft && typeof capability.agentEnabled === "boolean") {
|
|
6884
|
+
return capability.agentEnabled;
|
|
6885
|
+
}
|
|
6886
|
+
const key = agentPaymentDefaultKey(capability);
|
|
6887
|
+
return effectiveAgentPaymentDefaults(hazbase, { includeDraft: true }).some((entry) => agentPaymentDefaultKey(entry) === key);
|
|
6888
|
+
}
|
|
6889
|
+
|
|
6890
|
+
function paymentCapabilityDisplayLabel(capability) {
|
|
6891
|
+
const def = paymentCapabilityDefinition(capability.network);
|
|
6892
|
+
const networkLabel = capability.label || (def ? L(def.titleKey).replace(/^Issue |^Register /u, "") : capability.network);
|
|
6893
|
+
const asset = String(capability.asset || def?.asset || "").toUpperCase();
|
|
6894
|
+
return asset ? `${networkLabel} ${asset}` : networkLabel;
|
|
6895
|
+
}
|
|
6896
|
+
|
|
6897
|
+
function agentPaymentDefaultDisplayItems(hazbase, defaults) {
|
|
6898
|
+
return defaults.map((entry) => {
|
|
6899
|
+
const capability = resolveCapability(hazbase, entry.network, entry.asset) || entry;
|
|
6900
|
+
return {
|
|
6901
|
+
label: paymentCapabilityDisplayLabel(capability),
|
|
6902
|
+
address: capability.payTo || capability.payoutAddress || capability.smartAccountAddress || "",
|
|
6903
|
+
scheme: capability.scheme || "",
|
|
6904
|
+
network: capability.network || entry.network || "",
|
|
6905
|
+
asset: capability.asset || entry.asset || "",
|
|
6906
|
+
};
|
|
6907
|
+
});
|
|
6908
|
+
}
|
|
6474
6909
|
|
|
6475
|
-
|
|
6476
|
-
|
|
6910
|
+
function renderAgentPaymentDefaultsSummary(hazbase, defaults) {
|
|
6911
|
+
if (!defaults.length) {
|
|
6912
|
+
return {
|
|
6913
|
+
value: L("common.none"),
|
|
6914
|
+
valueTone: "attention",
|
|
6915
|
+
rawValue: false,
|
|
6916
|
+
stacked: false,
|
|
6917
|
+
valueClassName: "",
|
|
6918
|
+
};
|
|
6919
|
+
}
|
|
6920
|
+
const items = agentPaymentDefaultDisplayItems(hazbase, defaults);
|
|
6921
|
+
if (items.length === 1) {
|
|
6922
|
+
return {
|
|
6923
|
+
value: items[0].label,
|
|
6924
|
+
valueTone: "enabled",
|
|
6925
|
+
rawValue: false,
|
|
6926
|
+
stacked: false,
|
|
6927
|
+
valueClassName: "",
|
|
6928
|
+
};
|
|
6477
6929
|
}
|
|
6478
|
-
return
|
|
6930
|
+
return {
|
|
6931
|
+
value: `
|
|
6932
|
+
<button class="wallet-agent-defaults-summary-button" type="button" data-open-agent-payment-defaults aria-label="${escapeHtml(L("settings.wallet.agent.modalTitle"))}">
|
|
6933
|
+
<span class="wallet-agent-default-pill">${escapeHtml(items[0].label)}</span>
|
|
6934
|
+
<span class="wallet-agent-default-more">+${items.length - 1}</span>
|
|
6935
|
+
</button>
|
|
6936
|
+
`,
|
|
6937
|
+
valueTone: "",
|
|
6938
|
+
rawValue: true,
|
|
6939
|
+
stacked: false,
|
|
6940
|
+
valueClassName: "wallet-agent-defaults-summary-wrap",
|
|
6941
|
+
};
|
|
6942
|
+
}
|
|
6943
|
+
|
|
6944
|
+
function shortAddress(value) {
|
|
6945
|
+
const text = String(value || "");
|
|
6946
|
+
if (text.length <= 20) return text;
|
|
6947
|
+
return `${text.slice(0, 10)}...${text.slice(-6)}`;
|
|
6479
6948
|
}
|
|
6480
6949
|
|
|
6481
|
-
function
|
|
6950
|
+
function renderAgentPaymentDefaultChoice(hazbase, capability) {
|
|
6951
|
+
const checked = isAgentPaymentDefaultEnabled(hazbase, capability);
|
|
6952
|
+
const address = capability.payTo || capability.payoutAddress || "";
|
|
6482
6953
|
return `
|
|
6483
|
-
<
|
|
6484
|
-
<span class="
|
|
6485
|
-
<span class="
|
|
6486
|
-
<span class="
|
|
6954
|
+
<label class="settings-choice-row settings-choice-row--checkbox">
|
|
6955
|
+
<span class="settings-row__body">
|
|
6956
|
+
<span class="settings-row__title">${escapeHtml(paymentCapabilityDisplayLabel(capability))}</span>
|
|
6957
|
+
<span class="settings-row__subtitle">${escapeHtml(capability.scheme || "")}${address ? ` · ${escapeHtml(shortAddress(address))}` : ""}</span>
|
|
6487
6958
|
</span>
|
|
6488
|
-
<span class="
|
|
6489
|
-
|
|
6959
|
+
<span class="settings-choice-row__check">
|
|
6960
|
+
<input
|
|
6961
|
+
class="settings-choice-row__input"
|
|
6962
|
+
type="checkbox"
|
|
6963
|
+
data-agent-payment-default
|
|
6964
|
+
data-payment-network="${escapeHtml(capability.network)}"
|
|
6965
|
+
data-payment-asset="${escapeHtml(capability.asset || paymentCapabilityDefinition(capability.network)?.asset || "")}"
|
|
6966
|
+
${checked ? "checked" : ""}
|
|
6967
|
+
/>
|
|
6968
|
+
</span>
|
|
6969
|
+
</label>
|
|
6490
6970
|
`;
|
|
6491
6971
|
}
|
|
6492
6972
|
|
|
6973
|
+
function hazbaseActionPendingKey(action, network = "") {
|
|
6974
|
+
return action === "save-liquid-capability" ? `${action}:${network || ""}` : action;
|
|
6975
|
+
}
|
|
6976
|
+
|
|
6977
|
+
function isHazbaseActionPending(action, network = "") {
|
|
6978
|
+
return state.hazbasePendingAction === hazbaseActionPendingKey(action, network);
|
|
6979
|
+
}
|
|
6980
|
+
|
|
6981
|
+
function renderWalletInventoryCapabilityCard(hazbase, network) {
|
|
6982
|
+
const def = paymentCapabilityDefinition(network);
|
|
6983
|
+
const available = isPaymentCapabilityAvailable(network);
|
|
6984
|
+
const capability = resolveCapability(hazbase, network, def?.asset) || {
|
|
6985
|
+
network,
|
|
6986
|
+
asset: def?.asset || "",
|
|
6987
|
+
family: def?.family || "",
|
|
6988
|
+
configured: false,
|
|
6989
|
+
};
|
|
6990
|
+
const sessionInvalid = Boolean(hazbase.sessionInvalid);
|
|
6991
|
+
const signedIn = Boolean(hazbase.signedIn) && !sessionInvalid;
|
|
6992
|
+
const hasPasskey = Boolean(hazbase.credentialId || hazbase.deviceBindingId);
|
|
6993
|
+
const configured = Boolean(capability.payTo || capability.payoutAddress || capability.smartAccountAddress);
|
|
6994
|
+
const needsPasskey = def?.family === "evm";
|
|
6995
|
+
const canConfigure = signedIn && (!needsPasskey || hasPasskey);
|
|
6996
|
+
const actionBlocked = Boolean(state.hazbasePendingAction);
|
|
6997
|
+
const environmentKey = def?.environment === "mainnet"
|
|
6998
|
+
? "settings.wallet.inventory.environment.mainnet"
|
|
6999
|
+
: "settings.wallet.inventory.environment.testnet";
|
|
7000
|
+
const actions = [];
|
|
7001
|
+
if (!available) {
|
|
7002
|
+
actions.push(`
|
|
7003
|
+
<button class="secondary secondary--wide" type="button" disabled>${escapeHtml(L("settings.wallet.status.comingSoon"))}</button>
|
|
7004
|
+
`);
|
|
7005
|
+
} else if (!configured && def?.family === "evm") {
|
|
7006
|
+
const pending = isHazbaseActionPending(def.action);
|
|
7007
|
+
const labelKey = {
|
|
7008
|
+
base: "settings.hazbase.action.bootstrapBase",
|
|
7009
|
+
"base-sepolia": "settings.hazbase.action.bootstrapBaseSepolia",
|
|
7010
|
+
polygon: "settings.hazbase.action.bootstrapPolygon",
|
|
7011
|
+
"polygon-amoy": "settings.hazbase.action.bootstrapPolygonAmoy",
|
|
7012
|
+
}[network] || "settings.hazbase.action.bootstrapBaseSepolia";
|
|
7013
|
+
actions.push(`
|
|
7014
|
+
<button
|
|
7015
|
+
class="${canConfigure ? "primary" : "secondary"} ${canConfigure ? "primary--wide" : "secondary--wide"}${pending ? " is-loading" : ""}"
|
|
7016
|
+
type="button"
|
|
7017
|
+
data-hazbase-action="${escapeHtml(def.action)}"
|
|
7018
|
+
${canConfigure && !actionBlocked ? "" : "disabled"}
|
|
7019
|
+
${pending ? 'aria-busy="true"' : ""}
|
|
7020
|
+
>${escapeHtml(L(labelKey))}</button>
|
|
7021
|
+
`);
|
|
7022
|
+
} else if (!configured && def?.family === "liquid") {
|
|
7023
|
+
const pending = isHazbaseActionPending("save-liquid-capability", network);
|
|
7024
|
+
actions.push(`
|
|
7025
|
+
<button
|
|
7026
|
+
class="${canConfigure ? "primary" : "secondary"} ${canConfigure ? "primary--wide" : "secondary--wide"}${pending ? " is-loading" : ""}"
|
|
7027
|
+
type="button"
|
|
7028
|
+
data-hazbase-action="save-liquid-capability"
|
|
7029
|
+
data-payment-network="${escapeHtml(network)}"
|
|
7030
|
+
${canConfigure && !actionBlocked ? "" : "disabled"}
|
|
7031
|
+
${pending ? 'aria-busy="true"' : ""}
|
|
7032
|
+
>${escapeHtml(L("settings.hazbase.action.saveLiquidCapability"))}</button>
|
|
7033
|
+
`);
|
|
7034
|
+
}
|
|
7035
|
+
return renderHazbaseWalletStepCard({
|
|
7036
|
+
number: 0,
|
|
7037
|
+
eyebrow: L(environmentKey),
|
|
7038
|
+
icon: "coin",
|
|
7039
|
+
title: L(def.titleKey),
|
|
7040
|
+
copy: L(def.copyKey),
|
|
7041
|
+
detail: !available
|
|
7042
|
+
? L("settings.wallet.mainnet.comingSoonDetail")
|
|
7043
|
+
: configured ? (capability.payTo || capability.payoutAddress || capability.smartAccountAddress) : L("settings.hazbase.wallet.missing"),
|
|
7044
|
+
monoDetail: available && configured,
|
|
7045
|
+
status: !available ? "comingSoon" : configured ? "complete" : canConfigure ? "optional" : "locked",
|
|
7046
|
+
form: available && !configured && def?.family === "liquid" ? renderLiquidCapabilityForm(network, hazbase) : "",
|
|
7047
|
+
actions,
|
|
7048
|
+
});
|
|
7049
|
+
}
|
|
7050
|
+
|
|
6493
7051
|
function deriveHazbaseWalletFlow(hazbase) {
|
|
6494
|
-
const accounts = Array.isArray(hazbase.accounts) ? hazbase.accounts : [];
|
|
6495
|
-
const baseSepolia = accounts.find((entry) => Number(entry.chainId) === 84532) || null;
|
|
6496
|
-
const baseMainnet = accounts.find((entry) => Number(entry.chainId) === 8453) || null;
|
|
6497
7052
|
const sessionInvalid = Boolean(hazbase.sessionInvalid);
|
|
6498
7053
|
const signedIn = Boolean(hazbase.signedIn) && !sessionInvalid;
|
|
6499
7054
|
const passkeyHost = hazbasePasskeyHostSupport();
|
|
6500
7055
|
const hasPasskey = Boolean(hazbase.credentialId || hazbase.deviceBindingId);
|
|
6501
|
-
const hasBaseSepolia = Boolean(baseSepolia?.smartAccountAddress);
|
|
6502
|
-
const hasBaseMainnet = Boolean(baseMainnet?.smartAccountAddress);
|
|
6503
|
-
const coreReady = signedIn && hasPasskey && hasBaseSepolia;
|
|
6504
7056
|
|
|
6505
|
-
const actionButton = (labelKey, action, { primary = false, disabled = false } = {}) =>
|
|
7057
|
+
const actionButton = (labelKey, action, { primary = false, disabled = false, attrs = "" } = {}) => {
|
|
7058
|
+
const pending = isHazbaseActionPending(action);
|
|
7059
|
+
const blocked = disabled || Boolean(state.hazbasePendingAction);
|
|
7060
|
+
return `
|
|
6506
7061
|
<button
|
|
6507
|
-
class="${primary ? "primary" : "secondary"} ${primary ? "primary--wide" : "secondary--wide"}"
|
|
7062
|
+
class="${primary ? "primary" : "secondary"} ${primary ? "primary--wide" : "secondary--wide"}${pending ? " is-loading" : ""}"
|
|
6508
7063
|
type="button"
|
|
6509
7064
|
data-hazbase-action="${action}"
|
|
6510
|
-
${
|
|
7065
|
+
${attrs}
|
|
7066
|
+
${blocked ? "disabled" : ""}
|
|
7067
|
+
${pending ? 'aria-busy="true"' : ""}
|
|
6511
7068
|
>${escapeHtml(L(labelKey))}</button>
|
|
6512
7069
|
`;
|
|
7070
|
+
};
|
|
6513
7071
|
|
|
6514
7072
|
return {
|
|
6515
7073
|
hasPasskey,
|
|
6516
|
-
baseSepolia,
|
|
6517
|
-
baseMainnet,
|
|
6518
7074
|
sessionInvalid,
|
|
6519
|
-
coreReady,
|
|
6520
7075
|
steps: [
|
|
6521
7076
|
{
|
|
7077
|
+
kind: "auth",
|
|
6522
7078
|
number: 1,
|
|
6523
7079
|
icon: "approval",
|
|
6524
7080
|
title: sessionInvalid ? L("settings.wallet.step.refreshSession.title") : L("settings.wallet.step.signIn.title"),
|
|
@@ -6560,6 +7116,7 @@ function deriveHazbaseWalletFlow(hazbase) {
|
|
|
6560
7116
|
],
|
|
6561
7117
|
},
|
|
6562
7118
|
{
|
|
7119
|
+
kind: "auth",
|
|
6563
7120
|
number: 2,
|
|
6564
7121
|
icon: "lock",
|
|
6565
7122
|
title: L("settings.wallet.step.passkey.title"),
|
|
@@ -6579,79 +7136,31 @@ function deriveHazbaseWalletFlow(hazbase) {
|
|
|
6579
7136
|
}),
|
|
6580
7137
|
],
|
|
6581
7138
|
},
|
|
6582
|
-
{
|
|
6583
|
-
number: 3,
|
|
6584
|
-
icon: "coin",
|
|
6585
|
-
title: L("settings.wallet.step.baseSepolia.title"),
|
|
6586
|
-
copy: L("settings.wallet.step.baseSepolia.copy"),
|
|
6587
|
-
detail: baseSepolia?.smartAccountAddress || L("settings.hazbase.wallet.missing"),
|
|
6588
|
-
monoDetail: Boolean(baseSepolia?.smartAccountAddress),
|
|
6589
|
-
status: hasBaseSepolia ? "complete" : signedIn && hasPasskey ? "current" : "locked",
|
|
6590
|
-
actions: hasBaseSepolia
|
|
6591
|
-
? []
|
|
6592
|
-
: [
|
|
6593
|
-
actionButton("settings.hazbase.action.bootstrapBaseSepolia", "bootstrap-base-sepolia", {
|
|
6594
|
-
primary: signedIn && hasPasskey,
|
|
6595
|
-
disabled: !signedIn || !hasPasskey,
|
|
6596
|
-
}),
|
|
6597
|
-
],
|
|
6598
|
-
},
|
|
6599
|
-
{
|
|
6600
|
-
number: 4,
|
|
6601
|
-
icon: "coin",
|
|
6602
|
-
title: L("settings.wallet.step.base.title"),
|
|
6603
|
-
copy: L("settings.wallet.step.base.copy"),
|
|
6604
|
-
detail: baseMainnet?.smartAccountAddress || L("settings.wallet.step.base.comingSoonDetail"),
|
|
6605
|
-
monoDetail: Boolean(baseMainnet?.smartAccountAddress),
|
|
6606
|
-
status: hasBaseMainnet ? "complete" : "comingSoon",
|
|
6607
|
-
actions: [],
|
|
6608
|
-
},
|
|
6609
7139
|
],
|
|
6610
7140
|
};
|
|
6611
7141
|
}
|
|
6612
7142
|
|
|
6613
|
-
function
|
|
6614
|
-
const
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
const
|
|
6618
|
-
|
|
6619
|
-
: flow.sessionInvalid
|
|
6620
|
-
? "settings-copy-block settings-copy-block--stacked wallet-flow-banner wallet-flow-banner--session-expired"
|
|
6621
|
-
: "settings-copy-block settings-copy-block--stacked wallet-flow-banner";
|
|
6622
|
-
// Ready state: surface the smart-account address prominently so the user
|
|
6623
|
-
// can see at a glance which wallet agents will use as `--pay-to` when
|
|
6624
|
-
// gating paid shares / A2A payouts. The address is the single most
|
|
6625
|
-
// actionable fact on this page once setup is complete — muted filler
|
|
6626
|
-
// copy buries it.
|
|
6627
|
-
const payoutAddress = flow.baseSepolia?.smartAccountAddress || "";
|
|
6628
|
-
const copyLabel = L("settings.wallet.ready.copyAddress");
|
|
6629
|
-
const body = flow.coreReady && payoutAddress
|
|
6630
|
-
? `
|
|
6631
|
-
<p class="wallet-flow-banner__copy muted">${escapeHtml(L("settings.wallet.ready.payoutIntro"))}</p>
|
|
6632
|
-
<button
|
|
6633
|
-
class="wallet-flow-banner__address"
|
|
6634
|
-
type="button"
|
|
6635
|
-
data-wallet-address-copy="${escapeHtml(payoutAddress)}"
|
|
6636
|
-
aria-label="${escapeHtml(copyLabel)}: ${escapeHtml(payoutAddress)}"
|
|
6637
|
-
>
|
|
6638
|
-
<span class="wallet-flow-banner__address-text">${escapeHtml(payoutAddress)}</span>
|
|
6639
|
-
<span class="wallet-flow-banner__address-icon-slot">
|
|
6640
|
-
<span class="wallet-flow-banner__address-icon wallet-flow-banner__address-icon--copy" aria-hidden="true">${renderIcon("copy")}</span>
|
|
6641
|
-
<span class="wallet-flow-banner__address-icon wallet-flow-banner__address-icon--check" aria-hidden="true">${renderIcon("check")}</span>
|
|
6642
|
-
</span>
|
|
6643
|
-
</button>
|
|
6644
|
-
`
|
|
6645
|
-
: `<p class="wallet-flow-banner__copy muted">${escapeHtml(
|
|
6646
|
-
flow.sessionInvalid
|
|
6647
|
-
? L("settings.wallet.sessionExpired.copy")
|
|
6648
|
-
: flow.coreReady ? L("settings.wallet.ready.copy") : L("settings.wallet.flow.copy"),
|
|
6649
|
-
)}</p>`;
|
|
7143
|
+
function renderLiquidCapabilityForm(network, hazbase) {
|
|
7144
|
+
const capabilities = Array.isArray(hazbase.paymentCapabilities) ? hazbase.paymentCapabilities : [];
|
|
7145
|
+
const existing = capabilities.find((entry) => entry.network === network) || {};
|
|
7146
|
+
const placeholder = network === "liquidtestnet" ? "tlq1..." : "lq1...";
|
|
7147
|
+
const error = hazbaseFormError("liquid", network);
|
|
7148
|
+
const errorId = `hazbase-liquid-error-${network}`;
|
|
6650
7149
|
return `
|
|
6651
|
-
<div class="
|
|
6652
|
-
<
|
|
6653
|
-
|
|
6654
|
-
|
|
7150
|
+
<div class="wallet-step-card__form">
|
|
7151
|
+
<label class="wallet-step-card__field">
|
|
7152
|
+
<span class="wallet-step-card__field-label">${escapeHtml(L("settings.hazbase.field.liquidAddressLabel"))}</span>
|
|
7153
|
+
<input
|
|
7154
|
+
class="wallet-step-card__field-input wallet-step-card__field-input--mono${error ? " wallet-step-card__field-input--error" : ""}"
|
|
7155
|
+
data-hazbase-input="liquid-payto"
|
|
7156
|
+
data-payment-network="${escapeHtml(network)}"
|
|
7157
|
+
value="${escapeHtml(existing.payTo || "")}"
|
|
7158
|
+
placeholder="${escapeHtml(placeholder)}"
|
|
7159
|
+
autocomplete="off"
|
|
7160
|
+
${error ? `aria-invalid="true" aria-describedby="${escapeHtml(errorId)}"` : ""}
|
|
7161
|
+
/>
|
|
7162
|
+
${error ? `<span class="wallet-step-card__field-error" id="${escapeHtml(errorId)}">${escapeHtml(error)}</span>` : ""}
|
|
7163
|
+
</label>
|
|
6655
7164
|
</div>
|
|
6656
7165
|
`;
|
|
6657
7166
|
}
|
|
@@ -6705,7 +7214,7 @@ function renderHazbaseWalletStepCard(step, { mode = "full" } = {}) {
|
|
|
6705
7214
|
<div class="wallet-step-card__headline">
|
|
6706
7215
|
<span class="wallet-step-card__icon" aria-hidden="true">${renderIcon(step.icon)}</span>
|
|
6707
7216
|
<div class="wallet-step-card__title-wrap">
|
|
6708
|
-
<p class="wallet-step-card__eyebrow">${escapeHtml(L("settings.wallet.stepNumber", { count: step.number }))}</p>
|
|
7217
|
+
<p class="wallet-step-card__eyebrow">${escapeHtml(step.eyebrow || L("settings.wallet.stepNumber", { count: step.number }))}</p>
|
|
6709
7218
|
<h3 class="wallet-step-card__title">${escapeHtml(step.title)}</h3>
|
|
6710
7219
|
</div>
|
|
6711
7220
|
</div>
|
|
@@ -6730,6 +7239,10 @@ function renderHazbaseSignInForm({ email, otpRequested, code }) {
|
|
|
6730
7239
|
// as the intentional escape hatch — clicking it reverts the form to the
|
|
6731
7240
|
// pre-sent state (code discarded, email stays for easy typo recovery).
|
|
6732
7241
|
const emailLocked = Boolean(otpRequested);
|
|
7242
|
+
const emailError = hazbaseFormError("email");
|
|
7243
|
+
const otpError = hazbaseFormError("otp");
|
|
7244
|
+
const emailErrorId = "hazbase-email-error";
|
|
7245
|
+
const otpErrorId = "hazbase-otp-error";
|
|
6733
7246
|
const emailLabelRow = emailLocked
|
|
6734
7247
|
? `<span class="wallet-step-card__field-label-row">
|
|
6735
7248
|
<span class="wallet-step-card__field-label">${escapeHtml(L("settings.hazbase.field.emailLabel"))}</span>
|
|
@@ -6737,6 +7250,7 @@ function renderHazbaseSignInForm({ email, otpRequested, code }) {
|
|
|
6737
7250
|
type="button"
|
|
6738
7251
|
class="wallet-step-card__field-link"
|
|
6739
7252
|
data-hazbase-action="change-email"
|
|
7253
|
+
${state.hazbasePendingAction ? "disabled" : ""}
|
|
6740
7254
|
>${escapeHtml(L("settings.hazbase.action.changeEmail"))}</button>
|
|
6741
7255
|
</span>`
|
|
6742
7256
|
: `<span class="wallet-step-card__field-label">${escapeHtml(L("settings.hazbase.field.emailLabel"))}</span>`;
|
|
@@ -6745,7 +7259,7 @@ function renderHazbaseSignInForm({ email, otpRequested, code }) {
|
|
|
6745
7259
|
${emailLabelRow}
|
|
6746
7260
|
<input
|
|
6747
7261
|
type="email"
|
|
6748
|
-
class="wallet-step-card__field-input${emailLocked ? " wallet-step-card__field-input--locked" : ""}"
|
|
7262
|
+
class="wallet-step-card__field-input${emailLocked ? " wallet-step-card__field-input--locked" : ""}${emailError ? " wallet-step-card__field-input--error" : ""}"
|
|
6749
7263
|
data-hazbase-input="otp-email"
|
|
6750
7264
|
value="${escapeHtml(email || "")}"
|
|
6751
7265
|
placeholder="${escapeHtml(L("settings.hazbase.field.emailPlaceholder"))}"
|
|
@@ -6755,7 +7269,9 @@ function renderHazbaseSignInForm({ email, otpRequested, code }) {
|
|
|
6755
7269
|
autocorrect="off"
|
|
6756
7270
|
spellcheck="false"
|
|
6757
7271
|
${emailLocked ? "disabled aria-disabled=\"true\"" : ""}
|
|
7272
|
+
${emailError ? `aria-invalid="true" aria-describedby="${escapeHtml(emailErrorId)}"` : ""}
|
|
6758
7273
|
/>
|
|
7274
|
+
${emailError ? `<span class="wallet-step-card__field-error" id="${escapeHtml(emailErrorId)}">${escapeHtml(emailError)}</span>` : ""}
|
|
6759
7275
|
</label>
|
|
6760
7276
|
`;
|
|
6761
7277
|
const otpField = otpRequested
|
|
@@ -6764,7 +7280,7 @@ function renderHazbaseSignInForm({ email, otpRequested, code }) {
|
|
|
6764
7280
|
<span class="wallet-step-card__field-label">${escapeHtml(L("settings.hazbase.field.otpLabel"))}</span>
|
|
6765
7281
|
<input
|
|
6766
7282
|
type="text"
|
|
6767
|
-
class="wallet-step-card__field-input wallet-step-card__field-input--mono"
|
|
7283
|
+
class="wallet-step-card__field-input wallet-step-card__field-input--mono${otpError ? " wallet-step-card__field-input--error" : ""}"
|
|
6768
7284
|
data-hazbase-input="otp-code"
|
|
6769
7285
|
value="${escapeHtml(code || "")}"
|
|
6770
7286
|
placeholder="${escapeHtml(L("settings.hazbase.field.otpPlaceholder"))}"
|
|
@@ -6774,7 +7290,9 @@ function renderHazbaseSignInForm({ email, otpRequested, code }) {
|
|
|
6774
7290
|
autocorrect="off"
|
|
6775
7291
|
spellcheck="false"
|
|
6776
7292
|
maxlength="12"
|
|
7293
|
+
${otpError ? `aria-invalid="true" aria-describedby="${escapeHtml(otpErrorId)}"` : ""}
|
|
6777
7294
|
/>
|
|
7295
|
+
${otpError ? `<span class="wallet-step-card__field-error" id="${escapeHtml(otpErrorId)}">${escapeHtml(otpError)}</span>` : ""}
|
|
6778
7296
|
</label>
|
|
6779
7297
|
`
|
|
6780
7298
|
: "";
|
|
@@ -6822,11 +7340,10 @@ function formatExpiresIn(ms) {
|
|
|
6822
7340
|
return rtf ? rtf.format(hr, "hour") : `in ${hr}h`;
|
|
6823
7341
|
}
|
|
6824
7342
|
|
|
6825
|
-
//
|
|
6826
|
-
// $0.10). Mirrors formatUsdc() in scripts/share-cli.mjs — kept as a small
|
|
7343
|
+
// Payment amounts are stored as asset-native atomic units. Kept as a small
|
|
6827
7344
|
// standalone helper rather than a shared module since the app bundle has no
|
|
6828
7345
|
// build step that pulls from scripts/.
|
|
6829
|
-
function
|
|
7346
|
+
function formatAtomicAmount(atomic, decimals = 6) {
|
|
6830
7347
|
let n;
|
|
6831
7348
|
try {
|
|
6832
7349
|
n = BigInt(String(atomic ?? "0"));
|
|
@@ -6834,12 +7351,33 @@ function formatUsdcAtomic(atomic) {
|
|
|
6834
7351
|
return "0.00";
|
|
6835
7352
|
}
|
|
6836
7353
|
if (n < 0n) n = -n;
|
|
6837
|
-
const
|
|
6838
|
-
const
|
|
7354
|
+
const places = Math.max(0, Math.min(18, Number(decimals) || 0));
|
|
7355
|
+
const scale = 10n ** BigInt(places);
|
|
7356
|
+
const whole = scale > 0n ? n / scale : n;
|
|
7357
|
+
const frac = scale > 0n ? (n % scale).toString().padStart(places, "0").replace(/0+$/u, "") : "";
|
|
6839
7358
|
if (!frac) return `${whole}.00`;
|
|
6840
7359
|
return `${whole}.${frac.padEnd(2, "0")}`;
|
|
6841
7360
|
}
|
|
6842
7361
|
|
|
7362
|
+
function formatUsdcAtomic(atomic) {
|
|
7363
|
+
return formatAtomicAmount(atomic, 6);
|
|
7364
|
+
}
|
|
7365
|
+
|
|
7366
|
+
function paymentAssetDecimals(asset) {
|
|
7367
|
+
const key = String(asset || "").toLowerCase();
|
|
7368
|
+
if (key === "jpyc") return 18;
|
|
7369
|
+
if (key === "usdt" || key === "lbtc") return 8;
|
|
7370
|
+
return 6;
|
|
7371
|
+
}
|
|
7372
|
+
|
|
7373
|
+
function paymentAssetLabel(asset) {
|
|
7374
|
+
const key = String(asset || "").toLowerCase();
|
|
7375
|
+
if (key === "jpyc") return "JPYC";
|
|
7376
|
+
if (key === "usdt") return "USDt";
|
|
7377
|
+
if (key === "lbtc") return "L-BTC";
|
|
7378
|
+
return "USDC";
|
|
7379
|
+
}
|
|
7380
|
+
|
|
6843
7381
|
function renderSettingsInfoRow(label, value, options = {}) {
|
|
6844
7382
|
const tone = settingsNavValueTone(value, options.valueTone || "");
|
|
6845
7383
|
const rowClassName = [
|
|
@@ -8461,6 +8999,36 @@ function renderHazbaseLogoutConfirmModal() {
|
|
|
8461
8999
|
`;
|
|
8462
9000
|
}
|
|
8463
9001
|
|
|
9002
|
+
function renderAgentPaymentDefaultsModal() {
|
|
9003
|
+
if (!state.agentPaymentDefaultsModalOpen) {
|
|
9004
|
+
return "";
|
|
9005
|
+
}
|
|
9006
|
+
const hazbase = state.hazbaseStatus || {};
|
|
9007
|
+
const items = agentPaymentDefaultDisplayItems(hazbase, effectiveAgentPaymentDefaults(hazbase));
|
|
9008
|
+
if (!items.length) {
|
|
9009
|
+
return "";
|
|
9010
|
+
}
|
|
9011
|
+
return `
|
|
9012
|
+
<div class="modal-backdrop" data-close-agent-payment-defaults>
|
|
9013
|
+
<section class="modal-card modal-card--wallet-defaults" role="dialog" aria-modal="true" aria-labelledby="agent-payment-defaults-title">
|
|
9014
|
+
<div class="helper-copy">
|
|
9015
|
+
<strong id="agent-payment-defaults-title">${escapeHtml(L("settings.wallet.agent.modalTitle"))}</strong>
|
|
9016
|
+
<p class="muted">${escapeHtml(L("settings.wallet.agent.modalCopy"))}</p>
|
|
9017
|
+
</div>
|
|
9018
|
+
<div class="wallet-agent-defaults-modal-list">
|
|
9019
|
+
${items.map((item) => `
|
|
9020
|
+
<div class="wallet-agent-defaults-modal-row">
|
|
9021
|
+
<span class="wallet-agent-defaults-modal-title">${escapeHtml(item.label)}</span>
|
|
9022
|
+
<span class="wallet-agent-defaults-modal-meta">${escapeHtml([item.scheme, shortAddress(item.address)].filter(Boolean).join(" · "))}</span>
|
|
9023
|
+
</div>
|
|
9024
|
+
`).join("")}
|
|
9025
|
+
</div>
|
|
9026
|
+
<button class="primary primary--wide" type="button" data-close-agent-payment-defaults>${escapeHtml(L("common.close"))}</button>
|
|
9027
|
+
</section>
|
|
9028
|
+
</div>
|
|
9029
|
+
`;
|
|
9030
|
+
}
|
|
9031
|
+
|
|
8464
9032
|
function renderLogoutConfirmModal() {
|
|
8465
9033
|
if (!state.logoutConfirmOpen || !state.session?.authenticated) {
|
|
8466
9034
|
return "";
|
|
@@ -9493,9 +10061,48 @@ function bindShellInteractions() {
|
|
|
9493
10061
|
|
|
9494
10062
|
for (const button of document.querySelectorAll("[data-hazbase-action]")) {
|
|
9495
10063
|
button.addEventListener("click", async () => {
|
|
10064
|
+
const action = button.dataset.hazbaseAction || "";
|
|
10065
|
+
if (!action || button.disabled || state.hazbasePendingAction) {
|
|
10066
|
+
return;
|
|
10067
|
+
}
|
|
10068
|
+
const actionNetwork = button.dataset.paymentNetwork || "";
|
|
9496
10069
|
state.hazbaseNotice = "";
|
|
9497
10070
|
state.hazbaseError = "";
|
|
9498
|
-
|
|
10071
|
+
clearHazbaseFormErrorsForAction(action, actionNetwork);
|
|
10072
|
+
if (action === "logout") {
|
|
10073
|
+
// Gate wallet logout behind an explicit confirm — the modal's
|
|
10074
|
+
// "confirm" button dispatches `logout-confirm`, which actually
|
|
10075
|
+
// hits the API. Short-circuit here so the initial click only
|
|
10076
|
+
// opens the dialog.
|
|
10077
|
+
state.hazbaseLogoutConfirmOpen = true;
|
|
10078
|
+
await renderShell();
|
|
10079
|
+
return;
|
|
10080
|
+
}
|
|
10081
|
+
if (action === "change-email") {
|
|
10082
|
+
// Flip the form back to pre-send mode. We keep the email so typo
|
|
10083
|
+
// recovery ("hoshin" -> "hoshino") stays one edit away, but drop
|
|
10084
|
+
// the now-stale OTP code. No server call — hazbase invalidates
|
|
10085
|
+
// the previous OTP automatically when a fresh one is requested.
|
|
10086
|
+
state.hazbaseOtpRequested = false;
|
|
10087
|
+
state.hazbaseOtpCode = "";
|
|
10088
|
+
state.hazbaseNotice = "";
|
|
10089
|
+
state.hazbaseError = "";
|
|
10090
|
+
clearHazbaseFormError("email");
|
|
10091
|
+
clearHazbaseFormError("otp");
|
|
10092
|
+
await renderShell();
|
|
10093
|
+
// Move focus back to the (now re-enabled) email input so the user
|
|
10094
|
+
// can start editing immediately.
|
|
10095
|
+
document.querySelector('[data-hazbase-input="otp-email"]')?.focus();
|
|
10096
|
+
return;
|
|
10097
|
+
}
|
|
10098
|
+
state.hazbasePendingAction = hazbaseActionPendingKey(action, actionNetwork);
|
|
10099
|
+
// Wallet auth touches external services and WebAuthn; without an
|
|
10100
|
+
// immediate affordance the button looked dead while the request was
|
|
10101
|
+
// still in flight. Mutate the current DOM synchronously so the first
|
|
10102
|
+
// tap visibly "takes", then let the final render reconcile state.
|
|
10103
|
+
button.classList.add("is-loading");
|
|
10104
|
+
button.disabled = true;
|
|
10105
|
+
button.setAttribute("aria-busy", "true");
|
|
9499
10106
|
try {
|
|
9500
10107
|
if (action === "request-otp") {
|
|
9501
10108
|
// Read from the DOM input, not just state. The state mirror is
|
|
@@ -9505,14 +10112,20 @@ for (const button of document.querySelectorAll("[data-hazbase-action]")) {
|
|
|
9505
10112
|
// Treat DOM as authoritative and fall back to state.
|
|
9506
10113
|
const emailInput = document.querySelector('[data-hazbase-input="otp-email"]');
|
|
9507
10114
|
const email = (emailInput?.value || state.hazbaseOtpEmail || "").trim();
|
|
9508
|
-
if (!email)
|
|
9509
|
-
|
|
10115
|
+
if (!email) {
|
|
10116
|
+
setHazbaseFormError("email", L("error.hazbaseEmailRequired"));
|
|
10117
|
+
emailInput?.focus();
|
|
10118
|
+
return;
|
|
10119
|
+
}
|
|
10120
|
+
const result = await apiPost("/api/hazbase/request-otp", { email }, { timeoutMs: HAZBASE_ACTION_TIMEOUT_MS });
|
|
9510
10121
|
state.hazbaseOtpEmail = email;
|
|
9511
10122
|
state.hazbaseOtpRequested = true;
|
|
9512
10123
|
state.hazbaseOtpCode = "";
|
|
9513
|
-
|
|
10124
|
+
clearHazbaseFormError("email");
|
|
10125
|
+
clearHazbaseFormError("otp");
|
|
10126
|
+
showToast(result?.debugCode
|
|
9514
10127
|
? `${L("settings.hazbase.notice.otpRequested")} (${result.debugCode})`
|
|
9515
|
-
: L("settings.hazbase.notice.otpRequested");
|
|
10128
|
+
: L("settings.hazbase.notice.otpRequested"));
|
|
9516
10129
|
} else if (action === "verify-otp") {
|
|
9517
10130
|
// Same pattern — DOM wins, state fallback covers the rare case
|
|
9518
10131
|
// where the field was removed/re-added between type and click.
|
|
@@ -9520,88 +10133,148 @@ for (const button of document.querySelectorAll("[data-hazbase-action]")) {
|
|
|
9520
10133
|
const codeInput = document.querySelector('[data-hazbase-input="otp-code"]');
|
|
9521
10134
|
const email = (emailInput?.value || state.hazbaseOtpEmail || "").trim();
|
|
9522
10135
|
const code = (codeInput?.value || state.hazbaseOtpCode || "").trim();
|
|
9523
|
-
|
|
9524
|
-
if (!
|
|
9525
|
-
|
|
10136
|
+
let hasInlineError = false;
|
|
10137
|
+
if (!email) {
|
|
10138
|
+
setHazbaseFormError("email", L("error.hazbaseEmailRequired"));
|
|
10139
|
+
hasInlineError = true;
|
|
10140
|
+
}
|
|
10141
|
+
if (!code) {
|
|
10142
|
+
setHazbaseFormError("otp", L("error.hazbaseOtpRequired"));
|
|
10143
|
+
hasInlineError = true;
|
|
10144
|
+
}
|
|
10145
|
+
if (hasInlineError) {
|
|
10146
|
+
(email ? codeInput : emailInput)?.focus();
|
|
10147
|
+
return;
|
|
10148
|
+
}
|
|
10149
|
+
await apiPost("/api/hazbase/verify-otp", { email, code }, { timeoutMs: HAZBASE_ACTION_TIMEOUT_MS });
|
|
9526
10150
|
state.hazbaseOtpRequested = false;
|
|
9527
10151
|
state.hazbaseOtpEmail = "";
|
|
9528
10152
|
state.hazbaseOtpCode = "";
|
|
9529
|
-
|
|
10153
|
+
clearHazbaseFormError("email");
|
|
10154
|
+
clearHazbaseFormError("otp");
|
|
10155
|
+
showToast(L("settings.hazbase.notice.otpVerified"));
|
|
9530
10156
|
} else if (action === "register-passkey") {
|
|
9531
10157
|
if (!hazbasePasskeyHostSupport().eligible) {
|
|
9532
10158
|
throw new Error(L("error.hazbasePasskeyLocalHostRequired"));
|
|
9533
10159
|
}
|
|
9534
10160
|
const { createPasskeyRegistrationCredential } = await loadHazbasePasskeyModule();
|
|
9535
|
-
const challenge = await apiPost("/api/hazbase/passkey/register/challenge", {});
|
|
10161
|
+
const challenge = await apiPost("/api/hazbase/passkey/register/challenge", {}, { timeoutMs: HAZBASE_ACTION_TIMEOUT_MS });
|
|
9536
10162
|
const credential = await createPasskeyRegistrationCredential(challenge);
|
|
9537
10163
|
await apiPost("/api/hazbase/passkey/register/complete", {
|
|
9538
10164
|
challengeId: challenge.challengeId,
|
|
9539
10165
|
credential,
|
|
9540
|
-
});
|
|
9541
|
-
|
|
9542
|
-
} else if (action === "bootstrap-base-sepolia" || action === "bootstrap-base") {
|
|
10166
|
+
}, { timeoutMs: HAZBASE_ACTION_TIMEOUT_MS });
|
|
10167
|
+
showToast(L("settings.hazbase.notice.passkeyRegistered"));
|
|
10168
|
+
} else if (action === "bootstrap-base-sepolia" || action === "bootstrap-base" || action === "bootstrap-polygon-amoy" || action === "bootstrap-polygon") {
|
|
9543
10169
|
if (!hazbasePasskeyHostSupport().eligible) {
|
|
9544
10170
|
throw new Error(L("error.hazbasePasskeyLocalHostRequired"));
|
|
9545
10171
|
}
|
|
9546
10172
|
const { createPasskeyAssertionCredential } = await loadHazbasePasskeyModule();
|
|
9547
|
-
const chainId =
|
|
9548
|
-
|
|
10173
|
+
const chainId = {
|
|
10174
|
+
"bootstrap-base": 8453,
|
|
10175
|
+
"bootstrap-base-sepolia": 84532,
|
|
10176
|
+
"bootstrap-polygon": 137,
|
|
10177
|
+
"bootstrap-polygon-amoy": 80002,
|
|
10178
|
+
}[action];
|
|
10179
|
+
const challenge = await apiPost("/api/hazbase/passkey/assert/challenge", { purpose: "bootstrap" }, { timeoutMs: HAZBASE_ACTION_TIMEOUT_MS });
|
|
9549
10180
|
const credential = await createPasskeyAssertionCredential(challenge);
|
|
9550
10181
|
await apiPost("/api/hazbase/passkey/assert/complete", {
|
|
9551
10182
|
challengeId: challenge.challengeId,
|
|
9552
10183
|
credential,
|
|
9553
10184
|
purpose: "bootstrap",
|
|
9554
|
-
});
|
|
9555
|
-
await apiPost("/api/hazbase/account/bootstrap", { chainId });
|
|
9556
|
-
|
|
9557
|
-
} else if (action === "
|
|
9558
|
-
|
|
9559
|
-
|
|
9560
|
-
|
|
9561
|
-
|
|
9562
|
-
|
|
9563
|
-
|
|
9564
|
-
|
|
10185
|
+
}, { timeoutMs: HAZBASE_ACTION_TIMEOUT_MS });
|
|
10186
|
+
await apiPost("/api/hazbase/account/bootstrap", { chainId }, { timeoutMs: HAZBASE_ACTION_TIMEOUT_MS });
|
|
10187
|
+
showToast(L("settings.hazbase.notice.walletBootstrapped", { chainId }));
|
|
10188
|
+
} else if (action === "save-liquid-capability") {
|
|
10189
|
+
const network = actionNetwork;
|
|
10190
|
+
const input = document.querySelector(`[data-hazbase-input="liquid-payto"][data-payment-network="${CSS.escape(network)}"]`);
|
|
10191
|
+
const payTo = (input?.value || "").trim();
|
|
10192
|
+
if (!payTo) {
|
|
10193
|
+
setHazbaseFormError("liquid", L("error.hazbaseLiquidAddressRequired"), network);
|
|
10194
|
+
input?.focus();
|
|
10195
|
+
return;
|
|
10196
|
+
}
|
|
10197
|
+
await apiPost("/api/hazbase/payment-capability", { network, asset: "usdt", payTo }, { timeoutMs: HAZBASE_ACTION_TIMEOUT_MS });
|
|
10198
|
+
clearHazbaseFormError("liquid", network);
|
|
10199
|
+
showToast(L("settings.hazbase.notice.paymentCapabilitySaved", { network }));
|
|
9565
10200
|
} else if (action === "logout-confirm") {
|
|
9566
10201
|
state.hazbaseLogoutConfirmOpen = false;
|
|
9567
|
-
await apiPost("/api/hazbase/logout", {});
|
|
9568
|
-
|
|
10202
|
+
await apiPost("/api/hazbase/logout", {}, { timeoutMs: HAZBASE_ACTION_TIMEOUT_MS });
|
|
10203
|
+
showToast(L("settings.hazbase.notice.signedOut"));
|
|
9569
10204
|
} else if (action === "refresh-session") {
|
|
9570
|
-
await apiPost("/api/hazbase/session/refresh", {});
|
|
10205
|
+
await apiPost("/api/hazbase/session/refresh", {}, { timeoutMs: HAZBASE_ACTION_TIMEOUT_MS });
|
|
9571
10206
|
state.hazbaseOtpRequested = false;
|
|
9572
10207
|
state.hazbaseOtpCode = "";
|
|
9573
|
-
|
|
9574
|
-
} else if (action === "change-email") {
|
|
9575
|
-
// Flip the form back to pre-send mode. We keep the email so typo
|
|
9576
|
-
// recovery ("hoshin" → "hoshino") stays one edit away, but drop
|
|
9577
|
-
// the now-stale OTP code. No server call — hazbase invalidates
|
|
9578
|
-
// the previous OTP automatically when a fresh one is requested.
|
|
9579
|
-
state.hazbaseOtpRequested = false;
|
|
9580
|
-
state.hazbaseOtpCode = "";
|
|
9581
|
-
state.hazbaseNotice = "";
|
|
9582
|
-
state.hazbaseError = "";
|
|
9583
|
-
await renderShell();
|
|
9584
|
-
// Move focus back to the (now re-enabled) email input so the user
|
|
9585
|
-
// can start editing immediately.
|
|
9586
|
-
document.querySelector('[data-hazbase-input="otp-email"]')?.focus();
|
|
9587
|
-
return;
|
|
9588
|
-
} else if (action === "mainnet-opt-in") {
|
|
9589
|
-
// Pure client-side reveal — the mainnet step is always in the flow
|
|
9590
|
-
// data, we just hide it behind an opt-in link to keep the default
|
|
9591
|
-
// path (testnet only) focused. No network call; no status refetch.
|
|
9592
|
-
state.hazbaseMainnetOptIn = true;
|
|
9593
|
-
await renderShell();
|
|
9594
|
-
return;
|
|
10208
|
+
showToast(L("settings.hazbase.notice.sessionRefreshStarted"));
|
|
9595
10209
|
}
|
|
9596
|
-
await fetchHazbaseStatus();
|
|
10210
|
+
await fetchHazbaseStatus({ timeoutMs: HAZBASE_ACTION_TIMEOUT_MS });
|
|
9597
10211
|
} catch (error) {
|
|
9598
10212
|
if (error?.errorKey === "hazbase-session-expired") {
|
|
9599
10213
|
state.hazbaseOtpRequested = false;
|
|
9600
10214
|
state.hazbaseOtpCode = "";
|
|
9601
|
-
await fetchHazbaseStatus();
|
|
10215
|
+
await fetchHazbaseStatus({ timeoutMs: HAZBASE_ACTION_TIMEOUT_MS });
|
|
9602
10216
|
}
|
|
9603
|
-
|
|
10217
|
+
const message = error.message || String(error);
|
|
10218
|
+
if (!applyHazbaseInlineError(action, error, actionNetwork)) {
|
|
10219
|
+
showToast(message, { tone: "error" });
|
|
10220
|
+
}
|
|
10221
|
+
} finally {
|
|
10222
|
+
state.hazbasePendingAction = "";
|
|
10223
|
+
await renderShell();
|
|
9604
10224
|
}
|
|
10225
|
+
});
|
|
10226
|
+
}
|
|
10227
|
+
|
|
10228
|
+
for (const button of document.querySelectorAll("[data-hazbase-agent-defaults-save], [data-hazbase-agent-defaults-all]")) {
|
|
10229
|
+
button.addEventListener("click", async () => {
|
|
10230
|
+
if (button.disabled || state.hazbasePendingAction) {
|
|
10231
|
+
return;
|
|
10232
|
+
}
|
|
10233
|
+
state.hazbaseNotice = "";
|
|
10234
|
+
state.hazbaseError = "";
|
|
10235
|
+
state.hazbasePendingAction = button.hasAttribute("data-hazbase-agent-defaults-all")
|
|
10236
|
+
? "agent-defaults-all"
|
|
10237
|
+
: "agent-defaults-save";
|
|
10238
|
+
button.classList.add("is-loading");
|
|
10239
|
+
button.disabled = true;
|
|
10240
|
+
button.setAttribute("aria-busy", "true");
|
|
10241
|
+
try {
|
|
10242
|
+
if (button.hasAttribute("data-hazbase-agent-defaults-all")) {
|
|
10243
|
+
await apiPost("/api/hazbase/agent-payment-defaults", { mode: "configured" }, { timeoutMs: HAZBASE_ACTION_TIMEOUT_MS });
|
|
10244
|
+
} else {
|
|
10245
|
+
const accepts = state.hazbaseAgentPaymentDefaultsDraft?.mode === "custom"
|
|
10246
|
+
? normalizeAgentPaymentDefaultRefs(state.hazbaseAgentPaymentDefaultsDraft.accepts)
|
|
10247
|
+
: selectedAgentPaymentDefaultRefs();
|
|
10248
|
+
await apiPost("/api/hazbase/agent-payment-defaults", { mode: "custom", accepts }, { timeoutMs: HAZBASE_ACTION_TIMEOUT_MS });
|
|
10249
|
+
}
|
|
10250
|
+
state.hazbaseAgentPaymentDefaultsDraft = null;
|
|
10251
|
+
showToast(L("settings.hazbase.notice.agentDefaultsSaved"));
|
|
10252
|
+
await fetchHazbaseStatus({ timeoutMs: HAZBASE_ACTION_TIMEOUT_MS });
|
|
10253
|
+
} catch (error) {
|
|
10254
|
+
showToast(error.message || String(error), { tone: "error" });
|
|
10255
|
+
} finally {
|
|
10256
|
+
state.hazbasePendingAction = "";
|
|
10257
|
+
await renderShell();
|
|
10258
|
+
}
|
|
10259
|
+
});
|
|
10260
|
+
}
|
|
10261
|
+
|
|
10262
|
+
function selectedAgentPaymentDefaultRefs() {
|
|
10263
|
+
return normalizeAgentPaymentDefaultRefs(Array.from(document.querySelectorAll("[data-agent-payment-default]:checked"))
|
|
10264
|
+
.map((input) => ({
|
|
10265
|
+
network: input.dataset.paymentNetwork || "",
|
|
10266
|
+
asset: input.dataset.paymentAsset || "",
|
|
10267
|
+
})));
|
|
10268
|
+
}
|
|
10269
|
+
|
|
10270
|
+
for (const input of document.querySelectorAll("[data-agent-payment-default]")) {
|
|
10271
|
+
input.addEventListener("change", async () => {
|
|
10272
|
+
state.hazbaseNotice = "";
|
|
10273
|
+
state.hazbaseError = "";
|
|
10274
|
+
state.hazbaseAgentPaymentDefaultsDraft = {
|
|
10275
|
+
mode: "custom",
|
|
10276
|
+
accepts: selectedAgentPaymentDefaultRefs(),
|
|
10277
|
+
};
|
|
9605
10278
|
await renderShell();
|
|
9606
10279
|
});
|
|
9607
10280
|
}
|
|
@@ -9610,11 +10283,31 @@ for (const button of document.querySelectorAll("[data-hazbase-action]")) {
|
|
|
9610
10283
|
// notice clear, etc.) can repopulate `value="..."` without wiping what
|
|
9611
10284
|
// the user was typing. Reads happen at button-click time against state,
|
|
9612
10285
|
// which is why we don't need to also query the DOM in the handler.
|
|
10286
|
+
function clearRenderedHazbaseInputError(input) {
|
|
10287
|
+
input.classList.remove("wallet-step-card__field-input--error");
|
|
10288
|
+
input.removeAttribute("aria-invalid");
|
|
10289
|
+
const describedBy = input.getAttribute("aria-describedby");
|
|
10290
|
+
if (describedBy) {
|
|
10291
|
+
document.getElementById(describedBy)?.remove();
|
|
10292
|
+
input.removeAttribute("aria-describedby");
|
|
10293
|
+
}
|
|
10294
|
+
}
|
|
10295
|
+
|
|
9613
10296
|
for (const input of document.querySelectorAll("[data-hazbase-input]")) {
|
|
9614
10297
|
const name = input.dataset.hazbaseInput || "";
|
|
9615
10298
|
input.addEventListener("input", () => {
|
|
9616
|
-
if (name === "otp-email")
|
|
9617
|
-
|
|
10299
|
+
if (name === "otp-email") {
|
|
10300
|
+
state.hazbaseOtpEmail = input.value;
|
|
10301
|
+
clearHazbaseFormError("email");
|
|
10302
|
+
clearRenderedHazbaseInputError(input);
|
|
10303
|
+
} else if (name === "otp-code") {
|
|
10304
|
+
state.hazbaseOtpCode = input.value;
|
|
10305
|
+
clearHazbaseFormError("otp");
|
|
10306
|
+
clearRenderedHazbaseInputError(input);
|
|
10307
|
+
} else if (name === "liquid-payto") {
|
|
10308
|
+
clearHazbaseFormError("liquid", input.dataset.paymentNetwork || "");
|
|
10309
|
+
clearRenderedHazbaseInputError(input);
|
|
10310
|
+
}
|
|
9618
10311
|
});
|
|
9619
10312
|
// Enter key submits the step. In the email field it triggers "send"
|
|
9620
10313
|
// (or "verify" once a code was already issued — the email field stays
|
|
@@ -10248,6 +10941,23 @@ function bindSharedUi(renderFn) {
|
|
|
10248
10941
|
});
|
|
10249
10942
|
}
|
|
10250
10943
|
|
|
10944
|
+
for (const button of document.querySelectorAll("[data-open-agent-payment-defaults]")) {
|
|
10945
|
+
button.addEventListener("click", async () => {
|
|
10946
|
+
state.agentPaymentDefaultsModalOpen = true;
|
|
10947
|
+
await renderFn();
|
|
10948
|
+
});
|
|
10949
|
+
}
|
|
10950
|
+
|
|
10951
|
+
for (const button of document.querySelectorAll("[data-close-agent-payment-defaults]")) {
|
|
10952
|
+
button.addEventListener("click", async (event) => {
|
|
10953
|
+
if (button.classList.contains("modal-backdrop") && event.target.closest(".modal-card")) {
|
|
10954
|
+
return;
|
|
10955
|
+
}
|
|
10956
|
+
state.agentPaymentDefaultsModalOpen = false;
|
|
10957
|
+
await renderFn();
|
|
10958
|
+
});
|
|
10959
|
+
}
|
|
10960
|
+
|
|
10251
10961
|
for (const button of document.querySelectorAll("[data-dismiss-install]")) {
|
|
10252
10962
|
button.addEventListener("click", async () => {
|
|
10253
10963
|
state.installBannerDismissed = true;
|
|
@@ -10406,20 +11116,39 @@ function openSettingsSubpage(page) {
|
|
|
10406
11116
|
if (!page) {
|
|
10407
11117
|
return;
|
|
10408
11118
|
}
|
|
11119
|
+
const openingFromRoot = !state.settingsSubpage;
|
|
10409
11120
|
if (!isDesktopLayout()) {
|
|
10410
|
-
|
|
10411
|
-
|
|
10412
|
-
|
|
10413
|
-
|
|
11121
|
+
if (openingFromRoot) {
|
|
11122
|
+
state.settingsScrollState = {
|
|
11123
|
+
y: currentViewportScrollY(),
|
|
11124
|
+
};
|
|
11125
|
+
state.pendingSettingsScrollRestore = false;
|
|
11126
|
+
}
|
|
10414
11127
|
state.pendingSettingsSubpageScrollReset = true;
|
|
10415
11128
|
}
|
|
10416
11129
|
state.settingsSubpage = page;
|
|
10417
11130
|
}
|
|
10418
11131
|
|
|
11132
|
+
function parentSettingsSubpage(page) {
|
|
11133
|
+
if (page === "walletInventoryBase" || page === "walletInventoryLiquid" || page === "walletInventoryPolygon") {
|
|
11134
|
+
return "walletInventory";
|
|
11135
|
+
}
|
|
11136
|
+
return page === "walletInventory" ? "wallet" : "";
|
|
11137
|
+
}
|
|
11138
|
+
|
|
10419
11139
|
function closeSettingsSubpage() {
|
|
10420
11140
|
if (!state.settingsSubpage) {
|
|
10421
11141
|
return;
|
|
10422
11142
|
}
|
|
11143
|
+
const parentPage = parentSettingsSubpage(state.settingsSubpage);
|
|
11144
|
+
if (parentPage) {
|
|
11145
|
+
state.settingsSubpage = parentPage;
|
|
11146
|
+
if (!isDesktopLayout()) {
|
|
11147
|
+
state.pendingSettingsSubpageScrollReset = true;
|
|
11148
|
+
state.pendingSettingsScrollRestore = false;
|
|
11149
|
+
}
|
|
11150
|
+
return;
|
|
11151
|
+
}
|
|
10423
11152
|
state.settingsSubpage = "";
|
|
10424
11153
|
if (!isDesktopLayout() && state.settingsScrollState) {
|
|
10425
11154
|
state.pendingSettingsScrollRestore = true;
|
|
@@ -11478,7 +12207,7 @@ async function apiPost(url, body, opts = {}) {
|
|
|
11478
12207
|
async function readError(response) {
|
|
11479
12208
|
try {
|
|
11480
12209
|
const payload = await response.json();
|
|
11481
|
-
const errorKey =
|
|
12210
|
+
const errorKey = firstApiErrorKey(payload);
|
|
11482
12211
|
const message = localizeApiError(errorKey || payload.message || response.statusText);
|
|
11483
12212
|
return { message, errorKey, payload };
|
|
11484
12213
|
} catch {
|
|
@@ -11486,11 +12215,58 @@ async function readError(response) {
|
|
|
11486
12215
|
}
|
|
11487
12216
|
}
|
|
11488
12217
|
|
|
11489
|
-
function
|
|
12218
|
+
function firstApiErrorKey(payload) {
|
|
12219
|
+
if (!payload || typeof payload !== "object") {
|
|
12220
|
+
return "";
|
|
12221
|
+
}
|
|
12222
|
+
return [
|
|
12223
|
+
payload.error,
|
|
12224
|
+
payload.code,
|
|
12225
|
+
payload.errorCode,
|
|
12226
|
+
payload.message,
|
|
12227
|
+
].map(extractApiErrorKey).find(Boolean) || "";
|
|
12228
|
+
}
|
|
12229
|
+
|
|
12230
|
+
function extractApiErrorKey(value) {
|
|
11490
12231
|
const raw = normalizeClientText(value);
|
|
11491
12232
|
if (!raw) {
|
|
11492
12233
|
return "";
|
|
11493
12234
|
}
|
|
12235
|
+
const embedded = extractEmbeddedApiErrorPayload(raw);
|
|
12236
|
+
if (embedded) {
|
|
12237
|
+
return firstApiErrorKey(embedded);
|
|
12238
|
+
}
|
|
12239
|
+
const [head] = raw.split(":");
|
|
12240
|
+
const normalized = normalizeApiErrorKey(head);
|
|
12241
|
+
return normalized || normalizeApiErrorKey(raw);
|
|
12242
|
+
}
|
|
12243
|
+
|
|
12244
|
+
function extractEmbeddedApiErrorPayload(value) {
|
|
12245
|
+
const start = value.indexOf("{");
|
|
12246
|
+
const end = value.lastIndexOf("}");
|
|
12247
|
+
if (start < 0 || end <= start) {
|
|
12248
|
+
return null;
|
|
12249
|
+
}
|
|
12250
|
+
try {
|
|
12251
|
+
return JSON.parse(value.slice(start, end + 1));
|
|
12252
|
+
} catch {
|
|
12253
|
+
return null;
|
|
12254
|
+
}
|
|
12255
|
+
}
|
|
12256
|
+
|
|
12257
|
+
function normalizeApiErrorKey(value) {
|
|
12258
|
+
return normalizeClientText(value)
|
|
12259
|
+
.toLowerCase()
|
|
12260
|
+
.replace(/_/g, "-")
|
|
12261
|
+
.replace(/[^a-z0-9-]/g, "");
|
|
12262
|
+
}
|
|
12263
|
+
|
|
12264
|
+
function localizeApiError(value) {
|
|
12265
|
+
const original = normalizeClientText(value);
|
|
12266
|
+
const raw = extractApiErrorKey(value) || normalizeApiErrorKey(value);
|
|
12267
|
+
if (!original && !raw) {
|
|
12268
|
+
return "";
|
|
12269
|
+
}
|
|
11494
12270
|
const map = {
|
|
11495
12271
|
"pairing-unavailable": "error.pairingUnavailable",
|
|
11496
12272
|
"invalid-pairing-credentials": "error.invalidPairingCredentials",
|
|
@@ -11523,10 +12299,14 @@ function localizeApiError(value) {
|
|
|
11523
12299
|
"hazbase-session-expired": "error.hazbaseSessionExpired",
|
|
11524
12300
|
"hazbase-passkey-local-host-required": "error.hazbasePasskeyLocalHostRequired",
|
|
11525
12301
|
"hazbase-wallet-account-missing": "error.hazbaseWalletAccountMissing",
|
|
12302
|
+
"email-required": "error.hazbaseEmailRequired",
|
|
12303
|
+
"otp-required": "error.hazbaseOtpRequired",
|
|
12304
|
+
"invalid-liquid-address": "error.hazbaseLiquidAddressInvalid",
|
|
12305
|
+
"payment-network-coming-soon": "error.paymentNetworkComingSoon",
|
|
11526
12306
|
"unsupported-chain": "error.unsupportedChain",
|
|
11527
12307
|
};
|
|
11528
12308
|
const key = map[raw];
|
|
11529
|
-
return key ? L(key) :
|
|
12309
|
+
return key ? L(key) : original;
|
|
11530
12310
|
}
|
|
11531
12311
|
|
|
11532
12312
|
function normalizeClientText(value) {
|