Skip to content

feat: Web3Auth social login (Google + Apple) - #872

Draft
raul-oliveira wants to merge 1 commit into
masterfrom
raul-oliveira/feat/web3auth-social-login
Draft

feat: Web3Auth social login (Google + Apple)#872
raul-oliveira wants to merge 1 commit into
masterfrom
raul-oliveira/feat/web3auth-social-login

Conversation

@raul-oliveira

@raul-oliveira raul-oliveira commented May 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a third option on Welcome → WalletType"Sign in with social" — that bootstraps a single-key Hathor wallet from a Web3Auth-derived private key. OAuth and mandatory MFA are delegated to the Web3Auth-hosted webview opened in a sandboxed Electron BrowserWindow popup.

This is the desktop sibling of the mobile Web3Auth integration on wallet-mobile-web3auth and reuses the same Hathor-owned custom verifier (hathor-google), so the derived address is identical across platforms for the same Google account.

This is PR1 of three for the Web3Auth desktop rollout:

  1. (this PR) Google + Apple social login — full single-key flow end-to-end.
  2. Reown coexistence with single-key wallets.
  3. Email login (depends on the custom JWT verifier backend).

Blocker before merge: package.json currently consumes @hathor/wallet-lib via yalc from HathorNetwork/hathor-wallet-lib#1093. Once that lib PR is merged and published as @hathor/wallet-lib@^3.2.0, this PR will switch to the published version before merge.

What's in

  • sagas/web3auth.js — lazy-singleton Web3Auth no-modal SDK, web3authLogin(provider, network), classifyWeb3AuthError, restore + logout flows.
  • screens/Web3AuthLogin.js — three provider cards (Google active, Apple gated on appleClientId, Email inert until PR3).
  • components/Web3AuthErrorDialog.js — classifies SDK errors into 7 buckets (USER_CANCELLED, NETWORK, MFA_REQUIRED, VERIFIER_CONFIG, RATE_LIMITED, SESSION_EXPIRED, UNKNOWN) with localized copy and a Try-again affordance.
  • screens/Signin.js (Web3Auth branch) — ChoosePin variant that persists encrypted singleKeyPrivateKey via LOCAL_STORE.initWeb3AuthStorage; subsequent unlocks become PIN-only.
  • sagas/wallet.js (Web3Auth branch) — constructs HathorWallet({ privateKey, scanPolicy: SINGLE_ADDRESS, ... }), forces useWalletService=false, registers setExternalTxSigningMethod.
  • screens/LockedWallet.js — Web3Auth-aware unlock branch.
  • Network-aware configWEB3AUTH_CONFIG in src/constants.js (testnet Sapphire Devnet today; mainnet placeholder with controlled VERIFIER_CONFIG error).
  • Single-key UI invariantsSettings, WalletAddress, sagas/reown.js, components/Navigation.js gated on isSingleKeyWallet selector so Reown, Atomic Swap, Nano Contract, and "Generate new address" are hidden for Web3Auth wallets.
  • public/electron.jssetWindowOpenHandler whitelists Web3Auth + Google + Apple OAuth hosts in a sandboxed contextIsolation partition.
  • config-overrides.js + new src/web3auth-crypto-shim.js — Webpack 5 node:crypto scheme resolver (NodeProtocolUrlPlugin) plus a custom ESM shim that wraps crypto-browserify and exposes webcrypto (required by the Web3Auth SDK chain).
  • reducers/index.js + actions/index.js — new Redux state: walletType, web3authEmail, isSingleKeyWallet.
  • storage.jsinitWeb3AuthStorage + getters layered on top of the existing HybridStore.
  • Assets — Google/Apple/Email provider SVGs, styles/web3auth.scss (BEM + nested & per repo SCSS conventions).
  • Feature flagweb3auth-desktop.rollout (Unleash, default OFF in FEATURE_TOGGLE_DEFAULTS).
  • qa/QA.md — 10 manual QA scenarios covering happy path, restart/unlock, sign-out/sign-in, OAuth cancel, offline, MFA abort, feature-flag off, cross-platform key portability, mainnet placeholder, and single-key UI invariants.
  • locale/texts.pot — regenerated for the new t\`` strings.

What's NOT in

  • Reown / WalletConnect for single-key wallets — covered by PR2.
  • Email login — depends on the custom JWT verifier infra; covered by PR3.
  • Wallet-service support for single-key wallets — RFC [Feature] Flow to sell/buy custom tokens Over-the-counter (OTC) #46 §1.9 defers this; useWalletService is forced to false for Web3Auth wallets.
  • Mainnet credentialsWEB3AUTH_CONFIG.mainnet.clientId is null until the Web3Auth Mainnet project is provisioned; mainnet attempts surface the "Sign-in temporarily unavailable" dialog.
  • Apple Developer Program credentialsappleClientId is null until access is granted; the Apple card auto-activates once the constant is populated (no other code change required).
  • Automated tests — mobile made the same decision; the OAuth-popup-MFA flow is expensive to mock with fidelity. Coverage is qa/QA.md.
  • Migration between wallet types — a wallet is software, hardware, or Web3Auth; switching requires reset.

Acceptance criteria

All scenarios must pass on testnet with web3auth-desktop.rollout enabled. Mainnet items are gated on Web3Auth project provisioning.

Happy path & lifecycle

  • Scenario 1 — First-time login (Google): Welcome → WalletType → "Sign in with social" → "Continue with Google" → OAuth + mandatory MFA setup → PIN/password → dashboard loads. Receive 1 HTR from faucet and send 0.5 HTR back without errors.
  • Scenario 2 — Restart & unlock: Quit + relaunch shows LockedWallet with PIN prompt (no seed words). PIN unlocks the wallet and the previous balance is preserved.
  • Scenario 3 — Sign out & back in: Settings → "Sign out of your Hathor account <email>" → confirm. Re-login via the same Google account does NOT re-prompt MFA. New PIN is accepted. The Receive address matches the previous session.

Error / edge cases

  • Scenario 4 — Cancel OAuth popup: Closing the popup without authenticating returns silently to the social login screen — no error dialog.
  • Scenario 5 — Offline: With network disabled, clicking "Continue with Google" surfaces Web3AuthErrorDialog with "Connection problem" copy and a Try-again button.
  • Scenario 6 — Abort MFA setup: Closing the MFA webview without configuring a factor surfaces Web3AuthErrorDialog with the "Set up a recovery factor" copy.

Feature gating

  • Scenario 7 — Feature toggle OFF (with existing wallet): With web3auth-desktop.rollout disabled, the WalletType screen is reachable but the "Sign in with social" button is NOT shown. An already-loaded Web3Auth wallet can still be unlocked from LockedWallet with PIN.

Cross-platform parity

  • Scenario 8 — Key portability: Logging in via Web3Auth on desktop and mobile with the same Google account yields the same Hathor address on testnet.

Mainnet placeholder

  • Scenario 9 — Mainnet (when provisioned): With Hathor network set to mainnet, attempting social login surfaces "Sign-in temporarily unavailable" until the Web3Auth Mainnet project is provisioned. Once provisioned, Scenario 1 passes on mainnet.

Single-key UI invariants

  • Scenario 10: When signed in via Web3Auth:
    • Reown / WalletConnect entry is NOT visible in Settings.
    • Atomic Swap is NOT visible in top Navigation.
    • Nano Contract is NOT visible in top Navigation.
    • "Generate new address" button is NOT shown on Receive.
    • The Settings reset row label reads "Sign out of your Hathor account <email>" (not "Reset wallet").

Build / packaging

  • npm run build-css && npm run build-js produces a green production bundle.
  • npm run locale-update-pot succeeds (no ttag LogicalExpression rejections).
  • Packaged Electron build (npm run electron-pack-mac) completes the Web3Auth happy path — confirms the deferred T0 spike (postMessage origin behavior between the popup BrowserWindow and the parent renderer).

