# @layerswap/wallet-core

Headless foundation of the Layerswap wallet stack. Owns the wallet contracts (`WalletProvider`, `WalletConnectionStore`, `WalletConnectionService`, `WalletProviderDescriptor`, `AppNetworkAdapter`), the provider registry and descriptor-hydration machinery, WalletConnect plumbing (registry connectors, deep links, dynamic metadata), EIP-6963 detection, and string-based wallet icon resolution.

Contains no UI. React appears only as a peer dependency for the headless context providers (`WalletProvidersRegistryProvider`, `DescriptorHydrationBoundary`). The visual layer (connect modal, connectors list) lives in `@layerswap/ui-kit`, which depends on this package — never the other way around.

- `@layerswap/wallet-core` — runtime: registry, stores, WalletConnect helpers, icon strings, hooks.
- `@layerswap/wallet-core/types` — the contracts implemented by every `@layerswap/wallet-*` chain package.
- `@layerswap/wallet-core/errors` — shared wallet error classification: `isUserRejection(error)`, `normalizeWalletErrorCode(error)` (maps a provider error to a bounded `WalletErrorReasonCode` from `@layerswap/widget-types`), `walletErrorCode(error)` (the raw code on one error), and `walletErrorCodeFromChain(error)` (the outermost available raw code, including known error wrappers, for diagnostics). Chain packages and the widget use these instead of per-adapter rejection checks; adapter-specific legacy rules stay at the adapter boundary (e.g. `isEvmUserRejection`). Precedence is by evidence tier, innermost cause first: definitive codes/names, then descriptive message text, then the `-32603` / `SERVER_ERROR` / `InternalRpcError` buckets; `cause`, `data`, `originalError` and `error` wrappers are transparent. Misclassifications are fixed by adding a provenance-tagged fixture to `packages/wallets/core/tests/wallet-error-fixtures.mjs`, not by call-site special cases.
  Transfer providers throw through `walletActionError(name, { message, cause?, reasonCode? })` and `userRejectedError({ message?, cause? })`: `name` is the `ActionMessageType` the widget renders (UI copy only, it never classifies), `reasonCode` is what the host and telemetry are told and wins over every inferred signal anywhere in the `cause` chain. Rule: labels are UI copy; declare declines with `userRejectedError`. `tests/adapter-sentinel-guard.test.mjs` fails CI for any adapter that assigns the rejected label by hand.

Chain packages (`@layerswap/wallet-evm`, `@layerswap/wallet-svm`, …) must depend only on this package, `@layerswap/utils`, and `@layerswap/widget-types` — never on `@layerswap/ui-kit`.
