refactor(wallets): migrate chain wallet providers to class + Zustand stores#1999
Open
babkenmes wants to merge 27 commits into
Open
refactor(wallets): migrate chain wallet providers to class + Zustand stores#1999babkenmes wants to merge 27 commits into
babkenmes wants to merge 27 commits into
Conversation
… store Eliminates React-hook-based state and provider trees in the EVM wallet package. State now lives in a module-scoped Zustand store and orchestration in a class singleton, both of which survive the lazy-load remount cycles that were forcing hotfix workarounds in the old hook-heavy code. - Replaces the 561-line useEVMConnection.ts hook with a class (EvmConnectionService) plus a thin React adapter (service/useEvmConnection.ts). - Adds Zustand store (service/evmStore.ts) and vanilla wagmi watchers (service/syncWagmi.ts) that mirror connections/account/connectors. - Hoists the wagmi Config into a module-scoped singleton (service/getEvmConfig.ts) with reconnect() called on init, replacing <WagmiProvider reconnectOnMount>. - Deletes ActiveEvmAccountProvider, WagmiProvider, QueryClientProvider, WagmiWrapper, QueryWrapper from the EVM tree. EVMProvider/index.tsx is now an EVMHydrator that pushes init params and renders children directly. - Extracts pure helpers to service/: resolveWallet, resolveSupportedNetworks, networkBuckets, connectorsHelpers (dedupePreferInjected, splitRegistryConnectors, wagmiDisplayUriSource, attemptGetAccount). - Converts useEVMTransfer hook into a vanilla createEvmTransfer factory. - Rewrites EVMRpcHealthCheckProvider to read from the store instead of useAccount. - Replaces useWalletClient in evmUtils/ethers.ts with getWalletClient over the config singleton. - Migrates paradex useParadexConnection's useConfig() to getEvmConfig() so app boot doesn't crash without WagmiProvider in tree. Remaining paradex hooks (useAccount, useWalletClient) only fire during withdrawal flow and are a documented follow-up. - Preserves all connector orchestration bug-fix sediment verbatim (synchronous display URI subscription, attemptGetAccount retry loop, pending-metadata ordering, dedupePreferInjected). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The bridge app now mounts only the EVM provider by default instead of the full multi-chain bundle from getDefaultProviders(). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Restores parity with the pre-refactor context-fallback behavior so host apps that already own a wagmi Config (and optionally wrap the widget in their own <WagmiProvider>) can have the EVM package adopt that config instead of creating a parallel one. - getEvmConfig.ts: add provideExternalEvmConfig(cfg) + _external flag. Adopting an external config skips the internal createConfig() and skips reconnect() (host owns that lifecycle). Re-providing a different config after one is already in use warns and no-ops. - EVMHydrator: accept optional wagmiConfig prop (explicit path) and fall back to useContext(WagmiContext) for ambient detection (implicit path). Resolution order: existing registered config → external (prop/ambient) → internal createConfig() with widget-built connectors. - createEVMProvider: expose wagmiConfig?: Config on EVMProviderConfig and forward it to EVMProviderWrapper. - Re-export provideExternalEvmConfig and isExternalEvmConfig. attachWagmiSync works against any wagmi Config, so EvmConnectionService, useEthersSigner, and rpcHealthCheckProvider need no changes — they still read through getEvmConfig() / useEvmStore. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… store Mirrors the EVM wallet-provider refactor (ab23f35) for TON: orchestration moves into TonConnectionService, wallet state is observed once via attachTonSync and stored in a Zustand store, and useTonConnection becomes a thin React adapter. Old useTONConnection.ts stays as a re-export shim. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…d store Mirrors the EVM/TON refactors for Tron: orchestration moves into TronConnectionService, wallet state is synced from the @tronweb3/* react hooks into a Zustand store via a TronSync hydrator, and useTronConnection becomes a thin React adapter. Old useTronConnection.ts stays as a re-export shim. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tand store Mirrors the EVM refactor for Bitcoin: orchestration moves into BitcoinConnectionService, wagmi-style state from @bigmi/client is mirrored into a Zustand store via attachBitcoinSync, and useBitcoinConnection becomes a thin React adapter. Old useBitcoinConnection.ts stays as a re-export shim; unused useAccount/useSyncExternalStoreWithTracked utils are removed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…d store Mirrors the EVM/TON/Tron/Bitcoin refactors for Fuel: orchestration moves into FuelConnectionService, @fuels/react state (connectors + fuel instance) is mirrored into a local Zustand store from a thin React hook, and the hook becomes a thin adapter that wires global useWalletStore deps via configure(). Old useFuelConnection.ts stays as a re-export shim. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… store Mirrors the EVM refactor for Solana: orchestration moves into SvmConnectionService, @solana/wallet-adapter-react state is mirrored into a local Zustand store via an SvmSync hydrator, and useSvmConnection becomes a thin React adapter. Old useSVMConnection.tsx stays as a re-export shim. Also adds @types/react to devDependencies on packages that now use zustand, so pnpm doesn't pick @types/react@18 transitively (which surfaces a React Context type mismatch in EVM); and adds an explicit ReactElement return type to TronProvider for the same reason. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…stand store Mirrors the EVM refactor for Starknet: orchestration moves into StarknetConnectionService, @starknet-react/core state (connectors + disconnectAsync) is mirrored into the existing Zustand store via a StarknetSync hydrator, and useStarknetConnection becomes a thin React adapter. resolveStarknetWallet moves into the service. Old useStarknetConnection.ts and starknetWalletStore.ts stay as re-export shims. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ice class Mirrors the EVM refactor for Paradex: connect/switch/requestAdditional orchestration moves into ParadexConnectionService. The hook now wires inner EVM + Starknet providers and the global useWalletStore paradexAccounts deps into the class via configure(). Old useParadexConnection.ts stays as a re-export shim. Paradex doesn't get its own Zustand store — its persisted state already lives in the widget's global useWalletStore.paradexAccounts, and inner- provider connector state lives in the EVM/Starknet stores; the service just composes them. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…stand store Mirrors the EVM refactor for ImtblPassport. ImtblPassport has no WalletConnectionProvider hook of its own (connection happens through the EVM provider via EIP-6963), so the refactor is scoped to the Passport SDK singleton: ImtblPassportService owns init + connectEvm + loginCallback, and the ready flag + instance live in a Zustand store. The old module-level passportInstance export is removed; consumers should use imtblPassportService.getInstance() or useImtblPassportStore() instead. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the EVM-only default in WidgetWrapper with getDefaultProviders from @layerswap/wallets, which bundles EVM, Starknet, Fuel, Paradex, Bitcoin, TON, SVM, Tron, and (when configured) Immutable Passport. WalletConnect, TON, and Immutable Passport configs come from the existing NEXT_PUBLIC_* env vars; the TON manifest URL points at the manifest already shipped under /public/tonconnect-manifest.json. Callers can still pass walletProviders explicitly to override. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removes @walletconnect/universal-provider, @walletconnect/types, @walletconnect/utils, and bs58 — none have any import in src/. Only @walletconnect/ethereum-provider is still used (in connectors/resolveConnectors/walletConnect.ts as a type import). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removed when useAccount/useSyncExternalStoreWithTracked utils were deleted in the class-based refactor (commit 14db284). No imports remain. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… tweetnacl None have any import in src/. Likely orphans from earlier Fuel implementations that no longer apply. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ypto-utils and bignumber.js No imports in src/. Paradex SDK and starknet handle the crypto/big-number needs that these were presumably brought in for. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ton/crypto has no imports in src/ — only @ton/core and @ton/ton are used. The devDep `ton` (legacy v13) was superseded by @ton/ton (v15) and is also unimported. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
No axios imports in src/. @imtbl/sdk handles its own HTTP needs internally. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TronProvider only dynamically imports adapters for MetaMask, TronLink, OKX, BitKeep, Bybit, and Gate. The foxwallet, imtoken, ledger, tokenpocket, and trust adapters are declared but never imported anywhere in src/, so they're just bloating install size. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previous commit (46da1af) dropped @ton/crypto because nothing in wallet-ton/src imports it directly. But @ton/ton declares @ton/crypto as a peerDep, so a clean install of the bridge app fails with "Module not found: Can't resolve '@ton/crypto'" once the transitively-hoisted copy is gone. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…act/types None are imported in apps/bridge source. The other "looks unused" deps (@bigmi/*, wagmi/@wagmi/*, viem, zustand, @tanstack/react-query) are peerDeps of the wallet packages and must stay. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ternal stores
Reshapes the widget's wallet-provider contract so wallet packages no longer
have to be React hooks. The driver is to make @layerswap/wallet-evm fully
React-free; the contract change cascades to every wallet package.
Widget contract changes
- WalletProvider.walletConnectionProvider (hook) → createConnection: (props)
=> WalletConnectionStore (vanilla subscribe/getSnapshot/updateProps/destroy)
- WalletProvider gains optional `init: (ctx) => () => void` for one-shot
lifecycle; `wrapper` stays for packages that need an upstream React tree
(TonConnectUI, StarknetReact, etc.)
- RpcHealthCheckProvider.useRpcHealthCheck (hook) → createStore() external
store with combined snapshot + imperative ops
- Wallet.icon: (props) => JSX.Element → string | undefined (URL or data URI);
new WalletIconView falls back to AddressIcon when undefined
- BaseWalletProviderConfig.customHook → customConnection
Widget internals
- New vanilla zustand-style stores: getAdditionalConnectorsStore(ns, projectId)
and connectModalStore. useAdditionalConnectors / useConnectModal become thin
useSyncExternalStore adapters
- New createReactHookConnectionAdapter(useFn) helper bridges legacy hooks to
the external-store contract via a hidden <Hydrator> component
- getKnownConnectorIconBase64(id) / resolveWalletIconString expose string-only
icon lookup so wallet packages produce Wallet.icon strings without
importing React
EVM package (now fully React-free)
- Source has zero `from 'react'`, zero JSX, zero .tsx files
- react / react-dom / @types/react removed from peer + dev deps
- useEvmConnection hook → createEvmConnection(props) external store with
input-memoized snapshots and ref-counted internal subscriptions
- EVMHydrator component → initEvmProvider(opts) one-shot function
- useChainConfigs hook → getEvmChainsConfig(networks) pure function
- useEVMConnectors hook → buildEVMConnectors(...) pure function
- useEthersSigner hook → getEthersSigner({ chainId }) promise
- EVMRpcHealthCheckProvider.useRpcHealthCheck → createStore() vanilla store
- Deprecated EVMProvider constant + useEVMConnection shim removed
Other wallet packages (contract-only migration)
- bitcoin, fuel, starknet, svm, ton, tron, paradex, imtblPassport: each ships
a thin xxxConnectionAdapter built with createReactHookConnectionAdapter
whose Hydrator is mounted inside the package's existing wrapper. The
factories now return createConnection instead of walletConnectionProvider
- Paradex no longer recreates EVM/Starknet provider snapshots itself — reads
from useWalletProviders() instead
- Deprecated XxxProvider constants removed across all packages
Strict-mode safety
- The EVM connection store ref-counts its upstream subscriptions
(useEvmStore, additionalConnectorsStore, connectModalStore) so React 18's
double-mount cleanup doesn't permanently tear down internal listeners
Apps
- widget-playground: useCustomEvm wrapped via createReactHookConnectionAdapter;
LayerswapWidgetCustomEvm mounts the hydrator inside its WagmiProvider tree
- examples/nextjs-dynamic: same shape for useCustomStarknet
- bridge: getDefaultProviders shape unchanged externally
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…factories across chain packages Implements the external-store WalletProvider contract from b5ee460 across bitcoin, fuel, imtblPassport, paradex, starknet, svm, ton, and tron. Drops React XxxProvider components and useXxxConnection hooks in favor of createXxxConnection vanilla factories backed by Zustand stores, renames useXxxTransfer to createXxxTransfer, and updates the widget consumer to resolve providers through the new registry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Connectors were always marked type:'injected' with extensionNotFound keyed off installLink presence, so configured wallets showed "Installed" yet routed to the not-detected screen, and Web Wallet / Controller were mislabeled as installed. Now gate both on the connector's available() check plus configured-extension status. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
WalletProvidercontract with external stores; integrate all providers in the bridge app throughgetDefaultProvidersknownConnectorIcons,resolveWalletIcon) and connect-modal/connector store plumbing in the widgetConfigTest plan
pnpm installresolves cleanly with the pruned lockfile;pnpm buildsucceedsNote
The
tonconnect-manifest.jsonin this branch points at atrycloudflare.comtunnel URL for local testing — revert to the production URL before merging.🤖 Generated with Claude Code