Security checklist

  • New production dependencies introduced (Web3Auth SDK chain): @web3auth/no-modal@^8.12.4, @web3auth/base@^9.7.0, @web3auth/base-provider@^9.7.0, @web3auth/auth-adapter@^9.7.0. These are required for the OAuth + MFA + key-derivation flow; the same versions are used by the mobile wallet. The adapter is pinned to @9 to match the renamed WALLET_ADAPTERS.AUTH constant in @web3auth/base@9 (using @web3auth/openlogin-adapter@8 triggers a runtime Wallet is not found for undefined crash).
  • @hathor/wallet-lib is yalc-linked for now and MUST be switched to @hathor/wallet-lib@^3.2.0 (published from HathorNetwork/hathor-wallet-lib#1093) before this PR is merged.
  • Electron popup hardening: setWindowOpenHandler allow-list is scoped to Web3Auth + Google + Apple OAuth hosts; popup runs with contextIsolation: true, sandbox: true, in the persist:web3auth partition.
  • Encrypted private key at rest: singleKeyPrivateKey is encrypted before being persisted via LOCAL_STORE.initWeb3AuthStorage; PIN entry is required to decrypt on every unlock.
  • MFA is mandatory on first login — delegated to the Web3Auth-hosted setup webview, not bypassable from desktop code.
  • No new secrets in source: all Web3Auth client IDs live in WEB3AUTH_CONFIG (src/constants.js); mainnet entries are intentionally null until provisioned.

References

Summary by CodeRabbit

Release Notes

  • New Features

    • Added social login via Web3Auth, enabling sign-in with Google, Apple, or email accounts
    • Introduced single-key wallet mode for Web3Auth users with simplified address management
    • Added sign-out functionality for social login accounts
    • Added Web3Auth recovery and error handling screens
  • Chores

    • Updated security policies and dependencies for Web3Auth integration
    • Added manual QA testing documentation

Review Change Stack

Adds a third option on Welcome → WalletType ("Sign in with social") that
bootstraps a single-key Hathor wallet from a Web3Auth-derived private key,
delegating OAuth + mandatory MFA to the Web3Auth-hosted webview opened in
an Electron BrowserWindow popup.

Scope (PR1 of three):
- Web3Auth no-modal SDK + auth-adapter, lazy singleton in sagas/web3auth.js.
- Network-aware config (testnet Sapphire Devnet today; mainnet placeholder).
- Reuses the Hathor custom verifier (hathor-google) so the derived key is
  the same across desktop and mobile. Apple gated on appleClientId.
- New Web3AuthLogin screen (Google + Apple + Email cards; Email inert until
  the JWT verifier ships in PR3).
- ChoosePin (Signin) Web3Auth branch persists encrypted privateKey via
  LOCAL_STORE.initWeb3AuthStorage; subsequent unlocks are PIN-only.
- startWallet saga branch constructs HathorWallet({privateKey, scanPolicy:
  SINGLE_ADDRESS}), forces useWalletService=false, registers external
  signer.
- LockedWallet, reset, Reown, Navigation, Settings, WalletAddress gated on
  isSingleKeyWallet selector so single-key UI invariants hold.
- Web3AuthErrorDialog classifies SDK errors into 7 buckets with localized
  copy and a Try-again affordance.
- Electron main process whitelists Web3Auth + Google + Apple OAuth hosts via
  setWindowOpenHandler with sandboxed contextIsolation partition.
- Webpack node:crypto + webcrypto polyfill (NodeProtocolUrlPlugin + custom
  ESM crypto shim) — required by the Web3Auth SDK chain.
- Gated by web3auth-desktop.rollout Unleash flag, default false.
- QA scenarios documented in qa/QA.md (10 cases).

Wallet-lib: this PR currently consumes @hathor/wallet-lib via yalc from
HathorNetwork/hathor-wallet-lib#1093. It MUST switch to the published
@hathor/wallet-lib@^3.2.0 before merge.

Spec: docs/superpowers/specs/2026-05-22-web3auth-desktop-design.md
Plan: docs/superpowers/plans/2026-05-22-web3auth-pr1-google-apple-login.md
@coderabbitai

coderabbitai Bot commented May 25, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR integrates Web3Auth social login into the Hathor Desktop Wallet, enabling users to sign in via Google or Apple accounts instead of managing seed phrases. The implementation includes webpack configuration for Web3Auth SDK compatibility, a new authentication flow with error handling, Redux state management for wallet type and email, storage initialization for single-key wallets, login and recovery screens, app routing integration, feature gating of advanced features for single-key wallets, and hardened security policies for OAuth popups and bundled dependencies.

Changes

Web3Auth Integration

Layer / File(s) Summary
Webpack & Crypto Shim Setup
config-overrides.js, src/web3auth-crypto-shim.js
NodeProtocolUrlPlugin enables webpack to resolve node: scheme imports; a new crypto shim re-exports crypto-browserify alongside webcrypto to align with Web3Auth SDK expectations and Electron renderer security.
Constants, Configuration & Redux State
src/constants.js, src/actions/index.js, src/reducers/index.js
Feature toggle WEB3AUTH_FEATURE_TOGGLE defaults to false, testnet/mainnet configuration is defined, Redux actions manage walletType and web3authEmail, and a selector isSingleKeyWallet identifies Web3Auth wallets.
Web3Auth SDK & Authentication Logic
src/sagas/web3auth.js
Lazily initialized singleton Web3Auth SDK with Google/Apple/Email providers and mandatory MFA; exports login routine (secp256k1 private key retrieval), error classification, public key derivation, state persistence, and logout/cleanup functions.
Storage & Wallet Initialization
src/storage.js, src/sagas/wallet.js
LocalStorageStore.initWeb3AuthStorage generates and persists single-key wallet access data; startWallet detects Web3Auth wallets, restores credentials from storage, forces single-address mode, and registers external signing via transactionUtils.getSignatureForTx.
Login UI Screens & Error Dialog
src/screens/Web3AuthLogin.js, src/screens/Web3AuthRecovery.js, src/components/Web3AuthErrorDialog.js
Web3AuthLogin renders provider cards (Google enabled, Apple conditional, Email disabled) and navigates to Signin on success; Web3AuthRecovery shows recovery portal reference; error dialog maps error types to localized copy with retry/cancel paths.
App Routing & State Restoration
src/App.js
Routes /web3auth_login/ and /web3auth_recovery/ are registered; a useEffect on Root app boot dispatches restoreWeb3AuthState to recover persisted wallet type and email from localStorage.
Signin Flow & Wallet Type Screen Integration
src/screens/Signin.js, src/screens/WalletType.js
Signin accepts Web3Auth state in location and branches to PIN/password entry before wallet initialization; WalletType shows "Sign in with social" button when feature toggle is enabled, routing to /web3auth_login/.
Feature Gating & Component Modifications
src/components/Navigation.js, src/components/WalletAddress.js, src/screens/Settings.js, src/screens/LockedWallet.js, src/sagas/reown.js
Nano contracts and atomic swap navigation are hidden for Web3Auth wallets; address generation/QR UI is gated; Settings shows "Sign out" text with email for Web3Auth; Reown is disabled; all changes use isSingleKeyWallet selector.
Security & Electron/Lavamoat Integration
public/electron.js, lavamoat/webpack/policy.json
Electron restricts window opening to Web3Auth/Google/Apple hosts with isolated partition and hardened browser options; Lavamoat policy expanded with Web3Auth/TorusLabs dependency resource allowlists covering required globals (TextEncoder, crypto, timers, localStorage, socket.io) and sub-package wiring.
Styling & Localization
src/styles/web3auth.scss, locale/texts.pot, qa/QA.md, src/index.module.scss
SCSS adds .web3auth-login flex layout with card styles and divider UI; translation catalog updated with Web3Auth messages (sign-in, recovery, errors, providers); QA checklist covers social login scenarios, restart/unlock, offline, feature toggle paths, and single-key UI invariants.
Dependency Management
package.json
@hathor/wallet-lib switched to local Yalc development build; @web3auth/* packages pinned with semver ranges in resolutions.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant WalletType
  participant Web3AuthLogin
  participant Web3AuthSDK as Web3Auth SDK
  participant Signin
  participant HathorWallet as Wallet Storage
  participant App

  User->>WalletType: Click "Sign in with social"
  WalletType->>Web3AuthLogin: Navigate to /web3auth_login/
  User->>Web3AuthLogin: Select provider (Google/Apple)
  Web3AuthLogin->>Web3AuthSDK: web3authLogin(provider)
  Web3AuthSDK->>User: OAuth popup (isolated partition)
  User->>Web3AuthSDK: Authenticate, approve MFA
  Web3AuthSDK->>Web3AuthLogin: Return privateKey & email
  Web3AuthLogin->>Web3AuthLogin: Derive publicKey, persistWeb3AuthState
  Web3AuthLogin->>Signin: Navigate with privateKey, publicKey, email
  Signin->>User: Request PIN & password
  User->>Signin: Enter PIN & password
  Signin->>HathorWallet: initWeb3AuthStorage(privateKey, publicKey, pin, password)
  HathorWallet->>Signin: Storage initialized
  Signin->>App: startWalletRequested({walletType: 'web3auth'})
  App->>App: Wallet unlocked, address mode forced to SINGLE
  App->>User: Show wallet home, hide nano/atomic-swap nav
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • HathorNetwork/hathor-wallet#852: Modifies src/sagas/reown.js Reown integration; this PR adds Web3Auth wallet-type disablement to isReownEnabled, while the related PR adjusts Reown request/modals flow.
  • HathorNetwork/hathor-wallet#861: Modifies src/components/WalletAddress.js address UI gating; this PR uses isWeb3Auth selector while the related PR uses Redux addressMode for similar single-address UI constraints.

Suggested reviewers

  • tuliomir
  • pedroferreira1

🐰 A wallet born from social signs,
No seed phrase needed—just a smile!
Google or Apple brings you wings,
Web3Auth makes auth worthwhile. ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and clearly describes the main feature being added: Web3Auth-based social login with Google and Apple OAuth providers.
Docstring Coverage ✅ Passed Docstring coverage is 89.47% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch raul-oliveira/feat/web3auth-social-login

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@raul-oliveira
raul-oliveira marked this pull request as draft May 25, 2026 16:54
@raul-oliveira raul-oliveira self-assigned this May 25, 2026
@raul-oliveira raul-oliveira moved this from Todo to In Progress (WIP) in Hathor Network May 25, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🧹 Nitpick comments (4)
src/sagas/web3auth.js (2)

174-176: 💤 Low value

The email variable may contain a non-email value.

The fallback chain userInfo?.email || userInfo?.name || '' means email could contain a display name when the OAuth provider doesn't return an email. This value propagates to WEB3AUTH_EMAIL_KEY storage and web3authEmail Redux state, which could confuse users if displayed in the UI as an email address.

Consider either:

  1. Renaming to userIdentifier to reflect the actual semantics, or
  2. Only storing a value when userInfo?.email is truthy
♻️ Option 2: Only persist actual email
   const userInfo = await web3auth.getUserInfo();
-  const email = userInfo?.email || userInfo?.name || '';
+  const email = userInfo?.email || '';
   const privateKey = await web3auth.provider.request({ method: 'private_key' });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/sagas/web3auth.js` around lines 174 - 176, The current fallback assigns
userInfo?.name into the email variable, causing non-email values to be stored
and dispatched; change the logic so email is only set from userInfo?.email
(remove the fallback to userInfo?.name) and only write to WEB3AUTH_EMAIL_KEY and
dispatch the web3authEmail Redux action when that email value is truthy, or
alternatively rename the variable and related storage/dispatch keys to
userIdentifier/web3authIdentifier if you intend to keep the display name; update
references around userInfo, email, WEB3AUTH_EMAIL_KEY, and web3authEmail
accordingly.

23-68: 💤 Low value

Error classification relies on string matching.

The approach is documented as necessary given SDK limitations, but new SDK versions may introduce different error message patterns. Consider adding structured logging when UNKNOWN is returned to surface unrecognized patterns for future classification refinement.

🔍 Suggested enhancement for observability
+  // Log unclassified errors to help identify new patterns for future SDK updates
+  if (result === WEB3AUTH_ERROR_TYPES.UNKNOWN && err) {
+    // eslint-disable-next-line no-console
+    console.warn('[web3auth] Unclassified error pattern:', msg);
+  }
   return WEB3AUTH_ERROR_TYPES.UNKNOWN;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/sagas/web3auth.js` around lines 23 - 68, classifyWeb3AuthError currently
falls back to WEB3AUTH_ERROR_TYPES.UNKNOWN for unrecognized messages but doesn't
surface the raw pattern; update classifyWeb3AuthError to emit structured
observability data whenever it will return UNKNOWN: log the original error
object, its stack (if present), and the normalized msg string so new patterns
can be triaged and added later. Use the existing application logger (or console
as a fallback) and include clear fields like errorType: 'web3auth_unclassified',
classified: 'unknown', rawError, stack, and normalizedMessage before returning
WEB3AUTH_ERROR_TYPES.UNKNOWN to make future classification straightforward.
qa/QA.md (1)

65-74: 💤 Low value

Consider splitting the alternate verification into a separate note.

Scenario 7, step 4 embeds a conditional verification ("But if the user had NOT signed out...") within the main test flow. This makes the scenario harder to execute linearly. Consider moving this alternate case to a separate note or sub-scenario for clarity.

📝 Optional refactor
 3. Open the app — the WalletType screen is reachable, and the `Sign in with social`
    button is NOT shown.
-4. Sign in with social again is not possible. But if the user had NOT signed out, the
-   LockedWallet screen would still be reachable and the wallet could be unlocked with
-   PIN. (Verify by retesting with a wallet that was not signed out.)
+4. Sign in with social again is not possible.
+
+**Note:** If the feature toggle is disabled while a Web3Auth wallet is locked (not signed out), the LockedWallet screen remains reachable and the wallet can still be unlocked with PIN. Verify this separately by locking without signing out before disabling the toggle.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@qa/QA.md` around lines 65 - 74, Split the conditional verification embedded
in Scenario 7 step 4 into a separate note or sub-scenario: remove the
parenthetical sentence starting "But if the user had NOT signed out..." from the
main flow of "Scenario 7 — Feature toggle off (with existing wallet)" and add a
new sub-scenario or note titled e.g. "Alternate: wallet not signed out" that
describes retesting the LockedWallet screen and unlocking with PIN; reference
the same artifacts (WalletType screen, LockedWallet screen, and the
web3auth-desktop.rollout toggle) so the alternate path is executed independently
and the main scenario remains linear.
lavamoat/webpack/policy.json (1)

166-173: 💤 Low value

Reassess $root$ for @noble/hashes—it’s scoped and likely generator-driven

  • In lavamoat/webpack/policy.json, @web3auth/...>@noble/curves>@noble/hashes is the only `@noble/hashes` entry that gets `"$root$": true`; the direct `@web3auth/...>`@noble/hashes entry only gets TextEncoder.
  • config-overrides.js enables new LavaMoatPlugin({ generatePolicy: true, ... }) in production and doesn’t reference policy-override.json, so this $root$ permission is very likely coming from LavaMoat’s policy generation rather than a manual override.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lavamoat/webpack/policy.json` around lines 166 - 173, The policy currently
grants "$root$": true for the chain
"`@web3auth/base`>`@web3auth/auth`>`@ethereumjs/util`>ethereum-cryptography>`@noble/curves`>`@noble/hashes`"
in lavamoat/webpack/policy.json—reassess and remove or tighten this "$root$"
package permission and instead enumerate only required package keys (or set more
restrictive package scopes) for the `@noble/hashes` entry; if the "$root$" was
introduced by LavaMoat’s generator, update config-overrides.js (LavaMoatPlugin
generatePolicy) or add an explicit policy-override to prevent generator from
adding global root access, then regenerate the policy via the LavaMoatPlugin
build to verify the "`@web3auth/`...>`@noble/hashes`" entry only has the minimal
"globals": { "TextEncoder": true } or the intended restricted packages.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@locale/texts.pot`:
- Around line 3185-3188: The message for WEB3AUTH_ERROR_TYPES.VERIFIER_CONFIG in
Web3AuthErrorDialog.js currently implies the Hathor team was notified but only
issues console.warn; either remove/soften that claim in the i18n string
(texts.pot) so it doesn't promise notification, or emit an explicit Sentry event
when the dialog is shown (call Sentry.captureException or Sentry.captureMessage
with contextual info) from the Web3AuthErrorDialog component after logging the
console.warn; update the string and/or add the
Sentry.captureMessage(SOME_DESCRIPTIVE_TAGGED_MESSAGE, { extra: { errorType:
WEB3AUTH_ERROR_TYPES.VERIFIER_CONFIG, ... } }) call to ensure an event is
recorded when Sentry is initialized.

In `@package.json`:
- Around line 48-51: package.json currently mixes Web3Auth 9.x packages
("`@web3auth/auth-adapter`","`@web3auth/base`","`@web3auth/base-provider`") with an
8.x "`@web3auth/no-modal`", causing peer-dep/runtime conflicts; fix by aligning
major versions: update the "`@web3auth/no-modal`" dependency to a 9.x release that
matches the others (or alternatively downgrade the 9.x entries to 8.x if you
must stay on v8), then run install and verify no peer dependency warnings and
that the login flow using the auth adapter/base/base-provider works as expected;
target the same major (e.g., set "`@web3auth/no-modal`" to "^9.7.0" to match
"`@web3auth/base`" and "`@web3auth/auth-adapter`").
- Line 41: Replace the local yalc dependency for `@hathor/wallet-lib` in
package.json (currently "file:.yalc/@hathor/wallet-lib") with the published
semver version "^3.1.1", then run npm install (or npm ci after removing
node_modules) to regenerate package-lock.json so it no longer references .yalc;
also search package-lock.json and the repo for any remaining
".yalc/@hathor/wallet-lib" entries and remove/replace them with
"`@hathor/wallet-lib`": "^3.1.1" to ensure clean reproducible installs.

In `@qa/QA.md`:
- Line 98: Replace the HTML-encoded placeholder `&lt;email&gt;` in QA.md with a
proper Markdown-safe representation: either plain angle brackets `<email>` or,
preferably for inline code style, use backticks `` `<email>` ``; update the
occurrence of `&lt;email&gt;` so the Markdown renders correctly and search for
other HTML-encoded entities to fix similarly.

In `@src/index.module.scss`:
- Line 1323: Import statement "`@import` 'styles/web3auth';" is currently placed
in an invalid position; move that `@import` so it appears before any other rules
(variables, mixins, selectors, or declarations)—ideally grouped with the other
top-of-file imports at the start of the stylesheet. Remove the offending line at
its current location and reinsert the same "`@import` 'styles/web3auth';" at the
top of the file (or into a consolidated imports section) to satisfy the
stylesheet rule order (no-invalid-position-at-import-rule).

In `@src/screens/Signin.js`:
- Around line 85-101: pinSuccess currently performs async setup
(LOCAL_STORE.unlock, initWeb3AuthStorage, markBackupDone, LOCAL_STORE.open,
dispatch(startWalletRequested...), setPassword, navigate) with no error
handling; wrap the async sequence in a try/catch and only clear password,
dispatch startWalletRequested, and navigate after all steps succeed. On failure
in initWeb3AuthStorage or LOCAL_STORE.open, catch the error, log/report it,
undo/rollback any partial state (e.g. if LOCAL_STORE.unlock() succeeded ensure
you call LOCAL_STORE.lock() or another cleanup method and avoid calling
markBackupDone/open/dispatch), and surface a user-visible error instead of
navigating. Ensure sensitive values (password/privateKey) are only cleared after
successful completion or securely handled on rollback.
- Around line 91-97: The dispatched startWalletRequested currently includes
secrets (privateKey, pin, password) which are saved into state.startWalletAction
by the START_WALLET_REQUESTED handler and replayed by LoadWalletFailed; change
the flow to avoid storing secrets in Redux: update the START_WALLET_REQUESTED
reducer to store only a sanitized summary (e.g., walletType, publicKey, flags)
not the full action, and ensure the reducer clears state.startWalletAction on
START_WALLET_FAILED as well as START_WALLET_SUCCESS; update LoadWalletFailed to
re-dispatch a reconstructed, non-sensitive retry (or prompt the UI to re-enter
secrets) instead of replaying the original action; alternatively, keep secrets
out of the dispatched payload by moving privateKey/pin/password into local
component state or passing them directly to the saga middleware
(startWalletRequested metadata or a secure channel) so startWalletRequested,
reducers, and LoadWalletFailed never persist secrets.

In `@src/screens/Web3AuthLogin.js`:
- Around line 43-46: The persisted network value from
LOCAL_STORE.getNetworkSettings() (persistedNetwork / network) must be normalized
against known keys in WEB3AUTH_CONFIG before being used in the login flow;
change the code that derives cfg to also produce a normalizedNetwork (e.g., pick
network if it exists in WEB3AUTH_CONFIG, otherwise use 'testnet' or 'mainnet'
fallback) and pass that normalizedNetwork to web3authLogin instead of the raw
network value so UI gating (appleEnabled / cfg) and runtime config remain
consistent; update references to persistedNetwork/network, cfg, and the
web3authLogin call to use this normalized value.

In `@src/storage.js`:
- Around line 385-389: getWeb3AuthPublicKey currently calls this.getStorage()
without guarding against a null return like getWeb3AuthPrivateKey does; update
getWeb3AuthPublicKey to first call const storage = this.getStorage() and if
storage is null/undefined return null (or the same fallback used in
getWeb3AuthPrivateKey), otherwise await storage.getAccessData() and return
accessData?.singleKeyPublicKey so it mirrors the null-safety of
getWeb3AuthPrivateKey.
- Around line 375-378: The getWeb3AuthPrivateKey function calls
this.getStorage() and directly invokes storage.getSingleKeyPrivateKey(pin) which
will throw if getStorage() returns null; add a null check for the result of
this.getStorage() (similar to the pattern used in _getAccessData) and handle the
missing storage case by returning null or throwing a clear error, e.g., if
(!storage) return null or throw new Error('wallet not loaded'), before calling
storage.getSingleKeyPrivateKey(pin).

---

Nitpick comments:
In `@lavamoat/webpack/policy.json`:
- Around line 166-173: The policy currently grants "$root$": true for the chain
"`@web3auth/base`>`@web3auth/auth`>`@ethereumjs/util`>ethereum-cryptography>`@noble/curves`>`@noble/hashes`"
in lavamoat/webpack/policy.json—reassess and remove or tighten this "$root$"
package permission and instead enumerate only required package keys (or set more
restrictive package scopes) for the `@noble/hashes` entry; if the "$root$" was
introduced by LavaMoat’s generator, update config-overrides.js (LavaMoatPlugin
generatePolicy) or add an explicit policy-override to prevent generator from
adding global root access, then regenerate the policy via the LavaMoatPlugin
build to verify the "`@web3auth/`...>`@noble/hashes`" entry only has the minimal
"globals": { "TextEncoder": true } or the intended restricted packages.

In `@qa/QA.md`:
- Around line 65-74: Split the conditional verification embedded in Scenario 7
step 4 into a separate note or sub-scenario: remove the parenthetical sentence
starting "But if the user had NOT signed out..." from the main flow of "Scenario
7 — Feature toggle off (with existing wallet)" and add a new sub-scenario or
note titled e.g. "Alternate: wallet not signed out" that describes retesting the
LockedWallet screen and unlocking with PIN; reference the same artifacts
(WalletType screen, LockedWallet screen, and the web3auth-desktop.rollout
toggle) so the alternate path is executed independently and the main scenario
remains linear.

In `@src/sagas/web3auth.js`:
- Around line 174-176: The current fallback assigns userInfo?.name into the
email variable, causing non-email values to be stored and dispatched; change the
logic so email is only set from userInfo?.email (remove the fallback to
userInfo?.name) and only write to WEB3AUTH_EMAIL_KEY and dispatch the
web3authEmail Redux action when that email value is truthy, or alternatively
rename the variable and related storage/dispatch keys to
userIdentifier/web3authIdentifier if you intend to keep the display name; update
references around userInfo, email, WEB3AUTH_EMAIL_KEY, and web3authEmail
accordingly.
- Around line 23-68: classifyWeb3AuthError currently falls back to
WEB3AUTH_ERROR_TYPES.UNKNOWN for unrecognized messages but doesn't surface the
raw pattern; update classifyWeb3AuthError to emit structured observability data
whenever it will return UNKNOWN: log the original error object, its stack (if
present), and the normalized msg string so new patterns can be triaged and added
later. Use the existing application logger (or console as a fallback) and
include clear fields like errorType: 'web3auth_unclassified', classified:
'unknown', rawError, stack, and normalizedMessage before returning
WEB3AUTH_ERROR_TYPES.UNKNOWN to make future classification straightforward.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2d1fe5e3-4af3-4b23-8dc4-f40022f6764f

📥 Commits

Reviewing files that changed from the base of the PR and between 23f902a and 987e0ae.

⛔ Files ignored due to path filters (4)
  • package-lock.json is excluded by !**/package-lock.json
  • src/assets/web3auth-providers/apple.svg is excluded by !**/*.svg
  • src/assets/web3auth-providers/email.svg is excluded by !**/*.svg
  • src/assets/web3auth-providers/google.svg is excluded by !**/*.svg
📒 Files selected for processing (26)
  • config-overrides.js
  • lavamoat/webpack/policy.json
  • locale/texts.pot
  • package.json
  • public/electron.js
  • qa/QA.md
  • src/App.js
  • src/actions/index.js
  • src/components/Navigation.js
  • src/components/WalletAddress.js
  • src/components/Web3AuthErrorDialog.js
  • src/constants.js
  • src/index.module.scss
  • src/reducers/index.js
  • src/sagas/reown.js
  • src/sagas/wallet.js
  • src/sagas/web3auth.js
  • src/screens/LockedWallet.js
  • src/screens/Settings.js
  • src/screens/Signin.js
  • src/screens/WalletType.js
  • src/screens/Web3AuthLogin.js
  • src/screens/Web3AuthRecovery.js
  • src/storage.js
  • src/styles/web3auth.scss
  • src/web3auth-crypto-shim.js

Comment thread locale/texts.pot
Comment on lines +3185 to +3188
msgid ""
"There is a configuration issue with Web3Auth. The Hathor team has been "
"notified."
msgstr ""

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if CONFIG_ERROR triggers Sentry capture in the Web3Auth saga

# Search for Sentry.captureException or similar in web3auth saga error handling
rg -n -C5 'CONFIG_ERROR' --type=js src/sagas/web3auth.js

# Also check if this error type is captured in error boundary or middleware
rg -n 'CONFIG_ERROR.*Sentry|Sentry.*CONFIG_ERROR' --type=js

Repository: HathorNetwork/hathor-wallet

Length of output: 53


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Find mapping to the notified copy =="

rg -n --hidden --no-ignore -S "The Hathor team has been notified" . || true
rg -n --hidden --no-ignore -S "\"There is a configuration issue with Web3Auth" . || true

echo
echo "== Locate CONFIG_ERROR definition/usages (repo-wide) =="

rg -n --hidden --no-ignore -S "CONFIG_ERROR" . || true

echo
echo "== Locate Web3Auth saga/error handling files =="

rg -n --hidden --no-ignore -S "web3auth" src . || true
rg -n --hidden --no-ignore -S "Web3Auth" src . || true

echo
echo "== Search for Sentry capture in Web3Auth-related code =="

rg -n --hidden --no-ignore -S "Sentry\.capture|captureException|captureMessage" src . || true

Repository: HathorNetwork/hathor-wallet

Length of output: 50386


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Search for CONFIG_ERROR only in src/ =="
rg -n "CONFIG_ERROR" src || true

echo
echo "== Search for Sentry only in src/ =="
rg -n -S "Sentry|sentry" src || true

echo
echo "== Inspect Web3AuthErrorDialog.js around the string =="
FILE="src/components/Web3AuthErrorDialog.js"
if [ -f "$FILE" ]; then
  nl -ba "$FILE" | sed -n '1,120p'
else
  echo "Missing $FILE"
fi

Repository: HathorNetwork/hathor-wallet

Length of output: 5651


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="src/components/Web3AuthErrorDialog.js"
echo "== Web3AuthErrorDialog.js (key sections) =="
awk 'NR>=1 && NR<=140 {printf "%5d:%s\n", NR, $0}' "$FILE"

echo
echo "== Find where sentryTag / category is used =="
rg -n "sentryTag|category: copy\.sentryTag|breadcrumbs|breadcrumb|captureException|captureMessage" src/components src/utils src/screens || true

echo
echo "== Find any logger that handles category tags ==" 
rg -n "existing logger|observability hook|tagged log|category:" src || true

Repository: HathorNetwork/hathor-wallet

Length of output: 6298


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Locate Web3Auth saga files =="
ls -la src/sagas | head
rg -n "WEB3AUTH_ERROR_TYPES|VERIFIER_CONFIG|web3auth" src/sagas --type=js || true

echo
echo "== Search for Sentry capture / sentryWithScope in saga/util =="
rg -n -S "sentryWithScope|Sentry\.captureException|captureException\(" src/sagas src/utils || true

echo
echo "== Inspect initSentry in src/utils/wallet.js =="
rg -n "initSentry|Sentry\.init" src/utils/wallet.js
awk 'NR>=440 && NR<=520 {printf "%5d:%s\n", NR, $0}' src/utils/wallet.js

echo
echo "== Inspect src/sagas/web3auth around VERIFIER_CONFIG usage =="
FILE=$(fd -t f "web3auth.js" src/sagas || true | head -n 1)
if [ -n "$FILE" ]; then
  echo "Using $FILE"
  rg -n "VERIFIER_CONFIG|WEB3AUTH_ERROR_TYPES" "$FILE" || true
  awk 'NR>=1 && NR<=220 {printf "%5d:%s\n", NR, $0}' "$FILE"
else
  echo "Could not find src/sagas/web3auth.js"
fi

Repository: HathorNetwork/hathor-wallet

Length of output: 14633


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Find where Web3AuthErrorDialog is used =="
rg -n "Web3AuthErrorDialog" src --type=js || true

echo
echo "== Search for classifyWeb3AuthError usage =="
rg -n "classifyWeb3AuthError" src/sagas --type=js || true

echo
echo "== Search for VERIFIER_CONFIG usage in web3auth saga =="
rg -n "VERIFIER_CONFIG|verifier_config" src/sagas/web3auth.js --type=js || true

echo
echo "== Search for Sentry capture in web3auth saga =="
rg -n -S "Sentry\.capture|captureException|sentryWithScope" src/sagas/web3auth.js --type=js || true

echo
echo "== Inspect rest of src/sagas/web3auth.js for error handling/dialog dispatch =="
awk 'NR>=220 && NR<=520 {printf "%5d:%s\n", NR, $0}' src/sagas/web3auth.js

Repository: HathorNetwork/hathor-wallet

Length of output: 2237


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== web3authLogin call sites (src/) =="
rg -n "web3authLogin\\(" src --type=js || true

echo
echo "== classifyWeb3AuthError usage (src/) =="
rg -n "classifyWeb3AuthError" src --type=js || true

echo
echo "== Inspect src/screens/Web3AuthLogin.js around the error dialog =="
FILE="src/screens/Web3AuthLogin.js"
wc -l "$FILE"
awk 'NR>=90 && NR<=200 {printf "%5d:%s\n", NR, $0}' "$FILE"

echo
echo "== Search for verifier_config / VERIFIER_CONFIG in src/ =="
rg -n "VERIFIER_CONFIG|verifier_config" src --type=js || true

echo
echo "== Search for sentryWithScope mentions near web3auth =="
rg -n "sentryWithScope" src --type=js || true
rg -n -S "web3auth|web3authLogin|Web3Auth" src --type=js | head -n 50 || true

Repository: HathorNetwork/hathor-wallet

Length of output: 7227


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Web3AuthLogin.js: inspect top + login/catch logic =="
FILE="src/screens/Web3AuthLogin.js"
wc -l "$FILE"
awk 'NR>=1 && NR<=120 {printf "%5d:%s\n", NR, $0}' "$FILE"

echo
echo "== Search for web3auth dialog console.warn message string =="
rg -n "web3auth\\] error dialog shown" src --type=js || true

echo
echo "== Search for sentryWithScope call sites in src (all) =="
rg -n "sentryWithScope\\(" src --type=js || true

echo
echo "== Search for Sentry breadcrumb setup / addBreadcrumb in src =="
rg -n -S "addBreadcrumb|breadcrumbs|beforeSend|enableConsole|console" src/utils src --type=js || true

Repository: HathorNetwork/hathor-wallet

Length of output: 12510


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Sentry permission gating (wallet.js) =="
awk 'NR>=390 && NR<=520 {printf "%5d:%s\n", NR, $0}' src/utils/wallet.js

echo
echo "== Where updateSentryState() is called =="
rg -n "updateSentryState\\(" src --type=js || true

Repository: HathorNetwork/hathor-wallet

Length of output: 4180


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Sentry init occurrences (src/) =="
rg -n "Sentry\\.init" src --type=js || true

echo
echo "== Sentry capture occurrences (src/) =="
rg -n -S "Sentry\\.(captureException|captureMessage|captureEvent)" src --type=js || true

echo
echo "== Sentry breadcrumb / console integration hints (src/) =="
rg -n -S "breadcrumb|breadcrumbs|beforeSend|integrations|Console|console\\.(warn|log)" src --type=js || true

Repository: HathorNetwork/hathor-wallet

Length of output: 1943


🌐 Web query:

Sentry browser SDK default Console integration captures console.warn as breadcrumbs? default integrations when calling Sentry.init({dsn, release}) without integrations option

💡 Result:

Yes. In the Sentry Browser JavaScript SDK, console.warn is captured as breadcrumbs by the default Breadcrumbs integration (enabled by default) when you call Sentry.init({ dsn, release }) without specifying integrations. 1) The Breadcrumbs integration is enabled by default and wraps the browser console API to add breadcrumbs. - The integration documentation states it “is enabled by default” and that “By default, the Sentry SDK wraps the console … browser APIs to add breadcrumbs.”[1] 2) Those console calls include warn (not only log). - The Breadcrumbs integration doc describes the console option as “Log calls to console.log, console.debug, and so on.”[1] - The SDK source for the Breadcrumbs integration shows _options.console defaults to true and, when enabled, it installs console breadcrumb instrumentation via addConsoleInstrumentationHandler(...). This is the default behavior when you don’t pass an integrations option.[2] 3) Therefore, calling Sentry.init({ dsn, release }) with no integrations option will include the default Breadcrumbs integration, which captures console API calls (including console.warn) as breadcrumbs. - The Breadcrumbs integration doc explicitly ties breadcrumb capture to the default wrapping of console.[1] 4) If you want to stop capturing console calls as breadcrumbs, you must disable/override the Breadcrumbs integration’s console option. - The integrations docs show you can turn off browser capturing console calls by setting console: false in the Breadcrumbs integration instance you pass to integrations.[3] If you tell me your exact SDK package name/version (e.g., @sentry/browser vs sentry-cordova) and how you’re calling Sentry.init, I can map this to the precise integration name you’ll see in your build output.

Citations:


Fix “Hathor team has been notified” wording for Web3Auth config errors

The “configuration issue” copy is for WEB3AUTH_ERROR_TYPES.VERIFIER_CONFIG in src/components/Web3AuthErrorDialog.js, but this dialog only logs a tagged console.warn and never calls Sentry.captureException/Sentry.captureMessage. While Sentry can record console.warn as breadcrumbs (via default Breadcrumbs), breadcrumbs don’t by themselves send a notification/event to Sentry; also Sentry is only initialized when the user allows it. Update the copy to avoid guaranteeing notification, or emit an explicit Sentry event when showing this dialog.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@locale/texts.pot` around lines 3185 - 3188, The message for
WEB3AUTH_ERROR_TYPES.VERIFIER_CONFIG in Web3AuthErrorDialog.js currently implies
the Hathor team was notified but only issues console.warn; either remove/soften
that claim in the i18n string (texts.pot) so it doesn't promise notification, or
emit an explicit Sentry event when the dialog is shown (call
Sentry.captureException or Sentry.captureMessage with contextual info) from the
Web3AuthErrorDialog component after logging the console.warn; update the string
and/or add the Sentry.captureMessage(SOME_DESCRIPTIVE_TAGGED_MESSAGE, { extra: {
errorType: WEB3AUTH_ERROR_TYPES.VERIFIER_CONFIG, ... } }) call to ensure an
event is recorded when Sentry is initialized.

Comment thread package.json
"dependencies": {
"@hathor/hathor-rpc-handler": "4.4.0",
"@hathor/wallet-lib": "3.1.1",
"@hathor/wallet-lib": "file:.yalc/@hathor/wallet-lib",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Checking current declaration in package.json..."
python - <<'PY'
import json, pathlib
pkg = json.loads(pathlib.Path("package.json").read_text())
print(pkg["dependencies"].get("`@hathor/wallet-lib`"))
PY

echo
echo "Searching repo for lingering yalc refs..."
rg -n "file:\.yalc/@hathor/wallet-lib|\.yalc/@hathor/wallet-lib" package.json package-lock.json yarn.lock pnpm-lock.yaml 2>/dev/null || true

echo
echo "Verifying published target exists..."
npm view `@hathor/wallet-lib`@^3.2.0 version

Repository: HathorNetwork/hathor-wallet

Length of output: 1050


🏁 Script executed:

rg -n "file:\.yalc/@hathor/wallet-lib|\.yalc/@hathor/wallet-lib" package.json package-lock.json yarn.lock pnpm-lock.yaml 2>/dev/null || true
npm view `@hathor/wallet-lib` versions --json

Repository: HathorNetwork/hathor-wallet

Length of output: 2711


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Checking package.json declaration..."
python - <<'PY'
import json, pathlib
pkg = json.loads(pathlib.Path("package.json").read_text())
print(pkg["dependencies"].get("`@hathor/wallet-lib`"))
PY

echo
echo "Searching repo for lingering yalc refs..."
rg -n "file:\.yalc/@hathor/wallet-lib|\.yalc/@hathor/wallet-lib" package.json package-lock.json yarn.lock pnpm-lock.yaml 2>/dev/null || true

echo
echo "Verifying published target exists..."
npm view `@hathor/wallet-lib`@3.1.1 version

Repository: HathorNetwork/hathor-wallet

Length of output: 580


🏁 Script executed:

rg -n "file:\.yalc/@hathor/wallet-lib|\.yalc/@hathor/wallet-lib" package.json package-lock.json yarn.lock pnpm-lock.yaml 2>/dev/null || true
npm view `@hathor/wallet-lib`@3.1.1 version

Repository: HathorNetwork/hathor-wallet

Length of output: 432


Replace local Yalc dependency for @hathor/wallet-lib (blocker; ^3.2.0 not published, use ^3.1.1)

package.json (line 41) and package-lock.json still reference file:.yalc/@hathor/wallet-lib, which will fail clean/reproducible CI/release installs. npm does not provide @hathor/wallet-lib@^3.2.0, but @hathor/wallet-lib@3.1.1 exists.

Proposed fix
-    "`@hathor/wallet-lib`": "file:.yalc/@hathor/wallet-lib",
+    "`@hathor/wallet-lib`": "^3.1.1",

Also regenerate package-lock.json and remove any remaining .yalc/@hathor/wallet-lib entries.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"@hathor/wallet-lib": "file:.yalc/@hathor/wallet-lib",
"`@hathor/wallet-lib`": "^3.1.1",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` at line 41, Replace the local yalc dependency for
`@hathor/wallet-lib` in package.json (currently "file:.yalc/@hathor/wallet-lib")
with the published semver version "^3.1.1", then run npm install (or npm ci
after removing node_modules) to regenerate package-lock.json so it no longer
references .yalc; also search package-lock.json and the repo for any remaining
".yalc/@hathor/wallet-lib" entries and remove/replace them with
"`@hathor/wallet-lib`": "^3.1.1" to ensure clean reproducible installs.

Comment thread package.json
Comment on lines +48 to +51
"@web3auth/auth-adapter": "^9.7.0",
"@web3auth/base": "^9.7.0",
"@web3auth/base-provider": "^9.7.0",
"@web3auth/no-modal": "^8.12.4",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Declared Web3Auth versions in package.json:"
python - <<'PY'
import json, pathlib
deps = json.loads(pathlib.Path("package.json").read_text())["dependencies"]
for name in [
    "`@web3auth/auth-adapter`",
    "`@web3auth/base`",
    "`@web3auth/base-provider`",
    "`@web3auth/no-modal`",
]:
    print(f"{name}: {deps.get(name)}")
PY

echo
echo "Inspecting npm metadata for compatibility-relevant ranges:"
for p in \
  "`@web3auth/auth-adapter`@9.7.0" \
  "`@web3auth/base`@9.7.0" \
  "`@web3auth/base-provider`@9.7.0" \
  "`@web3auth/no-modal`@8.12.4"
do
  echo "---- $p ----"
  npm view "$p" peerDependencies dependencies --json
done

Repository: HathorNetwork/hathor-wallet

Length of output: 1653


Align Web3Auth packages to one major release train.
@web3auth/no-modal@^8.12.4 depends on @web3auth/base@^8.12.4 (and Torus openlogin ^8.x) while @web3auth/auth-adapter/base/base-provider are on ^9.7.0 (and use @web3auth/auth@^9.6.4 with @web3auth/base@^9.7.0). This 8.x/9.x skew is a common source of peer dependency conflicts and runtime login issues.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` around lines 48 - 51, package.json currently mixes Web3Auth 9.x
packages ("`@web3auth/auth-adapter`","`@web3auth/base`","`@web3auth/base-provider`")
with an 8.x "`@web3auth/no-modal`", causing peer-dep/runtime conflicts; fix by
aligning major versions: update the "`@web3auth/no-modal`" dependency to a 9.x
release that matches the others (or alternatively downgrade the 9.x entries to
8.x if you must stay on v8), then run install and verify no peer dependency
warnings and that the login flow using the auth adapter/base/base-provider works
as expected; target the same major (e.g., set "`@web3auth/no-modal`" to "^9.7.0"
to match "`@web3auth/base`" and "`@web3auth/auth-adapter`").

Comment thread qa/QA.md
- Nano Contract (in top navigation)
3. Open the `Receive` screen — verify the "Generate new address" button is NOT shown.
4. Settings → reset row label reads "Sign out of your Hathor account &lt;email&gt;",
not "Reset wallet".

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix HTML entity encoding in markdown.

The placeholder &lt;email&gt; uses HTML entities instead of plain angle brackets. In markdown, this should be <email> or the more conventional backtick format `<email>` for inline code.

✏️ Suggested fix
-4. Settings → reset row label reads "Sign out of your Hathor account &lt;email&gt;",
+4. Settings → reset row label reads "Sign out of your Hathor account `<email>`",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@qa/QA.md` at line 98, Replace the HTML-encoded placeholder `&lt;email&gt;` in
QA.md with a proper Markdown-safe representation: either plain angle brackets
`<email>` or, preferably for inline code style, use backticks `` `<email>` ``;
update the occurrence of `&lt;email&gt;` so the Markdown renders correctly and
search for other HTML-encoded entities to fix similarly.

Comment thread src/index.module.scss
}
}

@import 'styles/web3auth';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Move SCSS import to a valid position.

@import at Line 1323 violates the stylesheet rule order (no-invalid-position-at-import-rule) and can fail lint/pipeline.

Suggested fix
@@
 :export {
   purpleHathor: $purpleHathor;
 }
+
+@import 'styles/web3auth';
@@
-@import 'styles/web3auth';
🧰 Tools
🪛 Stylelint (17.11.1)

[error] 1323-1323: Invalid position for @import rule (no-invalid-position-at-import-rule)

(no-invalid-position-at-import-rule)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/index.module.scss` at line 1323, Import statement "`@import`
'styles/web3auth';" is currently placed in an invalid position; move that
`@import` so it appears before any other rules (variables, mixins, selectors, or
declarations)—ideally grouped with the other top-of-file imports at the start of
the stylesheet. Remove the offending line at its current location and reinsert
the same "`@import` 'styles/web3auth';" at the top of the file (or into a
consolidated imports section) to satisfy the stylesheet rule order
(no-invalid-position-at-import-rule).

Comment thread src/screens/Signin.js
Comment on lines +85 to +101
const pinSuccess = async (newPin) => {
const { privateKey, publicKey } = web3authState;
LOCAL_STORE.unlock();
await LOCAL_STORE.initWeb3AuthStorage(privateKey, publicKey, newPin, password);
LOCAL_STORE.markBackupDone();
LOCAL_STORE.open();
dispatch(startWalletRequested({
privateKey,
publicKey,
pin: newPin,
password,
walletType: 'web3auth',
}));
// Clear sensitive in-memory copy of the password.
setPassword('');
navigate('/loading_addresses/', { replace: true });
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle bootstrap failures in pinSuccess to avoid partial wallet state.

The async chain on Line 85-101 has no error handling. If initWeb3AuthStorage/open fails, you can end with unlocked/partial state and no UX recovery path.

Suggested fix
   const pinSuccess = async (newPin) => {
     const { privateKey, publicKey } = web3authState;
-    LOCAL_STORE.unlock();
-    await LOCAL_STORE.initWeb3AuthStorage(privateKey, publicKey, newPin, password);
-    LOCAL_STORE.markBackupDone();
-    LOCAL_STORE.open();
-    dispatch(startWalletRequested({
-      privateKey,
-      publicKey,
-      pin: newPin,
-      password,
-      walletType: 'web3auth',
-    }));
-    // Clear sensitive in-memory copy of the password.
-    setPassword('');
-    navigate('/loading_addresses/', { replace: true });
+    try {
+      LOCAL_STORE.unlock();
+      await LOCAL_STORE.initWeb3AuthStorage(privateKey, publicKey, newPin, password);
+      LOCAL_STORE.markBackupDone();
+      LOCAL_STORE.open();
+      dispatch(startWalletRequested({
+        privateKey,
+        publicKey,
+        pin: newPin,
+        password,
+        walletType: 'web3auth',
+      }));
+      setPassword('');
+      navigate('/loading_addresses/', { replace: true });
+    } catch (e) {
+      LOCAL_STORE.lock();
+      setPassword('');
+      setAskPIN(false);
+      navigate('/web3auth_login/', { replace: true });
+    }
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const pinSuccess = async (newPin) => {
const { privateKey, publicKey } = web3authState;
LOCAL_STORE.unlock();
await LOCAL_STORE.initWeb3AuthStorage(privateKey, publicKey, newPin, password);
LOCAL_STORE.markBackupDone();
LOCAL_STORE.open();
dispatch(startWalletRequested({
privateKey,
publicKey,
pin: newPin,
password,
walletType: 'web3auth',
}));
// Clear sensitive in-memory copy of the password.
setPassword('');
navigate('/loading_addresses/', { replace: true });
}
const pinSuccess = async (newPin) => {
const { privateKey, publicKey } = web3authState;
try {
LOCAL_STORE.unlock();
await LOCAL_STORE.initWeb3AuthStorage(privateKey, publicKey, newPin, password);
LOCAL_STORE.markBackupDone();
LOCAL_STORE.open();
dispatch(startWalletRequested({
privateKey,
publicKey,
pin: newPin,
password,
walletType: 'web3auth',
}));
setPassword('');
navigate('/loading_addresses/', { replace: true });
} catch (e) {
LOCAL_STORE.lock();
setPassword('');
setAskPIN(false);
navigate('/web3auth_login/', { replace: true });
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/screens/Signin.js` around lines 85 - 101, pinSuccess currently performs
async setup (LOCAL_STORE.unlock, initWeb3AuthStorage, markBackupDone,
LOCAL_STORE.open, dispatch(startWalletRequested...), setPassword, navigate) with
no error handling; wrap the async sequence in a try/catch and only clear
password, dispatch startWalletRequested, and navigate after all steps succeed.
On failure in initWeb3AuthStorage or LOCAL_STORE.open, catch the error,
log/report it, undo/rollback any partial state (e.g. if LOCAL_STORE.unlock()
succeeded ensure you call LOCAL_STORE.lock() or another cleanup method and avoid
calling markBackupDone/open/dispatch), and surface a user-visible error instead
of navigating. Ensure sensitive values (password/privateKey) are only cleared
after successful completion or securely handled on rollback.

Comment thread src/screens/Signin.js
Comment on lines +91 to +97
dispatch(startWalletRequested({
privateKey,
publicKey,
pin: newPin,
password,
walletType: 'web3auth',
}));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify how START_WALLET_REQUESTED payload fields are consumed and whether sensitive
# fields are persisted/logged in reducers, middleware, or sagas.

set -euo pipefail

echo "== Action type usage =="
rg -n -C3 'START_WALLET_REQUESTED|startWalletRequested\s*\(' --type js --type jsx --type ts --type tsx

echo
echo "== Sensitive field usage near start-wallet flow =="
rg -n -C3 '\b(privateKey|publicKey|pin|password)\b' --type js --type jsx --type ts --type tsx

echo
echo "== Potential persistence/logging touchpoints =="
rg -n -C3 'redux-logger|logger|persist|localStorage|sessionStorage|sentry|console\.(log|warn|error)' --type js --type jsx --type ts --type tsx

Repository: HathorNetwork/hathor-wallet

Length of output: 141


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Locate Signin dispatch payload =="
ls -la src/screens/Signin.js
sed -n '70,120p' src/screens/Signin.js | nl -ba | sed -n '1,120p'

echo
echo "== Find action type / action creator =="
rg -n --hidden -S --glob '*.js' --glob '*.jsx' --glob '*.ts' --glob '*.tsx' \
  'START_WALLET_REQUESTED|startWalletRequested\s*\('

echo
echo "== Find sensitive field handling in start-wallet flow =="
rg -n --hidden -S --glob '*.js' --glob '*.jsx' --glob '*.ts' --glob '*.tsx' \
  '\b(privateKey|publicKey|pin|password)\b'

echo
echo "== Check for common logging/persist middlewares =="
rg -n --hidden -S --glob '*.js' --glob '*.jsx' --glob '*.ts' --glob '*.tsx' \
  'redux-logger|logger|persist|localStorage|sessionStorage|sentry|console\.(log|warn|error)'

Repository: HathorNetwork/hathor-wallet

Length of output: 240


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Show Signin.js dispatch context =="
sed -n '70,115p' src/screens/Signin.js

echo
echo "== Find action creator / action type =="
rg -n -C2 'START_WALLET_REQUESTED|startWalletRequested\s*\(' -g'*.js' -g'*.jsx' -g'*.ts' -g'*.tsx'

echo
echo "== Find sensitive field identifiers =="
rg -n -C2 '\b(privateKey|publicKey|pin|password)\b' -g'*.js' -g'*.jsx' -g'*.ts' -g'*.tsx'

echo
echo "== Check for logging/persist/logging middlewares touchpoints =="
rg -n -C2 'redux-logger|persist|redux-persist|localStorage|sessionStorage|sentry|console\.(log|warn|error)|logger' -g'*.js' -g'*.jsx' -g'*.ts' -g'*.tsx'

Repository: HathorNetwork/hathor-wallet

Length of output: 50384


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Logging/persist middleware touchpoints (src only) =="
rg -n --hidden -S \
  'redux-logger|redux-persist|persistStore|persistReducer|PersistGate|localStorage|sessionStorage|sentry|console\.(log|warn|error)' \
  src

echo
echo "== Does reducers store startWalletAction / START_WALLET_REQUESTED payload? =="
rg -n --hidden -S 'startWalletAction|START_WALLET_REQUESTED' src/reducers/index.js
sed -n '120,220p' src/reducers/index.js
sed -n '400,480p' src/reducers/index.js

echo
echo "== Inspect wallet saga for sensitive handling/logging around startWallet =="
rg -n --hidden -S 'function startWallet|startWallet\(|START_WALLET_REQUESTED|console\.(log|warn|error)|logger' src/sagas/wallet.js
# Show the relevant destructuring and web3auth branch
sed -n '120,240p' src/sagas/wallet.js
sed -n '240,360p' src/sagas/wallet.js

echo
echo "== Inspect startWalletRequested action definition =="
sed -n '430,520p' src/actions/index.js

Repository: HathorNetwork/hathor-wallet

Length of output: 29912


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Find reducer logic for startWalletAction storage/clearing =="
rg -n --hidden -S 'startWalletAction' src/reducers/index.js
sed -n '1060,1180p' src/reducers/index.js

echo
echo "== Find where START_WALLET_REQUESTED action is dispatched/stored in reducers (handlers) =="
rg -n --hidden -S 'function onStartWalletRequested|onStartWalletRequested\(|onStartWalletReset\(|START_WALLET_RESET|START_WALLET_SUCCESS|START_WALLET_FAILED' src/reducers/index.js

echo
echo "== Inspect store/middleware setup for redux-logger/devtools/persist =="
rg -n --hidden -S 'applyMiddleware|redux-logger|devTools|composeWithDevTools|persistStore|persistReducer|redux-persist|createStore\(' src

Repository: HathorNetwork/hathor-wallet

Length of output: 4004


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Find redux store configuration (createStore/applyMiddleware) =="
rg -n --hidden -S 'createStore\(|configureStore\(|applyMiddleware\(|composeWithDevTools|redux-logger|devTools|persistStore|persistReducer|redux-persist' src

echo
echo "== Find reducer persistence (if any) =="
rg -n --hidden -S 'persistReducer|persistStore|redux-persist|PersistGate' src

echo
echo "== Inspect entry where store is created (top candidates) =="
ls -ლა src | head
fd -t f 'store*.js|index.js|configureStore*.js|reducers*.js|rootReducer*.js|App*.js' src -i --max-results 50 || true

Repository: HathorNetwork/hathor-wallet

Length of output: 437


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,220p' src/store/index.js

echo
echo "== Any store enhancers/devtools options? =="
rg -n --hidden -S 'devTools|redux-logger|logger|persist|redux-persist|PersistGate|applyMiddleware|middleware' src/store/index.js src/store -S || true

Repository: HathorNetwork/hathor-wallet

Length of output: 1389


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Where startWalletAction is used (beyond reducer) =="
rg -n --hidden -S 'startWalletAction' src

echo
echo "== Find errorHandler / startWallet retry logic =="
rg -n --hidden -S 'errorHandler\(|startWalletFailed\(|onStartWalletFailed\(|START_WALLET_FAILED|startWalletAction' src/sagas

echo
echo "== Inspect potential devtools configuration =="
rg -n --hidden -S 'devTools' src/store src

echo
echo "== Search for redux-persist in entire repo (includes non-src if any) =="
rg -n --hidden -S 'redux-persist|persistStore|persistReducer|PersistGate' .

Repository: HathorNetwork/hathor-wallet

Length of output: 932


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== LoadWalletFailed usage of startWalletAction =="
sed -n '1,120p' src/screens/LoadWalletFailed.js

echo
echo "== errorHandler retry logic =="
sed -n '60,160p' src/sagas/helpers.js

echo
echo "== startWallet failure handling around where failures occur =="
sed -n '400,560p' src/sagas/wallet.js
sed -n '940,1030p' src/sagas/wallet.js

Repository: HathorNetwork/hathor-wallet

Length of output: 13523


Limit sensitive data exposure from startWalletRequested retry path.

  • src/screens/Signin.js dispatches startWalletRequested({ privateKey, pin, password, walletType: 'web3auth' }).
  • src/reducers/index.js stores the entire action in state.startWalletAction on START_WALLET_REQUESTED and only clears it on START_WALLET_SUCCESS (not on START_WALLET_FAILED); src/screens/LoadWalletFailed.js retries by re-dispatching lastAction, so privateKey/pin/password remain in Redux state until success/reset.
  • src/store/index.js uses only redux-saga and redux-thunk (no redux-logger/redux-persist), but keeping secrets in Redux still makes them available to Redux DevTools/state inspection.
  • In the Web3Auth first-login flow, startWallet skips re-init and relies on privateKey/publicKey; pin is used for Web3Auth restore when privateKey is absent (and LockedWallet dispatches only { pin }, indicating password isn’t needed for restore).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/screens/Signin.js` around lines 91 - 97, The dispatched
startWalletRequested currently includes secrets (privateKey, pin, password)
which are saved into state.startWalletAction by the START_WALLET_REQUESTED
handler and replayed by LoadWalletFailed; change the flow to avoid storing
secrets in Redux: update the START_WALLET_REQUESTED reducer to store only a
sanitized summary (e.g., walletType, publicKey, flags) not the full action, and
ensure the reducer clears state.startWalletAction on START_WALLET_FAILED as well
as START_WALLET_SUCCESS; update LoadWalletFailed to re-dispatch a reconstructed,
non-sensitive retry (or prompt the UI to re-enter secrets) instead of replaying
the original action; alternatively, keep secrets out of the dispatched payload
by moving privateKey/pin/password into local component state or passing them
directly to the saga middleware (startWalletRequested metadata or a secure
channel) so startWalletRequested, reducers, and LoadWalletFailed never persist
secrets.

Comment on lines +43 to +46
const persistedNetwork = LOCAL_STORE.getNetworkSettings()?.network || 'mainnet';
const network = persistedNetwork;
const cfg = WEB3AUTH_CONFIG[network] || WEB3AUTH_CONFIG.testnet;
const appleEnabled = Boolean(cfg.appleClientId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Normalize persisted network before using it in login flow.

On Line 45 you safely fall back cfg to testnet, but on Line 55 you still pass the raw network to web3authLogin. If local storage has an unexpected value, UI gating and runtime config can diverge.

Suggested fix
-  const persistedNetwork = LOCAL_STORE.getNetworkSettings()?.network || 'mainnet';
-  const network = persistedNetwork;
-  const cfg = WEB3AUTH_CONFIG[network] || WEB3AUTH_CONFIG.testnet;
+  const persistedNetwork = LOCAL_STORE.getNetworkSettings()?.network || 'mainnet';
+  const network = WEB3AUTH_CONFIG[persistedNetwork] ? persistedNetwork : 'testnet';
+  const cfg = WEB3AUTH_CONFIG[network];

Also applies to: 55-55

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/screens/Web3AuthLogin.js` around lines 43 - 46, The persisted network
value from LOCAL_STORE.getNetworkSettings() (persistedNetwork / network) must be
normalized against known keys in WEB3AUTH_CONFIG before being used in the login
flow; change the code that derives cfg to also produce a normalizedNetwork
(e.g., pick network if it exists in WEB3AUTH_CONFIG, otherwise use 'testnet' or
'mainnet' fallback) and pass that normalizedNetwork to web3authLogin instead of
the raw network value so UI gating (appleEnabled / cfg) and runtime config
remain consistent; update references to persistedNetwork/network, cfg, and the
web3authLogin call to use this normalized value.

Comment thread src/storage.js
Comment on lines +375 to +378
async getWeb3AuthPrivateKey(pin) {
const storage = this.getStorage();
return storage.getSingleKeyPrivateKey(pin);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add null check for storage to prevent potential runtime error.

getStorage() can return null when the wallet is not loaded (see lines 396-400). Calling storage.getSingleKeyPrivateKey(pin) on null would throw. Other accessors like _getAccessData (line 413-418) guard against this.

🛡️ Proposed fix
 async getWeb3AuthPrivateKey(pin) {
   const storage = this.getStorage();
+  if (!storage) {
+    throw new Error('Cannot get Web3Auth private key from uninitialized wallet');
+  }
   return storage.getSingleKeyPrivateKey(pin);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/storage.js` around lines 375 - 378, The getWeb3AuthPrivateKey function
calls this.getStorage() and directly invokes storage.getSingleKeyPrivateKey(pin)
which will throw if getStorage() returns null; add a null check for the result
of this.getStorage() (similar to the pattern used in _getAccessData) and handle
the missing storage case by returning null or throwing a clear error, e.g., if
(!storage) return null or throw new Error('wallet not loaded'), before calling
storage.getSingleKeyPrivateKey(pin).

Comment thread src/storage.js
Comment on lines +385 to +389
async getWeb3AuthPublicKey() {
const storage = this.getStorage();
const accessData = await storage.getAccessData();
return accessData?.singleKeyPublicKey;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add null check for storage consistency.

Same issue as getWeb3AuthPrivateKeygetStorage() may return null.

🛡️ Proposed fix
 async getWeb3AuthPublicKey() {
   const storage = this.getStorage();
+  if (!storage) {
+    return undefined;
+  }
   const accessData = await storage.getAccessData();
   return accessData?.singleKeyPublicKey;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async getWeb3AuthPublicKey() {
const storage = this.getStorage();
const accessData = await storage.getAccessData();
return accessData?.singleKeyPublicKey;
}
async getWeb3AuthPublicKey() {
const storage = this.getStorage();
if (!storage) {
return undefined;
}
const accessData = await storage.getAccessData();
return accessData?.singleKeyPublicKey;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/storage.js` around lines 385 - 389, getWeb3AuthPublicKey currently calls
this.getStorage() without guarding against a null return like
getWeb3AuthPrivateKey does; update getWeb3AuthPublicKey to first call const
storage = this.getStorage() and if storage is null/undefined return null (or the
same fallback used in getWeb3AuthPrivateKey), otherwise await
storage.getAccessData() and return accessData?.singleKeyPublicKey so it mirrors
the null-safety of getWeb3AuthPrivateKey.

@raul-oliveira raul-oliveira moved this from In Progress (WIP) to Todo in Hathor Network Jun 1, 2026
@raul-oliveira raul-oliveira moved this from Todo to In Progress (WIP) in Hathor Network Jun 1, 2026
@raul-oliveira raul-oliveira moved this from In Progress (WIP) to Todo in Hathor Network Jun 2, 2026
@raul-oliveira raul-oliveira moved this from Todo to Parking Lot in Hathor Network Jun 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Parking Lot

Development

Successfully merging this pull request may close these issues.

1 participant