@usebutr/evm 1.0.1 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,177 @@
1
+ # @usebutr/evm
2
+
3
+ ## 1.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#179](https://github.com/pedroapfilho/usebutr/pull/179) [`bc5b58c`](https://github.com/pedroapfilho/usebutr/commit/bc5b58cd87601be38dbb77a14e16eaaa36e8f012) Thanks [@pedroapfilho](https://github.com/pedroapfilho)! - Include package READMEs, MIT licenses, and documentation and issue links on npm.
8
+ - Updated dependencies [[`bc5b58c`](https://github.com/pedroapfilho/usebutr/commit/bc5b58cd87601be38dbb77a14e16eaaa36e8f012)]:
9
+ - @usebutr/core@2.0.1
10
+
11
+ ## 1.1.0
12
+
13
+ ### Minor Changes
14
+
15
+ - 9b1caa2: Correctness pass across the storage, hydration and connector layers. Several of
16
+ these change observable behaviour; the ones worth knowing about before upgrading:
17
+
18
+ **Breaking**
19
+
20
+ - `SuiWallet.signTransaction` now returns `{ bytes, signature }` instead of
21
+ `Uint8Array`. Sui's `executeTransactionBlock` needs both, and each connector
22
+ previously returned a different half, so the old value could not be broadcast
23
+ and a consumer could not tell which half they held. SVM and Bitcoin keep their
24
+ single-`Uint8Array` shape.
25
+ - `@usebutr/svm`'s `sendTx` returns the signature base58-encoded, matching the
26
+ WalletConnect namespace, the Ledger app, Solana explorers and
27
+ `getSignatureStatuses`. It previously returned base64.
28
+ - `@usebutr/sui`'s feature input types drop the `string` arm from
29
+ `transaction`; wallets only ever accepted the `toJSON()` form. Strings and BCS
30
+ bytes are now wrapped for you, so `sendTx` accepts strictly more than before.
31
+
32
+ **Behaviour fixes**
33
+
34
+ - `WalletStorage.setPool` and `removePoolEntry` could self-deadlock on a corrupt
35
+ pool payload, leaving `connectWallet` unresolved and every later pool write
36
+ jammed for the page's life. Both are fixed, and `readWalletSnapshot` now shares
37
+ one codec with `WalletStorage` so the server and client decodes cannot drift.
38
+ - A seeded (SSR) entry whose silent reconnect failed used to stay in the pool
39
+ backed by a placeholder connector forever, so `useSigner`/`useBalance`
40
+ reported an error and `useConnectionStatus` reported `"reconnecting"`
41
+ permanently. Those entries are now dropped.
42
+ - `useSigner` and `useBalance` stay `idle` for a wallet that is still
43
+ reconnecting instead of surfacing a placeholder rejection as `status: "error"`.
44
+ - `useConnectionStatus` lets a live `"connecting"`/`"error"` take precedence over
45
+ the derived `"reconnecting"`, so an in-flight attempt and a connection error
46
+ are no longer hidden. New `useIsReconnecting(connectorId?)` answers the
47
+ per-wallet question directly.
48
+ - A background restore no longer writes the connect-attempt status or steals
49
+ `activeConnectorId`, and a superseded `CONNECT_FAILED` no longer overwrites the
50
+ current attempt.
51
+ - An externally-disconnected connector is now torn down, not just dropped from
52
+ the pool, so a cached adapter is not reused while still holding a session.
53
+ - EVM: a malformed provider response is treated as absent rather than `""`, which
54
+ previously produced chain `eip155:0`, zero-length signatures, empty transaction
55
+ hashes and a confident `0 ETH`. `switchChain` gained the same-chain
56
+ short-circuit `sendTxToChain` already had.
57
+ - WalletConnect: accounts carry their own chain instead of all being stamped with
58
+ the active one, so a multi-chain session no longer returns a wrong-chain
59
+ address. One pairing now covers every configured namespace, the pairing URI
60
+ survives a reconnect, and concurrent connects share one pairing.
61
+ - Bitcoin: the sats-connect (Xverse) adapter honours `silent`, so a reload no
62
+ longer triggers an unsolicited approval prompt, and exposes the payment address
63
+ as its single account instead of also presenting the ordinals address as
64
+ spendable.
65
+ - Polkadot: the injected adapter actually delivers events to subscribers,
66
+ `switchChain` notifies like its Wallet Standard sibling, and chain resolution
67
+ prefers the mainnets rather than whatever the wallet listed first.
68
+ - Ledger: the device session is committed atomically, so a locked device no
69
+ longer leaves an open transport that reports itself connected, and concurrent
70
+ connects open one transport.
71
+ - SVM: `sendTxToChain` submits to the chain you asked for instead of the
72
+ adapter's current one, and rejects a chain the wallet does not advertise.
73
+ - `@usebutr/svm`'s `switchChain` capability now counts only `solana:` chains, so
74
+ a multi-namespace wallet no longer advertises a method that always throws.
75
+
76
+ **Testing**
77
+
78
+ - `createFakePersistence` is now the real `WalletStorage` over memory drivers
79
+ rather than a parallel implementation, so it inherits upsert semantics,
80
+ validation and JSON round-tripping. Two divergences are fixed as a result:
81
+ `setPool` upserts rather than replaces, and `clearAll` leaves the
82
+ user-disconnected flag alone.
83
+ - `createFakeConnectedWallet` rejects being given both an `adapter` and explicit
84
+ `addresses`/`accounts`, a combination whose halves could disagree.
85
+
86
+ ### Patch Changes
87
+
88
+ - Updated dependencies [9b1caa2]
89
+ - @usebutr/core@2.0.0
90
+
91
+ ## 1.0.1
92
+
93
+ ### Patch Changes
94
+
95
+ - Updated dependencies [8ecaf89]
96
+ - @usebutr/core@1.1.0
97
+
98
+ ## 1.0.0
99
+
100
+ ### Major Changes
101
+
102
+ - f0a5116: **Breaking:** the `platform` field is gone from the `PlatformDiscoverer` type in `@usebutr/core`, and from the `evmDiscoverer`, `svmDiscoverer`, `suiDiscoverer`, `bitcoinDiscoverer` and `polkadotDiscoverer` objects that implement it. Nothing read it: the aggregator keys discoverers by `ChainPlatform` in its own registry, so the field only restated the key.
103
+
104
+ Migration: read the platform from the `KNOWN_DISCOVERERS` key in `@usebutr/wallets` (`Object.entries(KNOWN_DISCOVERERS)`), or from `adapter.chainPlatform` on a discovered adapter. Custom `PlatformDiscoverer` implementations must drop the `platform` property; keeping it is now an excess-property error.
105
+
106
+ ### Patch Changes
107
+
108
+ - Updated dependencies [7887cf0]
109
+ - Updated dependencies [7887cf0]
110
+ - Updated dependencies [f0a5116]
111
+ - Updated dependencies [f0a5116]
112
+ - @usebutr/core@1.0.0
113
+
114
+ ## 0.2.5
115
+
116
+ ### Patch Changes
117
+
118
+ - Updated dependencies [4467a5e]
119
+ - @usebutr/core@0.5.0
120
+
121
+ ## 0.2.4
122
+
123
+ ### Patch Changes
124
+
125
+ - Updated dependencies [c1309ee]
126
+ - @usebutr/core@0.4.2
127
+
128
+ ## 0.2.3
129
+
130
+ ### Patch Changes
131
+
132
+ - 937dfae: Bump runtime dependency floors (`@wallet-standard/app` 1.1.1, `@ledgerhq/*` latest minors, `@walletconnect/universal-provider` 2.23.10) and modernize public type declarations from method signatures to property function types (oxlint `method-signature-style`). Type-level only — no runtime behavior change.
133
+ - Updated dependencies [937dfae]
134
+ - @usebutr/core@0.4.1
135
+
136
+ ## 0.2.2
137
+
138
+ ### Patch Changes
139
+
140
+ - a46eecd: Ship unminified ESM so downstream bundlers (Vite/esbuild dep pre-bundling) process the package correctly; fixes a ReferenceError in consumer dev servers. The consuming app minifies once at its own build.
141
+ - Updated dependencies [b5322ae]
142
+ - Updated dependencies [d5f32c7]
143
+ - Updated dependencies [a46eecd]
144
+ - @usebutr/core@0.4.0
145
+
146
+ ## 0.2.1
147
+
148
+ ### Patch Changes
149
+
150
+ - Updated dependencies [886ee1d]
151
+ - @usebutr/core@0.3.0
152
+
153
+ ## 0.2.0
154
+
155
+ ### Minor Changes
156
+
157
+ - db5d7e9: The EIP-6963 adapter's `getBalance` now reads ERC20 balances. Called with no argument it returns the native ETH balance as before; called with a token address (`getBalance(tokenAddress)`) it reads `balanceOf` / `decimals` / `symbol` for the active account on the currently-connected chain via parallel `eth_call`s on the wallet's own provider (no separate RPC needed). Non-standard `bytes32` symbols are decoded best-effort.
158
+
159
+ ### Patch Changes
160
+
161
+ - Updated dependencies [db5d7e9]
162
+ - @usebutr/core@0.2.2
163
+
164
+ ## 0.1.2
165
+
166
+ ### Patch Changes
167
+
168
+ - f846e77: Wallet-announced icons are trimmed of surrounding whitespace on ingestion. Some wallets ship data-URI icons with a leading newline, which strict consumers reject — Next.js's `<Image>` throws because `src` must not start with a control character. `@usebutr/core` exports a `sanitizeIcon` helper; the EIP-6963 and Wallet Standard adapters apply it, and an all-whitespace icon now resolves to `undefined` rather than a blank string.
169
+ - Updated dependencies [f846e77]
170
+ - @usebutr/core@0.2.1
171
+
172
+ ## 0.1.1
173
+
174
+ ### Patch Changes
175
+
176
+ - Updated dependencies [b77a477]
177
+ - @usebutr/core@0.2.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pedro Filho
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # @usebutr/evm
2
+
3
+ EIP-1193 / EIP-6963 / injected wallet discovery + adapters for butr.
4
+
5
+ Part of [butr](https://www.usebutr.com), a multi-chain wallet discovery and
6
+ connection-state library. Your application owns the picker UI and chain client.
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ npm install @usebutr/react @usebutr/core @usebutr/evm zustand
12
+ ```
13
+
14
+ React 18+ is a peer dependency of the provider. Discovery does not render a connect modal; add your picker as the provider child.
15
+
16
+ ## Usage
17
+
18
+ ```tsx
19
+ import type { ReactNode } from "react";
20
+ import { createWalletSource } from "@usebutr/core";
21
+ import { discoverEvmAdapters } from "@usebutr/evm";
22
+ import { WalletManagerProvider } from "@usebutr/react";
23
+
24
+ const discovery = createWalletSource(discoverEvmAdapters);
25
+ export const WalletProvider = ({ children }: { children: ReactNode }) => (
26
+ <WalletManagerProvider discovery={discovery}>{children}</WalletManagerProvider>
27
+ );
28
+ ```
29
+
30
+ ## Documentation
31
+
32
+ - [Package reference](https://docs.usebutr.com/api/evm)
33
+ - [Getting started](https://docs.usebutr.com/getting-started/quickstart)
34
+ - [Examples and source](https://github.com/pedroapfilho/usebutr)
35
+ - [Report an issue](https://github.com/pedroapfilho/usebutr/issues)
36
+
37
+ ## License
38
+
39
+ [MIT](./LICENSE), copyright 2026 Pedro Filho.
package/dist/index.d.ts CHANGED
@@ -1,24 +1,23 @@
1
- import { ChainBase, PlatformDiscoverer, WalletAdapter, WalletCapabilities, bytesToHexPrefixed as bytesToHex, hexToBytes } from "@usebutr/core";
1
+ import { ChainBase, PlatformDiscoverer, WalletAdapter, WalletCapabilities, WalletSigner, bytesToHexPrefixed as bytesToHex, hexToBytes } from "@usebutr/core";
2
2
  //#region src/eip1193.d.ts
3
3
  /**
4
- * Minimal EIP-1193 / EIP-6963 type surface; declared inline rather
5
- * than imported from `@types/ethereum` (which doesn't exist as a
6
- * cohesive package) or `viem` (which would expand butr's footprint).
7
- *
8
- * These types describe only the methods butr's auto-discovery layer
9
- * actually calls on a provider. They're intentionally loose
10
- * (`request(...)` returns `unknown`) so individual call sites can
11
- * narrow the result via a cast.
4
+ * Declared inline rather than taken from `viem`, which would expand butr's
5
+ * footprint. Loose on purpose: `request(...)` returns `unknown` and the
6
+ * `request*` helpers narrow it at the boundary.
12
7
  */
8
+ type Eip1193Object = {
9
+ [key: string]: Eip1193Value | undefined;
10
+ };
11
+ type Eip1193Value = bigint | boolean | number | string | null | Uint8Array | ReadonlyArray<Eip1193Value> | Eip1193Object;
13
12
  type Eip1193RequestArgs = {
14
13
  method: string;
15
- params?: ReadonlyArray<unknown> | object;
14
+ params?: ReadonlyArray<Eip1193Value> | Eip1193Object;
16
15
  };
17
- type Eip1193Listener = (...args: ReadonlyArray<unknown>) => void;
16
+ type Eip1193Listener = (...args: ReadonlyArray<Eip1193Value | undefined>) => void;
18
17
  type Eip1193Provider = {
19
18
  on: (event: string, listener: Eip1193Listener) => void;
20
19
  removeListener: (event: string, listener: Eip1193Listener) => void;
21
- request: (args: Eip1193RequestArgs) => Promise<unknown>;
20
+ request: (args: Eip1193RequestArgs) => Promise<Eip1193Value | undefined>;
22
21
  };
23
22
  type Eip6963ProviderInfo = {
24
23
  /** Data URI or absolute URL pointing at the wallet's icon. */
@@ -48,61 +47,22 @@ type DiscoverOptions = {
48
47
  target?: EventTarget;
49
48
  };
50
49
  /**
51
- * Subscribe to EIP-6963 wallet announcements. Spec:
52
- * https://eips.ethereum.org/EIPS/eip-6963
53
- *
54
- * Flow:
55
- * 1. Page registers a listener for `eip6963:announceProvider`.
56
- * 2. Page dispatches `eip6963:requestProvider` to kick wallets that
57
- * have already loaded.
58
- * 3. Wallets that load later still dispatch their own announcement
59
- * when they boot, so the listener stays attached for the lifetime
60
- * of the discovery session.
61
- * 4. Dedup by `info.rdns`; wallets occasionally re-announce, and
62
- * `info.uuid` is regenerated per page load so it's not stable.
63
- *
64
- * Returns an unsubscribe function that removes the listener.
65
- * In non-browser environments (SSR, tests with no window) returns
66
- * a noop unsubscribe and never fires the callback.
50
+ * Spec: https://eips.ethereum.org/EIPS/eip-6963. Dedupe is by `info.rdns`
51
+ * because `info.uuid` is regenerated per page load. The listener stays
52
+ * attached for the session so wallets that boot late still register.
67
53
  */
68
54
  declare const discoverEvmAdapters: (onAdapter: AdapterCallback, options?: DiscoverOptions) => (() => void);
69
55
  //#endregion
56
+ //#region src/evm-balance.d.ts
57
+ declare const formatEther: (wei: bigint) => string;
58
+ //#endregion
70
59
  //#region src/eip6963-adapter.d.ts
71
60
  declare const chainIdHexToDecimal: (hex: string) => string;
72
61
  declare const chainIdDecimalToHex: (dec: string) => string;
73
- /** Pure 18-decimal wei → ether formatter. Trims trailing zeros so
74
- * `1.0` stays `1` and `1.5` stays `1.5`. */
75
- declare const formatEther: (wei: bigint) => string;
76
62
  /**
77
- * Adapt an EIP-1193 provider (announced via EIP-6963) into a butr
78
- * `WalletAdapter`. The returned adapter covers the full `Connector +
79
- * Wallet` surface; consumers can pass it through `createConnector` like
80
- * any hand-written adapter.
81
- *
82
- * **Caveats**
83
- *
84
- * - `disconnect` calls `wallet_revokePermissions`. Many wallets don't
85
- * implement that method and silently ignore the call. The reducer
86
- * will still mark the wallet as disconnected on butr's side; the
87
- * wallet's own auto-reconnect heuristic may or may not honour it.
88
- * - `switchAccount` is intentionally unimplemented because EIP-1193 has
89
- * no silent "use address X" RPC. The active account is whichever one
90
- * the wallet exposes first; the user changes it through the wallet's
91
- * own UI. butr's `subscribe` bridge auto-updates the pool entry when
92
- * `accountsChanged` fires. Call `requestAccounts` if you want the
93
- * wallet to expose more accounts.
94
- * - `getBalance()` returns the native ETH balance with symbol `"ETH"`
95
- * regardless of which EVM chain the wallet is currently on. Consumers
96
- * that target multiple EVM chains should overlay the symbol via
97
- * their own logic.
98
- * - `getBalance(tokenAddress)` reads the ERC20 balance for the active
99
- * account on the currently-connected chain. The token's own
100
- * `decimals()` and `symbol()` are queried for accurate formatting;
101
- * if `symbol()` is non-standard (bytes32) it's decoded as best-
102
- * effort. Routed through `eth_call` on the wallet's own provider;
103
- * the demo doesn't ship a separate RPC.
104
- * - `getSigner` returns the raw EIP-1193 provider. Wrap it in viem's
105
- * `createWalletClient` or ethers' `BrowserProvider` at the call site.
63
+ * `disconnect` calls `wallet_revokePermissions`, which many wallets don't
64
+ * implement and silently ignore, so their own auto-reconnect may outlive it.
65
+ * `getBalance()` labels the native balance `"ETH"` on every EVM chain.
106
66
  */
107
67
  declare const buildEvmAdapter: (info: Eip6963ProviderInfo, provider: Eip1193Provider) => WalletAdapter;
108
68
  //#endregion
@@ -115,45 +75,31 @@ declare const buildEvmAdapter: (info: Eip6963ProviderInfo, provider: Eip1193Prov
115
75
  declare const GENERIC_INJECTED_ICON = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNMjEgMTJWN0g1YTIgMiAwIDAgMSAwLTQgaDE0djQiLz48cGF0aCBkPSJNMyA1djE0YTIgMiAwIDAgMCAyIDJoMTZ2LTVNMjEgMTIgaC0zYTIgMiAwIDAgMC0yIDJ2MGEyIDIgMCAwIDAgMiAyaDN6Ii8+PC9zdmc+";
116
76
  type InjectedDiscoveryOptions = {
117
77
  /**
118
- * Predicate the fallback consults before emitting; if any EIP-6963
119
- * adapter has been registered by the time the settle timer fires,
120
- * the injected fallback is skipped (the wallet is already covered
121
- * via the standards path; we don't want a duplicate).
122
- *
123
- * `discoverWalletAdapters` wires this to the shared `seen` set so
124
- * the dedupe is automatic. Callers using `discoverInjectedAdapter`
125
- * directly should pass their own check or omit it (always emit).
78
+ * Consulted before emitting: an already-registered EIP-6963 adapter means
79
+ * the wallet is covered by the standards path and emitting would duplicate
80
+ * it. Omit to always emit.
126
81
  */
127
82
  hasAnyEip6963Adapter?: () => boolean;
128
83
  /**
129
- * How long to wait for EIP-6963 announcements before falling back
130
- * to `window.ethereum`. Most wallets announce within the first
131
- * frame; 150ms is a conservative settle window that keeps the
132
- * picker responsive while giving slow announcements time to land.
84
+ * Most wallets announce within the first frame; 150ms leaves room for slow
85
+ * announcements without stalling the picker.
133
86
  */
134
87
  settleMs?: number;
135
88
  /** Override the global `window` reference (tests, iframes). */
136
89
  target?: {
137
- ethereum?: unknown;
90
+ ethereum?: Partial<Eip1193Provider>;
138
91
  } | null;
139
92
  };
93
+ declare global {
94
+ interface Window {
95
+ ethereum?: Partial<Eip1193Provider>;
96
+ }
97
+ }
98
+ declare const isEip1193Provider: (value: WalletSigner | undefined) => value is Eip1193Provider;
140
99
  /**
141
- * Last-resort discovery path for EVM wallets that DON'T announce via
142
- * EIP-6963. Most modern wallets (MetaMask, Rabby, Coinbase, Phantom,
143
- * Brave, etc.) do announce, so the standards-based path
144
- * (`discoverEvmAdapters`) covers them. This module catches the long
145
- * tail: regional wallets (KuCoin, Bitget, BitKeep, Okto, Frontier)
146
- * and older builds that only expose `window.ethereum`.
147
- *
148
- * **Coordination**: the fallback waits `settleMs` (default 150ms)
149
- * after subscription, then consults `hasAnyEip6963Adapter` if
150
- * provided. If any EIP-6963 adapter has already been registered, we
151
- * skip emission; assume the same wallet is also speaking standards
152
- * and we'd just be duplicating. If no EIP-6963 adapter showed up but
153
- * `window.ethereum` does, we emit a generic adapter.
154
- *
155
- * The emitted adapter has `rdns: "injected:legacy"` so consumers can
156
- * distinguish it from EIP-6963 adapters in the picker UI.
100
+ * Last-resort path for the long tail of EVM wallets that expose only
101
+ * `window.ethereum` and never announce via EIP-6963. The emitted adapter
102
+ * carries `rdns: "injected:legacy"` so consumers can tell it apart.
157
103
  */
158
104
  declare const discoverInjectedAdapter: (onAdapter: (adapter: WalletAdapter) => void, options?: InjectedDiscoveryOptions) => (() => void);
159
105
  //#endregion
@@ -218,26 +164,16 @@ declare const resolveEip6963Capabilities: (input: Eip6963CapabilityInput) => Wal
218
164
  //#endregion
219
165
  //#region src/discoverer.d.ts
220
166
  /**
221
- * EVM's `PlatformDiscoverer`: EIP-6963 announcements primary, with the
222
- * legacy `window.ethereum` injected fallback wired to defer until
167
+ * EIP-6963 is primary; the legacy `window.ethereum` fallback defers until
223
168
  * EIP-6963 has had a chance to fire.
224
- *
225
- * Consumers using `@usebutr/wallets`'s `autoDiscovery()` get this
226
- * wired in automatically. Direct consumers who don't want the
227
- * injected fallback build their own discovery source from
228
- * `discoverEvmAdapters` alone.
229
169
  */
230
170
  declare const evmDiscoverer: PlatformDiscoverer;
231
171
  //#endregion
232
172
  //#region src/signer-augmentation.d.ts
233
173
  /**
234
- * Augment `@usebutr/core`'s `SignerForPlatform` registry with the EVM
235
- * entry. Consumers that import anything from `@usebutr/evm` get the
236
- * `evm` key typed as `Eip1193Provider` transitively.
237
- *
238
174
  * `getSigner()` itself still returns `Promise<unknown>` (see
239
- * `packages/core/src/types/signer.ts` for why); this declaration only
240
- * affects what `SignerForPlatform["evm"]` evaluates to.
175
+ * `packages/core/src/types/signer.ts` for why); this only changes what
176
+ * `SignerForPlatform["evm"]` evaluates to.
241
177
  */
242
178
  declare module "@usebutr/core" {
243
179
  interface SignerForPlatform {
@@ -245,5 +181,5 @@ declare module "@usebutr/core" {
245
181
  }
246
182
  }
247
183
  //#endregion
248
- export { ANNOUNCE_EVENT, EIP6963_RDNS_WITH_REQUEST_ACCOUNTS, EVM_CHAINS, EVM_CHAINS_LIST, type Eip1193Listener, type Eip1193Provider, type Eip1193RequestArgs, type Eip6963AnnounceEvent, type Eip6963CapabilityInput, type Eip6963ProviderDetail, type Eip6963ProviderInfo, GENERIC_INJECTED_ICON, type InjectedDiscoveryOptions, REQUEST_EVENT, buildEvmAdapter, bytesToHex, chainIdDecimalToHex, chainIdHexToDecimal, discoverEvmAdapters, discoverInjectedAdapter, evmDiscoverer, formatEther, hexToBytes, resolveEip6963Capabilities };
184
+ export { ANNOUNCE_EVENT, EIP6963_RDNS_WITH_REQUEST_ACCOUNTS, EVM_CHAINS, EVM_CHAINS_LIST, type Eip1193Listener, type Eip1193Object, type Eip1193Provider, type Eip1193RequestArgs, type Eip1193Value, type Eip6963AnnounceEvent, type Eip6963CapabilityInput, type Eip6963ProviderDetail, type Eip6963ProviderInfo, GENERIC_INJECTED_ICON, type InjectedDiscoveryOptions, REQUEST_EVENT, buildEvmAdapter, bytesToHex, chainIdDecimalToHex, chainIdHexToDecimal, discoverEvmAdapters, discoverInjectedAdapter, evmDiscoverer, formatEther, hexToBytes, isEip1193Provider, resolveEip6963Capabilities };
249
185
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/eip1193.ts","../src/eip6963.ts","../src/eip6963-adapter.ts","../src/injected.ts","../src/chains.ts","../src/capabilities.ts","../src/discoverer.ts","../src/signer-augmentation.ts"],"mappings":";;;;;;;;;;;;KAWK;EACH;EACA,SAAS;;KAGN,sBAAsB,MAAM;KAE5B;EACH,KAAK,eAAe,UAAU;EAC9B,iBAAiB,eAAe,UAAU;EAC1C,UAAU,MAAM,uBAAuB;;KAGpC;;EAEH;;EAEA;;;EAGA;;EAEA;;KAGG;EACH,MAAM;EACN,UAAU;;KAGP,uBAAuB,YAAY;;;cCpClC;cACA;KAED,mBAAmB,SAAS,eAAe,MAAM;KAEjD;;;;EAIH,SAAS;;;;;;;;;;;;;;;;;;;;cAyDL,sBACJ,WAAW,iBACX,UAAS;;;cChCL,sBAAuB;cACvB,sBAAuB;;;cAIvB,cAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAgHf,kBAAmB,MAAM,qBAAqB,UAAU,oBAAkB;;;;;;;;cCpJ1E;KAKD;;;;;;;;;;;EAWH;;;;;;;EAOA;;EAEA;IAAW;;;;;;;;;;;;;;;;;;;;;cAuCP,0BACJ,YAAY,SAAS,wBACrB,UAAS;;;;;;;;cCrEL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAeA,iBAAiB,cAAc;;;cCT/B,oCAAkC;KAInC;EAA2B;;cAE1B,6BAA8B,OAAO,2BAAyB;;;;;;;;;;;;;cCJ9D,eAAe;;;;;;;;;;;;;YCFT;IACR,KAAK"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/eip1193.ts","../src/eip6963.ts","../src/evm-balance.ts","../src/eip6963-adapter.ts","../src/injected.ts","../src/chains.ts","../src/capabilities.ts","../src/discoverer.ts","../src/signer-augmentation.ts"],"mappings":";;;;;;;KAMK;GAAmB,cAAc;;KACjC,2DAMD,aACA,cAAc,gBACd;KAEC;EACH;EACA,SAAS,cAAc,gBAAgB;;KAGpC,sBAAsB,MAAM,cAAc;KAE1C;EACH,KAAK,eAAe,UAAU;EAC9B,iBAAiB,eAAe,UAAU;EAC1C,UAAU,MAAM,uBAAuB,QAAQ;;KAG5C;;EAEH;;EAEA;;;EAGA;;EAEA;;KAGG;EACH,MAAM;EACN,UAAU;;KAGP,uBAAuB,YAAY;;;cC1ClC;cACA;KAED,mBAAmB,SAAS,eAAe,MAAM;KAEjD;;;;EAIH,SAAS;;;;;;;cAiDL,sBAAmB,WACZ,iBAAe,UACjB;;;cChCL,cAAW;;;cC0BX,sBAAmB;cACnB,sBAAmB;;;;;;cAoDnB,kBAAe,MAAU,qBAAmB,UAAY,oBAAkB;;;;;;;;cCtG1E;KAKD;;;;;;EAMH;;;;;EAKA;;EAEA;IAAW,WAAW,QAAQ;;;QAGxB;YAEI;IACR,WAAW,QAAQ;;;cAIjB,oBAAiB,OAAW,6BAA2B,SAAS;;;;;;cAmBhE,0BAAuB,YACf,SAAS,wBAAsB,UAClC;;;;;;;;cCpDL;;aAEF;aACA;aACA;aACA;;;aAEM;aAAmB;aAAc;aAAqB;;;aACvD;aAAiB;aAAyB;aAAqB;;;aAC1D;aAAgB;aAAkB;aAAqB;;;aACvD;aAAiB;aAAkB;aAAqB;;;aACzD;aAAkB;aAAiB;aAAqB;;;aACxD;aAAuB;aAAiB;aAAqB;;;cAGpE,iBAAiB,cAAc;;;cCpB/B,oCAAkC;KAInC;EAA2B;;cAE1B,6BAA0B,OAAW,2BAAyB;;;;;;;cCC9D,eAAe;;;;;;;;;YCAT;IACR,KAAK"}
package/dist/index.js CHANGED
@@ -16,82 +16,127 @@ const resolveEip6963Capabilities = (input) => ({
16
16
  });
17
17
 
18
18
  //#endregion
19
- //#region src/eip6963-adapter.ts
20
- const HEX_PREFIX = "0x";
21
- const ETH_DECIMALS = 18n;
22
- const ETH_UNIT = 10n ** ETH_DECIMALS;
23
- /** `provider.request` is typed `unknown` by design; these helpers
24
- * narrow the well-known EVM RPC shapes at the boundary with runtime
25
- * guards so call sites stay assertion-free. Malformed responses
26
- * collapse to an empty/blank value rather than throwing. */
27
- const requestStringArray = async (provider, args) => {
19
+ //#region src/eip1193.ts
20
+ /** `provider.request` is typed `unknown` by design; these helpers narrow
21
+ * the well-known EVM RPC shapes at the boundary with runtime guards so
22
+ * call sites stay assertion-free. A malformed response is reported as
23
+ * `null` / `[]` rather than a blank value, so call sites that cannot
24
+ * proceed without a real one fail instead of inventing a default. */
25
+ const requestString = async (provider, args) => {
28
26
  const result = await provider.request(args);
29
- return Array.isArray(result) ? result.filter((item) => typeof item === "string") : [];
27
+ return typeof result === "string" ? result : null;
30
28
  };
31
- const requestString = async (provider, args) => {
29
+ const requestStringArray = async (provider, args) => {
32
30
  const result = await provider.request(args);
33
- return typeof result === "string" ? result : "";
31
+ return Array.isArray(result) ? result.filter((item) => typeof item === "string") : [];
34
32
  };
35
- const toStringArray = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
36
- /** Merge a `from` address into a transaction object of unknown shape.
37
- * butr's `sendTx` accepts `tx: unknown` (viem/ethers/raw all differ);
38
- * when it's an object we overlay `from`, otherwise pass it through. */
39
- const withFrom = (tx, from) => typeof tx === "object" && tx !== null ? {
40
- ...tx,
41
- from
42
- } : tx;
43
- const chainIdHexToDecimal = (hex) => BigInt(hex).toString(10);
44
- const chainIdDecimalToHex = (dec) => `${HEX_PREFIX}${BigInt(dec).toString(16)}`;
45
- /** Pure 18-decimal wei → ether formatter. Trims trailing zeros so
46
- * `1.0` stays `1` and `1.5` stays `1.5`. */
47
- const formatEther = (wei) => {
48
- const integer = wei / ETH_UNIT;
49
- const remainder = wei % ETH_UNIT;
50
- if (remainder === 0n) return integer.toString();
51
- const fraction = remainder.toString().padStart(Number(ETH_DECIMALS), "0").replace(new RegExp("0+$", "v"), "");
52
- return fraction.length > 0 ? `${integer}.${fraction}` : integer.toString();
33
+
34
+ //#endregion
35
+ //#region src/evm-balance.ts
36
+ const HEX_PREFIX$1 = "0x";
37
+ const ETH_DECIMALS = 18n;
38
+ const ERC20_BALANCE_OF_SELECTOR = "0x70a08231";
39
+ const ERC20_DECIMALS_SELECTOR = "0x313ce567";
40
+ const ERC20_SYMBOL_SELECTOR = "0x95d89b41";
41
+ /** A failed read must not render as a confident zero balance. */
42
+ const requireHex = (hex, label) => {
43
+ if (hex === null) throw new Error(`Wallet returned a malformed ${label} response`);
44
+ return hex;
53
45
  };
54
- /** General-purpose `value / 10^decimals` formatter that trims trailing
55
- * zeros. Mirrors `formatEther` but for arbitrary token decimals. */
56
- const formatTokenUnits = (raw, decimals) => {
46
+ const formatUnits = (raw, decimals) => {
57
47
  if (decimals === 0) return raw.toString();
58
48
  const unit = 10n ** BigInt(decimals);
59
49
  const integer = raw / unit;
60
50
  const remainder = raw % unit;
61
51
  if (remainder === 0n) return integer.toString();
62
- const fraction = remainder.toString().padStart(decimals, "0").replace(new RegExp("0+$", "v"), "");
63
- return fraction.length > 0 ? `${integer}.${fraction}` : integer.toString();
52
+ return `${integer}.${remainder.toString().padStart(decimals, "0").replace(new RegExp("0+$", "v"), "")}`;
64
53
  };
65
- const ERC20_BALANCE_OF_SELECTOR = "0x70a08231";
66
- const ERC20_DECIMALS_SELECTOR = "0x313ce567";
67
- const ERC20_SYMBOL_SELECTOR = "0x95d89b41";
54
+ const formatEther = (wei) => formatUnits(wei, Number(ETH_DECIMALS));
68
55
  const padAddressForCall = (address) => address.replace(new RegExp("^0x", "v"), "").toLowerCase().padStart(64, "0");
69
- /**
70
- * Decode an ABI-encoded `string` return value. Layout:
71
- * bytes 0–31 : offset to the data (always 0x20 for a single string)
72
- * bytes 32–63 : string length (big-endian uint256)
73
- * bytes 64+ : UTF-8 bytes, right-padded to a 32-byte boundary
74
- *
75
- * Some non-standard tokens (early MakerDAO, etc.) return a `bytes32`
76
- * directly instead of a length-prefixed string; the fallback strips
77
- * trailing nulls and decodes the raw bytes.
78
- */
79
56
  const decodeAbiString = (hex) => {
80
- const clean = hex.startsWith(HEX_PREFIX) ? hex.slice(2) : hex;
57
+ const clean = hex.startsWith(HEX_PREFIX$1) ? hex.slice(2) : hex;
81
58
  if (clean.length === 0) return "";
82
59
  if (clean.length >= 128) {
83
60
  const lengthHex = clean.slice(64, 128);
84
61
  const length = Number(BigInt(`0x${lengthHex}`));
85
- if (length > 0 && clean.length >= 128 + length * 2) {
86
- const dataHex = clean.slice(128, 128 + length * 2);
87
- return new TextDecoder().decode(hexToBytes$1(dataHex));
88
- }
62
+ if (length > 0 && clean.length >= 128 + length * 2) return new TextDecoder().decode(hexToBytes$1(clean.slice(128, 128 + length * 2)));
63
+ }
64
+ const bytes = hexToBytes$1(clean.slice(0, 64));
65
+ let end = bytes.length;
66
+ while (end > 0 && bytes[end - 1] === 0) end -= 1;
67
+ return new TextDecoder().decode(bytes.subarray(0, end));
68
+ };
69
+ const readEvmBalance = async (provider, address, tokenAddress) => {
70
+ if (tokenAddress === void 0 || tokenAddress === "") {
71
+ const balanceHex = await requestString(provider, {
72
+ method: "eth_getBalance",
73
+ params: [address, "latest"]
74
+ });
75
+ const value = BigInt(requireHex(balanceHex, "eth_getBalance"));
76
+ return {
77
+ decimals: Number(ETH_DECIMALS),
78
+ formatted: formatEther(value),
79
+ symbol: "ETH",
80
+ value
81
+ };
89
82
  }
90
- const buf = hexToBytes$1(clean.slice(0, 64));
91
- let end = buf.length;
92
- while (end > 0 && buf[end - 1] === 0) end -= 1;
93
- return new TextDecoder().decode(buf.subarray(0, end));
83
+ const [balanceHex, decimalsHex, symbolHex] = await Promise.all([
84
+ requestString(provider, {
85
+ method: "eth_call",
86
+ params: [{
87
+ data: `${ERC20_BALANCE_OF_SELECTOR}${padAddressForCall(address)}`,
88
+ to: tokenAddress
89
+ }, "latest"]
90
+ }),
91
+ requestString(provider, {
92
+ method: "eth_call",
93
+ params: [{
94
+ data: ERC20_DECIMALS_SELECTOR,
95
+ to: tokenAddress
96
+ }, "latest"]
97
+ }),
98
+ requestString(provider, {
99
+ method: "eth_call",
100
+ params: [{
101
+ data: ERC20_SYMBOL_SELECTOR,
102
+ to: tokenAddress
103
+ }, "latest"]
104
+ })
105
+ ]);
106
+ const value = BigInt(requireHex(balanceHex, "balanceOf()"));
107
+ const decimals = Number(BigInt(requireHex(decimalsHex, "decimals()")));
108
+ return {
109
+ decimals,
110
+ formatted: formatUnits(value, decimals),
111
+ symbol: decodeAbiString(symbolHex ?? ""),
112
+ value
113
+ };
114
+ };
115
+
116
+ //#endregion
117
+ //#region src/eip6963-adapter.ts
118
+ const HEX_PREFIX = "0x";
119
+ const toStringArray = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
120
+ const toEip1193Value = (value) => {
121
+ if (typeof value === "function") throw new TypeError("EVM transactions cannot contain functions");
122
+ if (Array.isArray(value)) return value.map(toEip1193Value);
123
+ if (value instanceof Uint8Array) return value;
124
+ if (typeof value === "object" && value !== null) {
125
+ const result = {};
126
+ for (const [key, nested] of Object.entries(value)) result[key] = nested === void 0 ? void 0 : toEip1193Value(nested);
127
+ return result;
128
+ }
129
+ return value;
94
130
  };
131
+ const withFrom = (tx, from) => {
132
+ if (typeof tx === "string" || tx instanceof Uint8Array) return tx;
133
+ const result = {};
134
+ for (const [key, value] of Object.entries(tx)) result[key] = value === void 0 ? void 0 : toEip1193Value(value);
135
+ if (from !== void 0) result.from = from;
136
+ return result;
137
+ };
138
+ const chainIdHexToDecimal = (hex) => BigInt(hex).toString(10);
139
+ const chainIdDecimalToHex = (dec) => `${HEX_PREFIX}${BigInt(dec).toString(16)}`;
95
140
  const buildEvmChain = (chainIdHex, walletName) => {
96
141
  const reference = chainIdHexToDecimal(chainIdHex);
97
142
  return {
@@ -106,36 +151,26 @@ const buildEvmAccount = (address, chain) => ({
106
151
  id: `${chain.id}:${address.toLowerCase()}`,
107
152
  walletAddress: address
108
153
  });
154
+ /** Rejections propagate: `getAccount`/`getAccounts` surface the wallet's
155
+ * own provider error, while the `subscribe` bridge swallows it because a
156
+ * later event retries synchronization. */
157
+ const readAccountState = async (provider, walletName, options = {}) => {
158
+ const accounts = options.accounts ?? await requestStringArray(provider, { method: "eth_accounts" });
159
+ if (accounts.length === 0) return null;
160
+ const chainIdHex = options.chainIdHex ?? await requestString(provider, { method: "eth_chainId" });
161
+ if (chainIdHex === null) throw new Error("Wallet returned a malformed eth_chainId response");
162
+ const chain = buildEvmChain(chainIdHex, walletName);
163
+ const builtAccounts = accounts.map((address) => buildEvmAccount(address, chain));
164
+ const account = builtAccounts[0];
165
+ return account === void 0 ? null : {
166
+ account,
167
+ accounts: builtAccounts
168
+ };
169
+ };
109
170
  /**
110
- * Adapt an EIP-1193 provider (announced via EIP-6963) into a butr
111
- * `WalletAdapter`. The returned adapter covers the full `Connector +
112
- * Wallet` surface; consumers can pass it through `createConnector` like
113
- * any hand-written adapter.
114
- *
115
- * **Caveats**
116
- *
117
- * - `disconnect` calls `wallet_revokePermissions`. Many wallets don't
118
- * implement that method and silently ignore the call. The reducer
119
- * will still mark the wallet as disconnected on butr's side; the
120
- * wallet's own auto-reconnect heuristic may or may not honour it.
121
- * - `switchAccount` is intentionally unimplemented because EIP-1193 has
122
- * no silent "use address X" RPC. The active account is whichever one
123
- * the wallet exposes first; the user changes it through the wallet's
124
- * own UI. butr's `subscribe` bridge auto-updates the pool entry when
125
- * `accountsChanged` fires. Call `requestAccounts` if you want the
126
- * wallet to expose more accounts.
127
- * - `getBalance()` returns the native ETH balance with symbol `"ETH"`
128
- * regardless of which EVM chain the wallet is currently on. Consumers
129
- * that target multiple EVM chains should overlay the symbol via
130
- * their own logic.
131
- * - `getBalance(tokenAddress)` reads the ERC20 balance for the active
132
- * account on the currently-connected chain. The token's own
133
- * `decimals()` and `symbol()` are queried for accurate formatting;
134
- * if `symbol()` is non-standard (bytes32) it's decoded as best-
135
- * effort. Routed through `eth_call` on the wallet's own provider;
136
- * the demo doesn't ship a separate RPC.
137
- * - `getSigner` returns the raw EIP-1193 provider. Wrap it in viem's
138
- * `createWalletClient` or ethers' `BrowserProvider` at the call site.
171
+ * `disconnect` calls `wallet_revokePermissions`, which many wallets don't
172
+ * implement and silently ignore, so their own auto-reconnect may outlive it.
173
+ * `getBalance()` labels the native balance `"ETH"` on every EVM chain.
139
174
  */
140
175
  const buildEvmAdapter = (info, provider) => {
141
176
  return {
@@ -157,70 +192,15 @@ const buildEvmAdapter = (info, provider) => {
157
192
  } catch {}
158
193
  },
159
194
  async getAccount() {
160
- const accounts = await requestStringArray(provider, { method: "eth_accounts" });
161
- if (accounts.length === 0) return null;
162
- const chainIdHex = await requestString(provider, { method: "eth_chainId" });
163
- const chain = buildEvmChain(chainIdHex, info.name);
164
- const first = accounts[0];
165
- if (first === void 0) return null;
166
- return buildEvmAccount(first, chain);
195
+ return (await readAccountState(provider, info.name))?.account ?? null;
167
196
  },
168
197
  async getAccounts() {
169
- const accounts = await requestStringArray(provider, { method: "eth_accounts" });
170
- if (accounts.length === 0) return [];
171
- const chainIdHex = await requestString(provider, { method: "eth_chainId" });
172
- const chain = buildEvmChain(chainIdHex, info.name);
173
- return accounts.map((addr) => buildEvmAccount(addr, chain));
198
+ return (await readAccountState(provider, info.name))?.accounts ?? [];
174
199
  },
175
200
  async getBalance(mint) {
176
201
  const first = (await requestStringArray(provider, { method: "eth_accounts" }))[0];
177
202
  if (first === void 0) throw new Error("No connected account");
178
- if (mint === void 0 || mint === "") {
179
- const balanceHex = await requestString(provider, {
180
- method: "eth_getBalance",
181
- params: [first, "latest"]
182
- });
183
- const value = BigInt(balanceHex);
184
- return {
185
- decimals: Number(ETH_DECIMALS),
186
- formatted: formatEther(value),
187
- symbol: "ETH",
188
- value
189
- };
190
- }
191
- const balanceCallData = `${ERC20_BALANCE_OF_SELECTOR}${padAddressForCall(first)}`;
192
- const [balanceHex, decimalsHex, symbolHex] = await Promise.all([
193
- requestString(provider, {
194
- method: "eth_call",
195
- params: [{
196
- data: balanceCallData,
197
- to: mint
198
- }, "latest"]
199
- }),
200
- requestString(provider, {
201
- method: "eth_call",
202
- params: [{
203
- data: ERC20_DECIMALS_SELECTOR,
204
- to: mint
205
- }, "latest"]
206
- }),
207
- requestString(provider, {
208
- method: "eth_call",
209
- params: [{
210
- data: ERC20_SYMBOL_SELECTOR,
211
- to: mint
212
- }, "latest"]
213
- })
214
- ]);
215
- const value = BigInt(balanceHex);
216
- const decimals = Number(BigInt(decimalsHex));
217
- const symbol = decodeAbiString(symbolHex);
218
- return {
219
- decimals,
220
- formatted: formatTokenUnits(value, decimals),
221
- symbol,
222
- value
223
- };
203
+ return readEvmBalance(provider, first, mint);
224
204
  },
225
205
  getSigner: () => Promise.resolve(provider),
226
206
  async getTransactionReceipt(tx) {
@@ -253,27 +233,31 @@ const buildEvmAdapter = (info, provider) => {
253
233
  }
254
234
  },
255
235
  async sendTx(tx, account) {
256
- const txWithFrom = account ? withFrom(tx, account.walletAddress) : tx;
257
- return await requestString(provider, {
236
+ const txWithFrom = withFrom(tx, account?.walletAddress);
237
+ const hash = await requestString(provider, {
258
238
  method: "eth_sendTransaction",
259
239
  params: [txWithFrom]
260
240
  });
241
+ if (hash === null) throw new Error("Wallet returned no transaction hash");
242
+ return hash;
261
243
  },
262
244
  async sendTxToChain(tx, targetChainIdDecimal, account, cb) {
263
245
  const current = await requestString(provider, { method: "eth_chainId" });
264
246
  const targetHex = chainIdDecimalToHex(targetChainIdDecimal);
265
- if (current.toLowerCase() !== targetHex.toLowerCase()) {
247
+ if (current?.toLowerCase() !== targetHex.toLowerCase()) {
266
248
  await provider.request({
267
249
  method: "wallet_switchEthereumChain",
268
250
  params: [{ chainId: targetHex }]
269
251
  });
270
252
  cb?.();
271
253
  }
272
- const txWithFrom = account ? withFrom(tx, account.walletAddress) : tx;
273
- return await requestString(provider, {
254
+ const txWithFrom = withFrom(tx, account?.walletAddress);
255
+ const hash = await requestString(provider, {
274
256
  method: "eth_sendTransaction",
275
257
  params: [txWithFrom]
276
258
  });
259
+ if (hash === null) throw new Error("Wallet returned no transaction hash");
260
+ return hash;
277
261
  },
278
262
  async signMessage(msg, account) {
279
263
  let signer = account?.walletAddress;
@@ -283,63 +267,40 @@ const buildEvmAdapter = (info, provider) => {
283
267
  method: "personal_sign",
284
268
  params: [bytesToHexPrefixed(msg), signer]
285
269
  });
270
+ if (signatureHex === null) throw new Error("Wallet returned a malformed personal_sign response");
286
271
  return {
287
272
  signature: hexToBytes$1(signatureHex),
288
273
  signedMessage: msg
289
274
  };
290
275
  },
291
276
  subscribe(listener) {
277
+ const synchronizeAccount = async (options) => {
278
+ try {
279
+ const state = await readAccountState(provider, info.name, options);
280
+ if (state !== null) listener({
281
+ ...state,
282
+ type: "accountChanged"
283
+ });
284
+ } catch {}
285
+ };
292
286
  const onAccountsChanged = (...args) => {
293
287
  const accs = toStringArray(args[0]);
294
288
  if (accs.length === 0) {
295
289
  listener({ type: "disconnected" });
296
290
  return;
297
291
  }
298
- provider.request({ method: "eth_chainId" }).then((chainIdHex) => {
299
- const chain = buildEvmChain(typeof chainIdHex === "string" ? chainIdHex : "", info.name);
300
- const accounts = accs.map((addr) => buildEvmAccount(addr, chain));
301
- const first = accounts[0];
302
- if (first === void 0) return;
303
- listener({
304
- account: first,
305
- accounts,
306
- type: "accountChanged"
307
- });
308
- }).catch(() => {});
292
+ synchronizeAccount({ accounts: accs });
309
293
  };
310
294
  const onChainChanged = (...args) => {
311
- const chainIdHex = typeof args[0] === "string" ? args[0] : "";
312
- provider.request({ method: "eth_accounts" }).then((accounts) => {
313
- const accs = toStringArray(accounts);
314
- if (accs.length === 0) return;
315
- const chain = buildEvmChain(chainIdHex, info.name);
316
- const built = accs.map((addr) => buildEvmAccount(addr, chain));
317
- const first = built[0];
318
- if (first === void 0) return;
319
- listener({
320
- account: first,
321
- accounts: built,
322
- type: "accountChanged"
323
- });
324
- }).catch(() => {});
295
+ const [chainId] = args;
296
+ const chainIdHex = typeof chainId === "string" && chainId.length > 0 ? chainId : void 0;
297
+ synchronizeAccount({ chainIdHex });
325
298
  };
326
299
  const onDisconnect = () => {
327
300
  listener({ type: "disconnected" });
328
301
  };
329
302
  const onConnect = () => {
330
- Promise.all([provider.request({ method: "eth_accounts" }), provider.request({ method: "eth_chainId" })]).then(([accountsRaw, chainIdHex]) => {
331
- const accs = toStringArray(accountsRaw);
332
- if (accs.length === 0) return;
333
- const chain = buildEvmChain(typeof chainIdHex === "string" ? chainIdHex : "", info.name);
334
- const built = accs.map((addr) => buildEvmAccount(addr, chain));
335
- const first = built[0];
336
- if (first === void 0) return;
337
- listener({
338
- account: first,
339
- accounts: built,
340
- type: "accountChanged"
341
- });
342
- }).catch(() => {});
303
+ synchronizeAccount();
343
304
  };
344
305
  provider.on("accountsChanged", onAccountsChanged);
345
306
  provider.on("chainChanged", onChainChanged);
@@ -353,9 +314,11 @@ const buildEvmAdapter = (info, provider) => {
353
314
  };
354
315
  },
355
316
  async switchChain(chain) {
317
+ const targetHex = chainIdDecimalToHex(chain.reference);
318
+ if ((await requestString(provider, { method: "eth_chainId" }))?.toLowerCase() === targetHex.toLowerCase()) return;
356
319
  await provider.request({
357
320
  method: "wallet_switchEthereumChain",
358
- params: [{ chainId: chainIdDecimalToHex(chain.reference) }]
321
+ params: [{ chainId: targetHex }]
359
322
  });
360
323
  }
361
324
  };
@@ -371,31 +334,16 @@ const isEip6963AnnounceEvent = (event) => {
371
334
  if (typeof detail !== "object" || detail === null) return false;
372
335
  if (!("info" in detail) || !("provider" in detail)) return false;
373
336
  const { info, provider } = detail;
374
- return typeof info === "object" && info !== null && "rdns" in info && typeof info.rdns === "string" && typeof provider === "object" && provider !== null;
337
+ return typeof info === "object" && info !== null && "icon" in info && typeof info.icon === "string" && "name" in info && typeof info.name === "string" && "rdns" in info && typeof info.rdns === "string" && "uuid" in info && typeof info.uuid === "string" && typeof provider === "object" && provider !== null && "on" in provider && typeof provider.on === "function" && "removeListener" in provider && typeof provider.removeListener === "function" && "request" in provider && typeof provider.request === "function";
375
338
  };
376
- const isEventTarget = (value) => typeof value === "object" && value !== null && "addEventListener" in value && typeof value.addEventListener === "function";
377
339
  const resolveTarget = (target) => {
378
340
  if (target !== void 0) return target;
379
- const candidate = globalThis.window;
380
- return isEventTarget(candidate) ? candidate : null;
341
+ return globalThis.window ?? null;
381
342
  };
382
343
  /**
383
- * Subscribe to EIP-6963 wallet announcements. Spec:
384
- * https://eips.ethereum.org/EIPS/eip-6963
385
- *
386
- * Flow:
387
- * 1. Page registers a listener for `eip6963:announceProvider`.
388
- * 2. Page dispatches `eip6963:requestProvider` to kick wallets that
389
- * have already loaded.
390
- * 3. Wallets that load later still dispatch their own announcement
391
- * when they boot, so the listener stays attached for the lifetime
392
- * of the discovery session.
393
- * 4. Dedup by `info.rdns`; wallets occasionally re-announce, and
394
- * `info.uuid` is regenerated per page load so it's not stable.
395
- *
396
- * Returns an unsubscribe function that removes the listener.
397
- * In non-browser environments (SSR, tests with no window) returns
398
- * a noop unsubscribe and never fires the callback.
344
+ * Spec: https://eips.ethereum.org/EIPS/eip-6963. Dedupe is by `info.rdns`
345
+ * because `info.uuid` is regenerated per page load. The listener stays
346
+ * attached for the session so wallets that boot late still register.
399
347
  */
400
348
  const discoverEvmAdapters = (onAdapter, options = {}) => {
401
349
  const target = resolveTarget(options.target);
@@ -425,30 +373,15 @@ const discoverEvmAdapters = (onAdapter, options = {}) => {
425
373
  */
426
374
  const GENERIC_INJECTED_ICON = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNMjEgMTJWN0g1YTIgMiAwIDAgMSAwLTQgaDE0djQiLz48cGF0aCBkPSJNMyA1djE0YTIgMiAwIDAgMCAyIDJoMTZ2LTVNMjEgMTIgaC0zYTIgMiAwIDAgMC0yIDJ2MGEyIDIgMCAwIDAgMiAyaDN6Ii8+PC9zdmc+";
427
375
  const DEFAULT_SETTLE_MS = 150;
428
- const isEip1193Provider = (value) => typeof value === "object" && value !== null && "request" in value && typeof value.request === "function";
376
+ const isEip1193Provider = (value) => value !== void 0 && "on" in value && typeof value.on === "function" && "removeListener" in value && typeof value.removeListener === "function" && "request" in value && typeof value.request === "function";
429
377
  const readEthereum = (target) => {
430
- const host = target === void 0 ? typeof window === "undefined" ? null : window : target;
431
- if (typeof host !== "object" || host === null || !("ethereum" in host)) return null;
432
- const eth = host.ethereum;
433
- return isEip1193Provider(eth) ? eth : null;
378
+ const host = target === void 0 ? globalThis.window : target;
379
+ return isEip1193Provider(host?.ethereum) ? host.ethereum : null;
434
380
  };
435
381
  /**
436
- * Last-resort discovery path for EVM wallets that DON'T announce via
437
- * EIP-6963. Most modern wallets (MetaMask, Rabby, Coinbase, Phantom,
438
- * Brave, etc.) do announce, so the standards-based path
439
- * (`discoverEvmAdapters`) covers them. This module catches the long
440
- * tail: regional wallets (KuCoin, Bitget, BitKeep, Okto, Frontier)
441
- * and older builds that only expose `window.ethereum`.
442
- *
443
- * **Coordination**: the fallback waits `settleMs` (default 150ms)
444
- * after subscription, then consults `hasAnyEip6963Adapter` if
445
- * provided. If any EIP-6963 adapter has already been registered, we
446
- * skip emission; assume the same wallet is also speaking standards
447
- * and we'd just be duplicating. If no EIP-6963 adapter showed up but
448
- * `window.ethereum` does, we emit a generic adapter.
449
- *
450
- * The emitted adapter has `rdns: "injected:legacy"` so consumers can
451
- * distinguish it from EIP-6963 adapters in the picker UI.
382
+ * Last-resort path for the long tail of EVM wallets that expose only
383
+ * `window.ethereum` and never announce via EIP-6963. The emitted adapter
384
+ * carries `rdns: "injected:legacy"` so consumers can tell it apart.
452
385
  */
453
386
  const discoverInjectedAdapter = (onAdapter, options = {}) => {
454
387
  const settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;
@@ -527,14 +460,8 @@ const EVM_CHAINS_LIST = Object.values(EVM_CHAINS);
527
460
  //#endregion
528
461
  //#region src/discoverer.ts
529
462
  /**
530
- * EVM's `PlatformDiscoverer`: EIP-6963 announcements primary, with the
531
- * legacy `window.ethereum` injected fallback wired to defer until
463
+ * EIP-6963 is primary; the legacy `window.ethereum` fallback defers until
532
464
  * EIP-6963 has had a chance to fire.
533
- *
534
- * Consumers using `@usebutr/wallets`'s `autoDiscovery()` get this
535
- * wired in automatically. Direct consumers who don't want the
536
- * injected fallback build their own discovery source from
537
- * `discoverEvmAdapters` alone.
538
465
  */
539
466
  const evmDiscoverer = {
540
467
  fallback: { subscribe: (onAdapter, opts) => discoverInjectedAdapter(onAdapter, { hasAnyEip6963Adapter: opts.hasAnyPrimaryAdapter }) },
@@ -542,5 +469,5 @@ const evmDiscoverer = {
542
469
  };
543
470
 
544
471
  //#endregion
545
- export { ANNOUNCE_EVENT, EIP6963_RDNS_WITH_REQUEST_ACCOUNTS, EVM_CHAINS, EVM_CHAINS_LIST, GENERIC_INJECTED_ICON, REQUEST_EVENT, buildEvmAdapter, bytesToHex, chainIdDecimalToHex, chainIdHexToDecimal, discoverEvmAdapters, discoverInjectedAdapter, evmDiscoverer, formatEther, hexToBytes, resolveEip6963Capabilities };
472
+ export { ANNOUNCE_EVENT, EIP6963_RDNS_WITH_REQUEST_ACCOUNTS, EVM_CHAINS, EVM_CHAINS_LIST, GENERIC_INJECTED_ICON, REQUEST_EVENT, buildEvmAdapter, bytesToHex, chainIdDecimalToHex, chainIdHexToDecimal, discoverEvmAdapters, discoverInjectedAdapter, evmDiscoverer, formatEther, hexToBytes, isEip1193Provider, resolveEip6963Capabilities };
546
473
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["hexToBytes","bytesToHex"],"sources":["../src/capabilities.ts","../src/eip6963-adapter.ts","../src/eip6963.ts","../src/injected.ts","../src/chains.ts","../src/discoverer.ts"],"sourcesContent":["import type { WalletCapabilities } from \"@usebutr/core\";\n\n// Allow-list of EIP-6963 wallets whose `wallet_requestPermissions` call\n// actually surfaces a fresh account-picker UI (the spec intent).\n// Every other tested wallet either rejects the method (Phantom EVM,\n// Coinbase Wallet) or silently returns the existing accounts (Rabby,\n// OKX, Binance, Backpack EVM), making the affordance a no-op.\n//\n// Listed by EIP-6963 `rdns`. Add a wallet here only after verifying\n// against the real install that calling `requestAccounts` surfaces a\n// picker. The capability flag gates UI affordances: `requestAccounts`\n// stays callable on every adapter for consumers with wallet-specific\n// flows.\nconst EIP6963_RDNS_WITH_REQUEST_ACCOUNTS = new Set<string>([\n \"io.metamask\", // MetaMask: verified May 2026\n]);\n\ntype Eip6963CapabilityInput = { rdns: string };\n\nconst resolveEip6963Capabilities = (input: Eip6963CapabilityInput): WalletCapabilities => ({\n getBalance: true,\n getTransactionReceipt: true,\n requestAccounts: EIP6963_RDNS_WITH_REQUEST_ACCOUNTS.has(input.rdns),\n sendTransaction: true,\n signIn: false,\n signMessage: true,\n // EVM adapters don't expose a standalone signTransaction (most\n // wallets reject eth_signTransaction); use sendTx.\n signTransaction: false,\n subscribe: true,\n switchAccount: false,\n switchChain: true,\n});\n\nexport type { Eip6963CapabilityInput };\nexport { EIP6963_RDNS_WITH_REQUEST_ACCOUNTS, resolveEip6963Capabilities };\n","import type { Account, ChainBase, WalletAdapter } from \"@usebutr/core\";\nimport { bytesToHexPrefixed as bytesToHex, hexToBytes, sanitizeIcon } from \"@usebutr/core\";\n\nimport { resolveEip6963Capabilities } from \"./capabilities\";\nimport type { Eip1193Listener, Eip1193Provider, Eip6963ProviderInfo } from \"./eip1193\";\n\nconst HEX_PREFIX = \"0x\";\nconst ETH_DECIMALS = 18n;\nconst ETH_UNIT = 10n ** ETH_DECIMALS;\n\n/** `provider.request` is typed `unknown` by design; these helpers\n * narrow the well-known EVM RPC shapes at the boundary with runtime\n * guards so call sites stay assertion-free. Malformed responses\n * collapse to an empty/blank value rather than throwing. */\nconst requestStringArray = async (\n provider: Eip1193Provider,\n args: { method: string; params?: ReadonlyArray<unknown> },\n): Promise<Array<string>> => {\n const result = await provider.request(args);\n return Array.isArray(result)\n ? result.filter((item): item is string => typeof item === \"string\")\n : [];\n};\n\nconst requestString = async (\n provider: Eip1193Provider,\n args: { method: string; params?: ReadonlyArray<unknown> },\n): Promise<string> => {\n const result = await provider.request(args);\n return typeof result === \"string\" ? result : \"\";\n};\n\nconst toStringArray = (value: unknown): Array<string> =>\n Array.isArray(value) ? value.filter((item): item is string => typeof item === \"string\") : [];\n\n/** Merge a `from` address into a transaction object of unknown shape.\n * butr's `sendTx` accepts `tx: unknown` (viem/ethers/raw all differ);\n * when it's an object we overlay `from`, otherwise pass it through. */\nconst withFrom = (tx: unknown, from: string): unknown =>\n typeof tx === \"object\" && tx !== null ? { ...tx, from } : tx;\n\nconst chainIdHexToDecimal = (hex: string): string => BigInt(hex).toString(10);\nconst chainIdDecimalToHex = (dec: string): string => `${HEX_PREFIX}${BigInt(dec).toString(16)}`;\n\n/** Pure 18-decimal wei → ether formatter. Trims trailing zeros so\n * `1.0` stays `1` and `1.5` stays `1.5`. */\nconst formatEther = (wei: bigint): string => {\n const integer = wei / ETH_UNIT;\n const remainder = wei % ETH_UNIT;\n if (remainder === 0n) {\n return integer.toString();\n }\n const fraction = remainder.toString().padStart(Number(ETH_DECIMALS), \"0\").replace(/0+$/v, \"\");\n return fraction.length > 0 ? `${integer}.${fraction}` : integer.toString();\n};\n\n/** General-purpose `value / 10^decimals` formatter that trims trailing\n * zeros. Mirrors `formatEther` but for arbitrary token decimals. */\nconst formatTokenUnits = (raw: bigint, decimals: number): string => {\n if (decimals === 0) {\n return raw.toString();\n }\n const unit = 10n ** BigInt(decimals);\n const integer = raw / unit;\n const remainder = raw % unit;\n if (remainder === 0n) {\n return integer.toString();\n }\n const fraction = remainder.toString().padStart(decimals, \"0\").replace(/0+$/v, \"\");\n return fraction.length > 0 ? `${integer}.${fraction}` : integer.toString();\n};\n\nconst ERC20_BALANCE_OF_SELECTOR = \"0x70a08231\";\nconst ERC20_DECIMALS_SELECTOR = \"0x313ce567\";\nconst ERC20_SYMBOL_SELECTOR = \"0x95d89b41\";\n\nconst padAddressForCall = (address: string): string =>\n address.replace(/^0x/v, \"\").toLowerCase().padStart(64, \"0\");\n\n/**\n * Decode an ABI-encoded `string` return value. Layout:\n * bytes 0–31 : offset to the data (always 0x20 for a single string)\n * bytes 32–63 : string length (big-endian uint256)\n * bytes 64+ : UTF-8 bytes, right-padded to a 32-byte boundary\n *\n * Some non-standard tokens (early MakerDAO, etc.) return a `bytes32`\n * directly instead of a length-prefixed string; the fallback strips\n * trailing nulls and decodes the raw bytes.\n */\nconst decodeAbiString = (hex: string): string => {\n const clean = hex.startsWith(HEX_PREFIX) ? hex.slice(2) : hex;\n if (clean.length === 0) {\n return \"\";\n }\n if (clean.length >= 128) {\n const lengthHex = clean.slice(64, 128);\n const length = Number(BigInt(`0x${lengthHex}`));\n if (length > 0 && clean.length >= 128 + length * 2) {\n const dataHex = clean.slice(128, 128 + length * 2);\n return new TextDecoder().decode(hexToBytes(dataHex));\n }\n }\n // bytes32 fallback; trim trailing zero bytes.\n const buf = hexToBytes(clean.slice(0, 64));\n let end = buf.length;\n while (end > 0 && buf[end - 1] === 0) {\n end -= 1;\n }\n return new TextDecoder().decode(buf.subarray(0, end));\n};\n\nconst buildEvmChain = (chainIdHex: string, walletName: string): ChainBase => {\n const reference = chainIdHexToDecimal(chainIdHex);\n return {\n id: `eip155:${reference}`,\n name: walletName,\n namespace: \"eip155\",\n reference,\n };\n};\n\nconst buildEvmAccount = (address: string, chain: ChainBase): Account => ({\n chain,\n id: `${chain.id}:${address.toLowerCase()}`,\n walletAddress: address,\n});\n\n/**\n * Adapt an EIP-1193 provider (announced via EIP-6963) into a butr\n * `WalletAdapter`. The returned adapter covers the full `Connector +\n * Wallet` surface; consumers can pass it through `createConnector` like\n * any hand-written adapter.\n *\n * **Caveats**\n *\n * - `disconnect` calls `wallet_revokePermissions`. Many wallets don't\n * implement that method and silently ignore the call. The reducer\n * will still mark the wallet as disconnected on butr's side; the\n * wallet's own auto-reconnect heuristic may or may not honour it.\n * - `switchAccount` is intentionally unimplemented because EIP-1193 has\n * no silent \"use address X\" RPC. The active account is whichever one\n * the wallet exposes first; the user changes it through the wallet's\n * own UI. butr's `subscribe` bridge auto-updates the pool entry when\n * `accountsChanged` fires. Call `requestAccounts` if you want the\n * wallet to expose more accounts.\n * - `getBalance()` returns the native ETH balance with symbol `\"ETH\"`\n * regardless of which EVM chain the wallet is currently on. Consumers\n * that target multiple EVM chains should overlay the symbol via\n * their own logic.\n * - `getBalance(tokenAddress)` reads the ERC20 balance for the active\n * account on the currently-connected chain. The token's own\n * `decimals()` and `symbol()` are queried for accurate formatting;\n * if `symbol()` is non-standard (bytes32) it's decoded as best-\n * effort. Routed through `eth_call` on the wallet's own provider;\n * the demo doesn't ship a separate RPC.\n * - `getSigner` returns the raw EIP-1193 provider. Wrap it in viem's\n * `createWalletClient` or ethers' `BrowserProvider` at the call site.\n */\nconst buildEvmAdapter = (info: Eip6963ProviderInfo, provider: Eip1193Provider): WalletAdapter => {\n return {\n capabilities: resolveEip6963Capabilities({ rdns: info.rdns }),\n chainPlatform: \"evm\",\n\n async connect(opts) {\n if (opts?.silent === true) {\n const accounts = await requestStringArray(provider, {\n method: \"eth_accounts\",\n });\n if (accounts.length === 0) {\n throw new Error(\"No authorized accounts for silent reconnect\");\n }\n return;\n }\n await provider.request({ method: \"eth_requestAccounts\" });\n },\n\n async disconnect() {\n try {\n await provider.request({\n method: \"wallet_revokePermissions\",\n params: [{ eth_accounts: {} }],\n });\n } catch {\n // Many wallets don't implement wallet_revokePermissions yet;\n // not a failure.\n }\n },\n\n async getAccount() {\n const accounts = await requestStringArray(provider, { method: \"eth_accounts\" });\n if (accounts.length === 0) {\n return null;\n }\n const chainIdHex = await requestString(provider, { method: \"eth_chainId\" });\n const chain = buildEvmChain(chainIdHex, info.name);\n const first = accounts[0];\n if (first === undefined) {\n return null;\n }\n return buildEvmAccount(first, chain);\n },\n\n async getAccounts() {\n const accounts = await requestStringArray(provider, { method: \"eth_accounts\" });\n if (accounts.length === 0) {\n return [];\n }\n const chainIdHex = await requestString(provider, { method: \"eth_chainId\" });\n const chain = buildEvmChain(chainIdHex, info.name);\n return accounts.map((addr) => buildEvmAccount(addr, chain));\n },\n\n async getBalance(mint) {\n const accounts = await requestStringArray(provider, { method: \"eth_accounts\" });\n const first = accounts[0];\n if (first === undefined) {\n throw new Error(\"No connected account\");\n }\n if (mint === undefined || mint === \"\") {\n const balanceHex = await requestString(provider, {\n method: \"eth_getBalance\",\n params: [first, \"latest\"],\n });\n const value = BigInt(balanceHex);\n return {\n decimals: Number(ETH_DECIMALS),\n formatted: formatEther(value),\n symbol: \"ETH\",\n value,\n };\n }\n const balanceCallData = `${ERC20_BALANCE_OF_SELECTOR}${padAddressForCall(first)}`;\n const [balanceHex, decimalsHex, symbolHex] = await Promise.all([\n requestString(provider, {\n method: \"eth_call\",\n params: [{ data: balanceCallData, to: mint }, \"latest\"],\n }),\n requestString(provider, {\n method: \"eth_call\",\n params: [{ data: ERC20_DECIMALS_SELECTOR, to: mint }, \"latest\"],\n }),\n requestString(provider, {\n method: \"eth_call\",\n params: [{ data: ERC20_SYMBOL_SELECTOR, to: mint }, \"latest\"],\n }),\n ]);\n const value = BigInt(balanceHex);\n const decimals = Number(BigInt(decimalsHex));\n const symbol = decodeAbiString(symbolHex);\n return {\n decimals,\n formatted: formatTokenUnits(value, decimals),\n symbol,\n value,\n };\n },\n\n getSigner: () => Promise.resolve(provider),\n\n async getTransactionReceipt(tx) {\n const receipt = await provider.request({\n method: \"eth_getTransactionReceipt\",\n params: [tx],\n });\n if (receipt === null || typeof receipt !== \"object\" || !(\"status\" in receipt)) {\n return { status: \"Pending\" };\n }\n return { status: receipt.status === \"0x1\" ? \"Success\" : \"Error\" };\n },\n\n icon: sanitizeIcon(info.icon),\n id: info.rdns,\n name: info.name,\n\n async requestAccounts() {\n // Preferred path: EIP-2255 `wallet_requestPermissions` reopens\n // the wallet's account-picker UI so the user can grant new\n // accounts. In practice, MetaMask is the only major EVM wallet\n // that actually surfaces a fresh picker; Rabby / OKX / Binance\n // / Backpack silently return existing accounts; Phantom EVM and\n // Coinbase Wallet reject with `method not supported`. The\n // `capabilities` layer (`EIP6963_RDNS_WITH_REQUEST_ACCOUNTS` in\n // `capabilities.ts`) gates the\n // demo button so users only see it where it'll do something\n // visible; this method stays callable for consumers with\n // wallet-specific flows.\n // Fallback: rejecting wallets get routed through\n // `eth_requestAccounts`, which at least doesn't throw. It won't\n // reopen the picker but surfaces newly-added accounts on the\n // next read.\n try {\n await provider.request({\n method: \"wallet_requestPermissions\",\n params: [{ eth_accounts: {} }],\n });\n } catch (error: unknown) {\n const outerCode =\n typeof error === \"object\" && error !== null && \"code\" in error ? error.code : undefined;\n const errData =\n typeof error === \"object\" && error !== null && \"data\" in error ? error.data : undefined;\n const originalError =\n typeof errData === \"object\" && errData !== null && \"originalError\" in errData\n ? errData.originalError\n : undefined;\n const innerCode =\n typeof originalError === \"object\" && originalError !== null && \"code\" in originalError\n ? originalError.code\n : undefined;\n const isMethodNotSupported =\n outerCode === 4200 || // EIP-1474 \"method not supported\"\n outerCode === -32_601 || // JSON-RPC \"method not found\"\n outerCode === -32_603 || // JSON-RPC \"internal error\" (Coinbase wraps -32604 here)\n innerCode === 4200 ||\n innerCode === -32_601 ||\n innerCode === -32_604; // Coinbase's custom \"method not supported\"\n if (isMethodNotSupported) {\n await provider.request({ method: \"eth_requestAccounts\" });\n return;\n }\n throw error;\n }\n },\n\n async sendTx(tx, account) {\n const txWithFrom = account ? withFrom(tx, account.walletAddress) : tx;\n const hash = await requestString(provider, {\n method: \"eth_sendTransaction\",\n params: [txWithFrom],\n });\n return hash;\n },\n\n async sendTxToChain(tx, targetChainIdDecimal, account, cb) {\n const current = await requestString(provider, { method: \"eth_chainId\" });\n const targetHex = chainIdDecimalToHex(targetChainIdDecimal);\n if (current.toLowerCase() !== targetHex.toLowerCase()) {\n await provider.request({\n method: \"wallet_switchEthereumChain\",\n params: [{ chainId: targetHex }],\n });\n cb?.();\n }\n const txWithFrom = account ? withFrom(tx, account.walletAddress) : tx;\n const hash = await requestString(provider, {\n method: \"eth_sendTransaction\",\n params: [txWithFrom],\n });\n return hash;\n },\n\n async signMessage(msg, account) {\n // Default to the first exposed account when the caller doesn't\n // specify one. MetaMask requires the address to be one currently\n // exposed via `eth_requestAccounts`; pass any address from\n // `ConnectedWallet.accounts` to sign with a non-active one.\n let signer = account?.walletAddress;\n if (signer === undefined || signer === \"\") {\n const accounts = await requestStringArray(provider, { method: \"eth_accounts\" });\n signer = accounts[0];\n }\n if (signer === undefined || signer === \"\") {\n throw new Error(\"No connected account\");\n }\n const signatureHex = await requestString(provider, {\n method: \"personal_sign\",\n params: [bytesToHex(msg), signer],\n });\n return { signature: hexToBytes(signatureHex), signedMessage: msg };\n },\n\n subscribe(listener) {\n const onAccountsChanged: Eip1193Listener = (...args) => {\n const accs = toStringArray(args[0]);\n if (accs.length === 0) {\n listener({ type: \"disconnected\" });\n return;\n }\n // Forward the FULL accounts array; `accountsChanged` reflects\n // the wallet's current exposure set, not an incremental add.\n // The runtime mirrors this list verbatim into the pool entry,\n // so single-account-exposure wallets (Phantom EVM) don't end\n // up with stale, non-signable addresses lingering in the array.\n void provider\n .request({ method: \"eth_chainId\" })\n // oxlint-disable-next-line promise/prefer-await-to-then -- callback context, not async\n .then((chainIdHex) => {\n const chain = buildEvmChain(\n typeof chainIdHex === \"string\" ? chainIdHex : \"\",\n info.name,\n );\n const accounts = accs.map((addr) => buildEvmAccount(addr, chain));\n const first = accounts[0];\n if (first === undefined) {\n return undefined;\n }\n listener({ account: first, accounts, type: \"accountChanged\" });\n return undefined;\n })\n // oxlint-disable-next-line promise/prefer-await-to-then -- callback context, not async\n .catch(() => {\n // Drop silently; next event will retry the read.\n });\n };\n\n const onChainChanged: Eip1193Listener = (...args) => {\n const chainIdHex = typeof args[0] === \"string\" ? args[0] : \"\";\n void provider\n .request({ method: \"eth_accounts\" })\n // oxlint-disable-next-line promise/prefer-await-to-then -- callback context, not async\n .then((accounts) => {\n const accs = toStringArray(accounts);\n if (accs.length === 0) {\n return undefined;\n }\n const chain = buildEvmChain(chainIdHex, info.name);\n const built = accs.map((addr) => buildEvmAccount(addr, chain));\n const first = built[0];\n if (first === undefined) {\n return undefined;\n }\n listener({ account: first, accounts: built, type: \"accountChanged\" });\n return undefined;\n })\n // oxlint-disable-next-line promise/prefer-await-to-then -- callback context, not async\n .catch(() => {\n // Drop silently.\n });\n };\n\n const onDisconnect: Eip1193Listener = () => {\n listener({ type: \"disconnected\" });\n };\n\n const onConnect: Eip1193Listener = () => {\n void Promise.all([\n provider.request({ method: \"eth_accounts\" }),\n provider.request({ method: \"eth_chainId\" }),\n ])\n // oxlint-disable-next-line promise/prefer-await-to-then -- callback context, not async\n .then(([accountsRaw, chainIdHex]) => {\n const accs = toStringArray(accountsRaw);\n if (accs.length === 0) {\n return undefined;\n }\n const chain = buildEvmChain(\n typeof chainIdHex === \"string\" ? chainIdHex : \"\",\n info.name,\n );\n const built = accs.map((addr) => buildEvmAccount(addr, chain));\n const first = built[0];\n if (first === undefined) {\n return undefined;\n }\n listener({ account: first, accounts: built, type: \"accountChanged\" });\n return undefined;\n })\n // oxlint-disable-next-line promise/prefer-await-to-then -- callback context, not async\n .catch(() => {\n // Drop silently; a later event will resync.\n });\n };\n\n provider.on(\"accountsChanged\", onAccountsChanged);\n provider.on(\"chainChanged\", onChainChanged);\n provider.on(\"connect\", onConnect);\n provider.on(\"disconnect\", onDisconnect);\n\n return () => {\n provider.removeListener(\"accountsChanged\", onAccountsChanged);\n provider.removeListener(\"chainChanged\", onChainChanged);\n provider.removeListener(\"connect\", onConnect);\n provider.removeListener(\"disconnect\", onDisconnect);\n };\n },\n\n async switchChain(chain) {\n await provider.request({\n method: \"wallet_switchEthereumChain\",\n params: [{ chainId: chainIdDecimalToHex(chain.reference) }],\n });\n },\n };\n};\n\nexport { bytesToHexPrefixed as bytesToHex, hexToBytes } from \"@usebutr/core\";\nexport { buildEvmAdapter, chainIdDecimalToHex, chainIdHexToDecimal, formatEther };\n","import type { WalletAdapter } from \"@usebutr/core\";\n\nimport type { Eip6963AnnounceEvent, Eip6963ProviderInfo } from \"./eip1193\";\nimport { buildEvmAdapter } from \"./eip6963-adapter\";\n\nconst ANNOUNCE_EVENT = \"eip6963:announceProvider\";\nconst REQUEST_EVENT = \"eip6963:requestProvider\";\n\ntype AdapterCallback = (adapter: WalletAdapter, info: Eip6963ProviderInfo) => void;\n\ntype DiscoverOptions = {\n /** Event target to listen on. Defaults to `window` in browser/RN/\n * SSR'd environments; tests can pass a fresh `new EventTarget()` to\n * exercise the discovery loop without a DOM. */\n target?: EventTarget;\n};\n\nconst isEip6963AnnounceEvent = (event: Event): event is Eip6963AnnounceEvent => {\n if (!(\"detail\" in event)) {\n return false;\n }\n const detail: unknown = event.detail;\n if (typeof detail !== \"object\" || detail === null) {\n return false;\n }\n if (!(\"info\" in detail) || !(\"provider\" in detail)) {\n return false;\n }\n const { info, provider } = detail;\n return (\n typeof info === \"object\" &&\n info !== null &&\n \"rdns\" in info &&\n typeof info.rdns === \"string\" &&\n typeof provider === \"object\" &&\n provider !== null\n );\n};\n\nconst isEventTarget = (value: unknown): value is EventTarget =>\n typeof value === \"object\" &&\n value !== null &&\n \"addEventListener\" in value &&\n typeof value.addEventListener === \"function\";\n\nconst resolveTarget = (target?: EventTarget): EventTarget | null => {\n if (target !== undefined) {\n return target;\n }\n const candidate: unknown = (globalThis as { window?: unknown }).window;\n return isEventTarget(candidate) ? candidate : null;\n};\n\n/**\n * Subscribe to EIP-6963 wallet announcements. Spec:\n * https://eips.ethereum.org/EIPS/eip-6963\n *\n * Flow:\n * 1. Page registers a listener for `eip6963:announceProvider`.\n * 2. Page dispatches `eip6963:requestProvider` to kick wallets that\n * have already loaded.\n * 3. Wallets that load later still dispatch their own announcement\n * when they boot, so the listener stays attached for the lifetime\n * of the discovery session.\n * 4. Dedup by `info.rdns`; wallets occasionally re-announce, and\n * `info.uuid` is regenerated per page load so it's not stable.\n *\n * Returns an unsubscribe function that removes the listener.\n * In non-browser environments (SSR, tests with no window) returns\n * a noop unsubscribe and never fires the callback.\n */\nconst discoverEvmAdapters = (\n onAdapter: AdapterCallback,\n options: DiscoverOptions = {},\n): (() => void) => {\n const target = resolveTarget(options.target);\n if (!target) {\n return () => {};\n }\n\n const seen = new Set<string>();\n const handler = (event: Event) => {\n if (!isEip6963AnnounceEvent(event)) {\n return;\n }\n const { info, provider } = event.detail;\n if (info.rdns.length === 0) {\n return;\n }\n if (seen.has(info.rdns)) {\n return;\n }\n seen.add(info.rdns);\n onAdapter(buildEvmAdapter(info, provider), info);\n };\n\n target.addEventListener(ANNOUNCE_EVENT, handler);\n target.dispatchEvent(new Event(REQUEST_EVENT));\n\n return () => {\n target.removeEventListener(ANNOUNCE_EVENT, handler);\n };\n};\n\nexport { ANNOUNCE_EVENT, REQUEST_EVENT, discoverEvmAdapters };\n","import type { WalletAdapter } from \"@usebutr/core\";\n\nimport type { Eip1193Provider, Eip6963ProviderInfo } from \"./eip1193\";\nimport { buildEvmAdapter } from \"./eip6963-adapter\";\n\n/**\n * Generic wallet icon (a stylised purse SVG, ~600 bytes). Used as\n * the fallback icon when an injected provider has no EIP-6963 info.\n * Inline rather than a network fetch so the picker doesn't flicker.\n */\nconst GENERIC_INJECTED_ICON =\n \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNMjEgMTJWN0g1YTIgMiAwIDAgMSAwLTQgaDE0djQiLz48cGF0aCBkPSJNMyA1djE0YTIgMiAwIDAgMCAyIDJoMTZ2LTVNMjEgMTIgaC0zYTIgMiAwIDAgMC0yIDJ2MGEyIDIgMCAwIDAgMiAyaDN6Ii8+PC9zdmc+\";\n\nconst DEFAULT_SETTLE_MS = 150;\n\ntype InjectedDiscoveryOptions = {\n /**\n * Predicate the fallback consults before emitting; if any EIP-6963\n * adapter has been registered by the time the settle timer fires,\n * the injected fallback is skipped (the wallet is already covered\n * via the standards path; we don't want a duplicate).\n *\n * `discoverWalletAdapters` wires this to the shared `seen` set so\n * the dedupe is automatic. Callers using `discoverInjectedAdapter`\n * directly should pass their own check or omit it (always emit).\n */\n hasAnyEip6963Adapter?: () => boolean;\n /**\n * How long to wait for EIP-6963 announcements before falling back\n * to `window.ethereum`. Most wallets announce within the first\n * frame; 150ms is a conservative settle window that keeps the\n * picker responsive while giving slow announcements time to land.\n */\n settleMs?: number;\n /** Override the global `window` reference (tests, iframes). */\n target?: { ethereum?: unknown } | null;\n};\n\nconst isEip1193Provider = (value: unknown): value is Eip1193Provider =>\n typeof value === \"object\" &&\n value !== null &&\n \"request\" in value &&\n typeof value.request === \"function\";\n\nconst readEthereum = (target: InjectedDiscoveryOptions[\"target\"]): Eip1193Provider | null => {\n // Resolve the host object: use the caller's override if given, otherwise\n // fall back to the global window (or null in SSR environments).\n const globalWindow: unknown = typeof window === \"undefined\" ? null : window;\n const host: unknown = target === undefined ? globalWindow : target;\n if (typeof host !== \"object\" || host === null || !(\"ethereum\" in host)) {\n return null;\n }\n const eth: unknown = host.ethereum;\n return isEip1193Provider(eth) ? eth : null;\n};\n\n/**\n * Last-resort discovery path for EVM wallets that DON'T announce via\n * EIP-6963. Most modern wallets (MetaMask, Rabby, Coinbase, Phantom,\n * Brave, etc.) do announce, so the standards-based path\n * (`discoverEvmAdapters`) covers them. This module catches the long\n * tail: regional wallets (KuCoin, Bitget, BitKeep, Okto, Frontier)\n * and older builds that only expose `window.ethereum`.\n *\n * **Coordination**: the fallback waits `settleMs` (default 150ms)\n * after subscription, then consults `hasAnyEip6963Adapter` if\n * provided. If any EIP-6963 adapter has already been registered, we\n * skip emission; assume the same wallet is also speaking standards\n * and we'd just be duplicating. If no EIP-6963 adapter showed up but\n * `window.ethereum` does, we emit a generic adapter.\n *\n * The emitted adapter has `rdns: \"injected:legacy\"` so consumers can\n * distinguish it from EIP-6963 adapters in the picker UI.\n */\nconst discoverInjectedAdapter = (\n onAdapter: (adapter: WalletAdapter) => void,\n options: InjectedDiscoveryOptions = {},\n): (() => void) => {\n const settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;\n let cancelled = false;\n const timer = setTimeout(() => {\n if (cancelled) {\n return;\n }\n if (options.hasAnyEip6963Adapter?.() === true) {\n return;\n }\n const provider = readEthereum(options.target);\n if (provider === null) {\n return;\n }\n const info: Eip6963ProviderInfo = {\n icon: GENERIC_INJECTED_ICON,\n name: \"Browser wallet\",\n rdns: \"injected:legacy\",\n uuid: \"injected:legacy\",\n };\n onAdapter(buildEvmAdapter(info, provider));\n }, settleMs);\n\n return () => {\n cancelled = true;\n clearTimeout(timer);\n };\n};\n\nexport type { InjectedDiscoveryOptions };\nexport { GENERIC_INJECTED_ICON, discoverInjectedAdapter };\n","import type { ChainBase } from \"@usebutr/core\";\n\n/**\n * Common EVM chain registry keyed by CAIP-2 id. Useful for chain-\n * switcher UIs. butr itself doesn't read this: `Connector.switchChain`\n * accepts any `ChainBase`.\n */\nconst EVM_CHAINS = {\n arbitrum: {\n id: \"eip155:42161\",\n name: \"Arbitrum One\",\n namespace: \"eip155\",\n reference: \"42161\",\n },\n base: { id: \"eip155:8453\", name: \"Base\", namespace: \"eip155\", reference: \"8453\" },\n bsc: { id: \"eip155:56\", name: \"BNB Smart Chain\", namespace: \"eip155\", reference: \"56\" },\n ethereum: { id: \"eip155:1\", name: \"Ethereum\", namespace: \"eip155\", reference: \"1\" },\n optimism: { id: \"eip155:10\", name: \"Optimism\", namespace: \"eip155\", reference: \"10\" },\n polygon: { id: \"eip155:137\", name: \"Polygon\", namespace: \"eip155\", reference: \"137\" },\n sepolia: { id: \"eip155:11155111\", name: \"Sepolia\", namespace: \"eip155\", reference: \"11155111\" },\n} as const satisfies Record<string, ChainBase>;\n\nconst EVM_CHAINS_LIST: ReadonlyArray<ChainBase> = Object.values(EVM_CHAINS);\n\nexport { EVM_CHAINS, EVM_CHAINS_LIST };\n","import type { PlatformDiscoverer } from \"@usebutr/core\";\n\nimport { discoverEvmAdapters } from \"./eip6963\";\nimport { discoverInjectedAdapter } from \"./injected\";\n\n/**\n * EVM's `PlatformDiscoverer`: EIP-6963 announcements primary, with the\n * legacy `window.ethereum` injected fallback wired to defer until\n * EIP-6963 has had a chance to fire.\n *\n * Consumers using `@usebutr/wallets`'s `autoDiscovery()` get this\n * wired in automatically. Direct consumers who don't want the\n * injected fallback build their own discovery source from\n * `discoverEvmAdapters` alone.\n */\nconst evmDiscoverer: PlatformDiscoverer = {\n fallback: {\n subscribe: (onAdapter, opts) =>\n discoverInjectedAdapter(onAdapter, {\n hasAnyEip6963Adapter: opts.hasAnyPrimaryAdapter,\n }),\n },\n subscribe: discoverEvmAdapters,\n};\n\nexport { evmDiscoverer };\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["HEX_PREFIX","hexToBytes","bytesToHex","hexToBytes"],"sources":["../src/capabilities.ts","../src/eip1193.ts","../src/evm-balance.ts","../src/eip6963-adapter.ts","../src/eip6963.ts","../src/injected.ts","../src/chains.ts","../src/discoverer.ts"],"sourcesContent":["import type { WalletCapabilities } from \"@usebutr/core\";\n\nconst EIP6963_RDNS_WITH_REQUEST_ACCOUNTS = new Set<string>([\n \"io.metamask\", // MetaMask: verified May 2026\n]);\n\ntype Eip6963CapabilityInput = { rdns: string };\n\nconst resolveEip6963Capabilities = (input: Eip6963CapabilityInput): WalletCapabilities => ({\n getBalance: true,\n getTransactionReceipt: true,\n requestAccounts: EIP6963_RDNS_WITH_REQUEST_ACCOUNTS.has(input.rdns),\n sendTransaction: true,\n signIn: false,\n signMessage: true,\n signTransaction: false,\n subscribe: true,\n switchAccount: false,\n switchChain: true,\n});\n\nexport type { Eip6963CapabilityInput };\nexport { EIP6963_RDNS_WITH_REQUEST_ACCOUNTS, resolveEip6963Capabilities };\n","/**\n * Declared inline rather than taken from `viem`, which would expand butr's\n * footprint. Loose on purpose: `request(...)` returns `unknown` and the\n * `request*` helpers narrow it at the boundary.\n */\n\ntype Eip1193Object = { [key: string]: Eip1193Value | undefined };\ntype Eip1193Value =\n | bigint\n | boolean\n | number\n | string\n | null\n | Uint8Array\n | ReadonlyArray<Eip1193Value>\n | Eip1193Object;\n\ntype Eip1193RequestArgs = {\n method: string;\n params?: ReadonlyArray<Eip1193Value> | Eip1193Object;\n};\n\ntype Eip1193Listener = (...args: ReadonlyArray<Eip1193Value | undefined>) => void;\n\ntype Eip1193Provider = {\n on: (event: string, listener: Eip1193Listener) => void;\n removeListener: (event: string, listener: Eip1193Listener) => void;\n request: (args: Eip1193RequestArgs) => Promise<Eip1193Value | undefined>;\n};\n\ntype Eip6963ProviderInfo = {\n /** Data URI or absolute URL pointing at the wallet's icon. */\n icon: string;\n /** Human-readable wallet name shown in pickers. */\n name: string;\n /** Reverse-DNS identifier: `io.metamask`, `io.rabby`, `app.phantom`.\n * Stable across versions, unique per wallet, used as the pool key. */\n rdns: string;\n /** Per-page UUID generated by the wallet. Not stable across reloads. */\n uuid: string;\n};\n\ntype Eip6963ProviderDetail = {\n info: Eip6963ProviderInfo;\n provider: Eip1193Provider;\n};\n\ntype Eip6963AnnounceEvent = CustomEvent<Eip6963ProviderDetail>;\n\n/** `provider.request` is typed `unknown` by design; these helpers narrow\n * the well-known EVM RPC shapes at the boundary with runtime guards so\n * call sites stay assertion-free. A malformed response is reported as\n * `null` / `[]` rather than a blank value, so call sites that cannot\n * proceed without a real one fail instead of inventing a default. */\nconst requestString = async (\n provider: Eip1193Provider,\n args: Eip1193RequestArgs,\n): Promise<string | null> => {\n const result = await provider.request(args);\n return typeof result === \"string\" ? result : null;\n};\n\nconst requestStringArray = async (\n provider: Eip1193Provider,\n args: Eip1193RequestArgs,\n): Promise<Array<string>> => {\n const result = await provider.request(args);\n return Array.isArray(result)\n ? result.filter((item): item is string => typeof item === \"string\")\n : [];\n};\n\nexport { requestString, requestStringArray };\n\nexport type {\n Eip1193Listener,\n Eip1193Object,\n Eip1193Provider,\n Eip1193RequestArgs,\n Eip1193Value,\n Eip6963AnnounceEvent,\n Eip6963ProviderDetail,\n Eip6963ProviderInfo,\n};\n","import { hexToBytes } from \"@usebutr/core\";\n\nimport type { Eip1193Provider } from \"./eip1193\";\nimport { requestString } from \"./eip1193\";\n\nconst HEX_PREFIX = \"0x\";\nconst ETH_DECIMALS = 18n;\nconst ERC20_BALANCE_OF_SELECTOR = \"0x70a08231\";\nconst ERC20_DECIMALS_SELECTOR = \"0x313ce567\";\nconst ERC20_SYMBOL_SELECTOR = \"0x95d89b41\";\n\n/** A failed read must not render as a confident zero balance. */\nconst requireHex = (hex: string | null, label: string): string => {\n if (hex === null) {\n throw new Error(`Wallet returned a malformed ${label} response`);\n }\n return hex;\n};\n\nconst formatUnits = (raw: bigint, decimals: number): string => {\n if (decimals === 0) {\n return raw.toString();\n }\n const unit = 10n ** BigInt(decimals);\n const integer = raw / unit;\n const remainder = raw % unit;\n if (remainder === 0n) {\n return integer.toString();\n }\n const fraction = remainder.toString().padStart(decimals, \"0\").replace(/0+$/v, \"\");\n return `${integer}.${fraction}`;\n};\n\nconst formatEther = (wei: bigint): string => formatUnits(wei, Number(ETH_DECIMALS));\n\nconst padAddressForCall = (address: string): string =>\n address.replace(/^0x/v, \"\").toLowerCase().padStart(64, \"0\");\n\nconst decodeAbiString = (hex: string): string => {\n const clean = hex.startsWith(HEX_PREFIX) ? hex.slice(2) : hex;\n if (clean.length === 0) {\n return \"\";\n }\n if (clean.length >= 128) {\n const lengthHex = clean.slice(64, 128);\n const length = Number(BigInt(`0x${lengthHex}`));\n if (length > 0 && clean.length >= 128 + length * 2) {\n return new TextDecoder().decode(hexToBytes(clean.slice(128, 128 + length * 2)));\n }\n }\n const bytes = hexToBytes(clean.slice(0, 64));\n let end = bytes.length;\n while (end > 0 && bytes[end - 1] === 0) {\n end -= 1;\n }\n return new TextDecoder().decode(bytes.subarray(0, end));\n};\n\nconst readEvmBalance = async (\n provider: Eip1193Provider,\n address: string,\n tokenAddress?: string,\n) => {\n if (tokenAddress === undefined || tokenAddress === \"\") {\n const balanceHex = await requestString(provider, {\n method: \"eth_getBalance\",\n params: [address, \"latest\"],\n });\n const value = BigInt(requireHex(balanceHex, \"eth_getBalance\"));\n return {\n decimals: Number(ETH_DECIMALS),\n formatted: formatEther(value),\n symbol: \"ETH\",\n value,\n };\n }\n\n const [balanceHex, decimalsHex, symbolHex] = await Promise.all([\n requestString(provider, {\n method: \"eth_call\",\n params: [\n { data: `${ERC20_BALANCE_OF_SELECTOR}${padAddressForCall(address)}`, to: tokenAddress },\n \"latest\",\n ],\n }),\n requestString(provider, {\n method: \"eth_call\",\n params: [{ data: ERC20_DECIMALS_SELECTOR, to: tokenAddress }, \"latest\"],\n }),\n requestString(provider, {\n method: \"eth_call\",\n params: [{ data: ERC20_SYMBOL_SELECTOR, to: tokenAddress }, \"latest\"],\n }),\n ]);\n const value = BigInt(requireHex(balanceHex, \"balanceOf()\"));\n const decimals = Number(BigInt(requireHex(decimalsHex, \"decimals()\")));\n return {\n decimals,\n formatted: formatUnits(value, decimals),\n symbol: decodeAbiString(symbolHex ?? \"\"),\n value,\n };\n};\n\nexport { formatEther, readEvmBalance };\n","import type {\n Account,\n ChainBase,\n TransactionInput,\n TransactionValue,\n WalletAdapter,\n} from \"@usebutr/core\";\nimport { bytesToHexPrefixed as bytesToHex, hexToBytes, sanitizeIcon } from \"@usebutr/core\";\n\nimport { resolveEip6963Capabilities } from \"./capabilities\";\nimport type {\n Eip1193Listener,\n Eip1193Object,\n Eip1193Provider,\n Eip1193Value,\n Eip6963ProviderInfo,\n} from \"./eip1193\";\nimport { requestString, requestStringArray } from \"./eip1193\";\nimport { readEvmBalance } from \"./evm-balance\";\n\nconst HEX_PREFIX = \"0x\";\n\nconst toStringArray = (value: Eip1193Value | undefined): Array<string> =>\n Array.isArray(value) ? value.filter((item): item is string => typeof item === \"string\") : [];\n\nconst toEip1193Value = (value: TransactionValue): Eip1193Value => {\n if (typeof value === \"function\") {\n throw new TypeError(\"EVM transactions cannot contain functions\");\n }\n if (Array.isArray(value)) {\n return value.map(toEip1193Value);\n }\n if (value instanceof Uint8Array) {\n return value;\n }\n if (typeof value === \"object\" && value !== null) {\n const result: Eip1193Object = {};\n for (const [key, nested] of Object.entries(value)) {\n result[key] = nested === undefined ? undefined : toEip1193Value(nested);\n }\n return result;\n }\n return value;\n};\n\nconst withFrom = (tx: TransactionInput, from?: string): Eip1193Value => {\n if (typeof tx === \"string\" || tx instanceof Uint8Array) {\n return tx;\n }\n const result: Eip1193Object = {};\n for (const [key, value] of Object.entries(tx)) {\n result[key] = value === undefined ? undefined : toEip1193Value(value);\n }\n if (from !== undefined) {\n result.from = from;\n }\n return result;\n};\n\nconst chainIdHexToDecimal = (hex: string): string => BigInt(hex).toString(10);\nconst chainIdDecimalToHex = (dec: string): string => `${HEX_PREFIX}${BigInt(dec).toString(16)}`;\n\nconst buildEvmChain = (chainIdHex: string, walletName: string): ChainBase => {\n const reference = chainIdHexToDecimal(chainIdHex);\n return {\n id: `eip155:${reference}`,\n name: walletName,\n namespace: \"eip155\",\n reference,\n };\n};\n\nconst buildEvmAccount = (address: string, chain: ChainBase): Account => ({\n chain,\n id: `${chain.id}:${address.toLowerCase()}`,\n walletAddress: address,\n});\n\ntype AccountStateOptions = {\n accounts?: Array<string>;\n chainIdHex?: string;\n};\n\n/** Rejections propagate: `getAccount`/`getAccounts` surface the wallet's\n * own provider error, while the `subscribe` bridge swallows it because a\n * later event retries synchronization. */\nconst readAccountState = async (\n provider: Eip1193Provider,\n walletName: string,\n options: AccountStateOptions = {},\n) => {\n const accounts =\n options.accounts ?? (await requestStringArray(provider, { method: \"eth_accounts\" }));\n if (accounts.length === 0) {\n return null;\n }\n const chainIdHex =\n options.chainIdHex ?? (await requestString(provider, { method: \"eth_chainId\" }));\n if (chainIdHex === null) {\n throw new Error(\"Wallet returned a malformed eth_chainId response\");\n }\n const chain = buildEvmChain(chainIdHex, walletName);\n const builtAccounts = accounts.map((address) => buildEvmAccount(address, chain));\n const account = builtAccounts[0];\n return account === undefined ? null : { account, accounts: builtAccounts };\n};\n\n/**\n * `disconnect` calls `wallet_revokePermissions`, which many wallets don't\n * implement and silently ignore, so their own auto-reconnect may outlive it.\n * `getBalance()` labels the native balance `\"ETH\"` on every EVM chain.\n */\nconst buildEvmAdapter = (info: Eip6963ProviderInfo, provider: Eip1193Provider): WalletAdapter => {\n return {\n capabilities: resolveEip6963Capabilities({ rdns: info.rdns }),\n chainPlatform: \"evm\",\n\n async connect(opts) {\n if (opts?.silent === true) {\n const accounts = await requestStringArray(provider, {\n method: \"eth_accounts\",\n });\n if (accounts.length === 0) {\n throw new Error(\"No authorized accounts for silent reconnect\");\n }\n return;\n }\n await provider.request({ method: \"eth_requestAccounts\" });\n },\n\n async disconnect() {\n try {\n await provider.request({\n method: \"wallet_revokePermissions\",\n params: [{ eth_accounts: {} }],\n });\n } catch {\n // Many wallets don't implement wallet_revokePermissions yet;\n // not a failure.\n }\n },\n\n async getAccount() {\n const state = await readAccountState(provider, info.name);\n return state?.account ?? null;\n },\n\n async getAccounts() {\n const state = await readAccountState(provider, info.name);\n return state?.accounts ?? [];\n },\n\n async getBalance(mint) {\n const accounts = await requestStringArray(provider, { method: \"eth_accounts\" });\n const first = accounts[0];\n if (first === undefined) {\n throw new Error(\"No connected account\");\n }\n return readEvmBalance(provider, first, mint);\n },\n\n getSigner: () => Promise.resolve(provider),\n\n async getTransactionReceipt(tx) {\n const receipt = await provider.request({\n method: \"eth_getTransactionReceipt\",\n params: [tx],\n });\n if (receipt === null || typeof receipt !== \"object\" || !(\"status\" in receipt)) {\n return { status: \"Pending\" };\n }\n return { status: receipt.status === \"0x1\" ? \"Success\" : \"Error\" };\n },\n\n icon: sanitizeIcon(info.icon),\n id: info.rdns,\n name: info.name,\n\n async requestAccounts() {\n try {\n await provider.request({\n method: \"wallet_requestPermissions\",\n params: [{ eth_accounts: {} }],\n });\n } catch (error) {\n const outerCode =\n typeof error === \"object\" && error !== null && \"code\" in error ? error.code : undefined;\n const errData =\n typeof error === \"object\" && error !== null && \"data\" in error ? error.data : undefined;\n const originalError =\n typeof errData === \"object\" && errData !== null && \"originalError\" in errData\n ? errData.originalError\n : undefined;\n const innerCode =\n typeof originalError === \"object\" && originalError !== null && \"code\" in originalError\n ? originalError.code\n : undefined;\n const isMethodNotSupported =\n outerCode === 4200 || // EIP-1474 \"method not supported\"\n outerCode === -32_601 || // JSON-RPC \"method not found\"\n outerCode === -32_603 || // JSON-RPC \"internal error\" (Coinbase wraps -32604 here)\n innerCode === 4200 ||\n innerCode === -32_601 ||\n innerCode === -32_604; // Coinbase's custom \"method not supported\"\n if (isMethodNotSupported) {\n await provider.request({ method: \"eth_requestAccounts\" });\n return;\n }\n throw error;\n }\n },\n\n async sendTx(tx, account) {\n const txWithFrom = withFrom(tx, account?.walletAddress);\n const hash = await requestString(provider, {\n method: \"eth_sendTransaction\",\n params: [txWithFrom],\n });\n if (hash === null) {\n throw new Error(\"Wallet returned no transaction hash\");\n }\n return hash;\n },\n\n async sendTxToChain(tx, targetChainIdDecimal, account, cb) {\n const current = await requestString(provider, { method: \"eth_chainId\" });\n const targetHex = chainIdDecimalToHex(targetChainIdDecimal);\n if (current?.toLowerCase() !== targetHex.toLowerCase()) {\n await provider.request({\n method: \"wallet_switchEthereumChain\",\n params: [{ chainId: targetHex }],\n });\n cb?.();\n }\n const txWithFrom = withFrom(tx, account?.walletAddress);\n const hash = await requestString(provider, {\n method: \"eth_sendTransaction\",\n params: [txWithFrom],\n });\n if (hash === null) {\n throw new Error(\"Wallet returned no transaction hash\");\n }\n return hash;\n },\n\n async signMessage(msg, account) {\n let signer = account?.walletAddress;\n if (signer === undefined || signer === \"\") {\n const accounts = await requestStringArray(provider, { method: \"eth_accounts\" });\n signer = accounts[0];\n }\n if (signer === undefined || signer === \"\") {\n throw new Error(\"No connected account\");\n }\n const signatureHex = await requestString(provider, {\n method: \"personal_sign\",\n params: [bytesToHex(msg), signer],\n });\n if (signatureHex === null) {\n throw new Error(\"Wallet returned a malformed personal_sign response\");\n }\n return { signature: hexToBytes(signatureHex), signedMessage: msg };\n },\n\n subscribe(listener) {\n const synchronizeAccount = async (options?: AccountStateOptions) => {\n try {\n const state = await readAccountState(provider, info.name, options);\n if (state !== null) {\n listener({ ...state, type: \"accountChanged\" });\n }\n } catch {\n // EIP-1193 event reads are best-effort; a later event retries synchronization.\n }\n };\n\n const onAccountsChanged: Eip1193Listener = (...args) => {\n const accs = toStringArray(args[0]);\n if (accs.length === 0) {\n listener({ type: \"disconnected\" });\n return;\n }\n void synchronizeAccount({ accounts: accs });\n };\n\n const onChainChanged: Eip1193Listener = (...args) => {\n const [chainId] = args;\n // Some wallets emit a decimal number here instead of the EIP-1193\n // hex string; leaving it absent re-engages the eth_chainId read.\n const chainIdHex = typeof chainId === \"string\" && chainId.length > 0 ? chainId : undefined;\n void synchronizeAccount({ chainIdHex });\n };\n\n const onDisconnect: Eip1193Listener = () => {\n listener({ type: \"disconnected\" });\n };\n\n const onConnect: Eip1193Listener = () => {\n void synchronizeAccount();\n };\n\n provider.on(\"accountsChanged\", onAccountsChanged);\n provider.on(\"chainChanged\", onChainChanged);\n provider.on(\"connect\", onConnect);\n provider.on(\"disconnect\", onDisconnect);\n\n return () => {\n provider.removeListener(\"accountsChanged\", onAccountsChanged);\n provider.removeListener(\"chainChanged\", onChainChanged);\n provider.removeListener(\"connect\", onConnect);\n provider.removeListener(\"disconnect\", onDisconnect);\n };\n },\n\n async switchChain(chain) {\n const targetHex = chainIdDecimalToHex(chain.reference);\n const current = await requestString(provider, { method: \"eth_chainId\" });\n if (current?.toLowerCase() === targetHex.toLowerCase()) {\n return;\n }\n await provider.request({\n method: \"wallet_switchEthereumChain\",\n params: [{ chainId: targetHex }],\n });\n },\n };\n};\n\nexport { bytesToHexPrefixed as bytesToHex, hexToBytes } from \"@usebutr/core\";\nexport { formatEther } from \"./evm-balance\";\nexport { buildEvmAdapter, chainIdDecimalToHex, chainIdHexToDecimal };\n","import type { WalletAdapter } from \"@usebutr/core\";\n\nimport type { Eip6963AnnounceEvent, Eip6963ProviderInfo } from \"./eip1193\";\nimport { buildEvmAdapter } from \"./eip6963-adapter\";\n\nconst ANNOUNCE_EVENT = \"eip6963:announceProvider\";\nconst REQUEST_EVENT = \"eip6963:requestProvider\";\n\ntype AdapterCallback = (adapter: WalletAdapter, info: Eip6963ProviderInfo) => void;\n\ntype DiscoverOptions = {\n /** Event target to listen on. Defaults to `window` in browser/RN/\n * SSR'd environments; tests can pass a fresh `new EventTarget()` to\n * exercise the discovery loop without a DOM. */\n target?: EventTarget;\n};\n\nconst isEip6963AnnounceEvent = (event: Event): event is Eip6963AnnounceEvent => {\n if (!(\"detail\" in event)) {\n return false;\n }\n const detail: unknown = event.detail;\n if (typeof detail !== \"object\" || detail === null) {\n return false;\n }\n if (!(\"info\" in detail) || !(\"provider\" in detail)) {\n return false;\n }\n const { info, provider } = detail;\n return (\n typeof info === \"object\" &&\n info !== null &&\n \"icon\" in info &&\n typeof info.icon === \"string\" &&\n \"name\" in info &&\n typeof info.name === \"string\" &&\n \"rdns\" in info &&\n typeof info.rdns === \"string\" &&\n \"uuid\" in info &&\n typeof info.uuid === \"string\" &&\n typeof provider === \"object\" &&\n provider !== null &&\n \"on\" in provider &&\n typeof provider.on === \"function\" &&\n \"removeListener\" in provider &&\n typeof provider.removeListener === \"function\" &&\n \"request\" in provider &&\n typeof provider.request === \"function\"\n );\n};\n\nconst resolveTarget = (target?: EventTarget): EventTarget | null => {\n if (target !== undefined) {\n return target;\n }\n return globalThis.window ?? null;\n};\n\n/**\n * Spec: https://eips.ethereum.org/EIPS/eip-6963. Dedupe is by `info.rdns`\n * because `info.uuid` is regenerated per page load. The listener stays\n * attached for the session so wallets that boot late still register.\n */\nconst discoverEvmAdapters = (\n onAdapter: AdapterCallback,\n options: DiscoverOptions = {},\n): (() => void) => {\n const target = resolveTarget(options.target);\n if (!target) {\n return () => {};\n }\n\n const seen = new Set<string>();\n const handler = (event: Event) => {\n if (!isEip6963AnnounceEvent(event)) {\n return;\n }\n const { info, provider } = event.detail;\n if (info.rdns.length === 0) {\n return;\n }\n if (seen.has(info.rdns)) {\n return;\n }\n seen.add(info.rdns);\n onAdapter(buildEvmAdapter(info, provider), info);\n };\n\n target.addEventListener(ANNOUNCE_EVENT, handler);\n target.dispatchEvent(new Event(REQUEST_EVENT));\n\n return () => {\n target.removeEventListener(ANNOUNCE_EVENT, handler);\n };\n};\n\nexport { ANNOUNCE_EVENT, REQUEST_EVENT, discoverEvmAdapters };\n","import type { WalletAdapter, WalletSigner } from \"@usebutr/core\";\n\nimport type { Eip1193Provider, Eip6963ProviderInfo } from \"./eip1193\";\nimport { buildEvmAdapter } from \"./eip6963-adapter\";\n\n/**\n * Generic wallet icon (a stylised purse SVG, ~600 bytes). Used as\n * the fallback icon when an injected provider has no EIP-6963 info.\n * Inline rather than a network fetch so the picker doesn't flicker.\n */\nconst GENERIC_INJECTED_ICON =\n \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNMjEgMTJWN0g1YTIgMiAwIDAgMSAwLTQgaDE0djQiLz48cGF0aCBkPSJNMyA1djE0YTIgMiAwIDAgMCAyIDJoMTZ2LTVNMjEgMTIgaC0zYTIgMiAwIDAgMC0yIDJ2MGEyIDIgMCAwIDAgMiAyaDN6Ii8+PC9zdmc+\";\n\nconst DEFAULT_SETTLE_MS = 150;\n\ntype InjectedDiscoveryOptions = {\n /**\n * Consulted before emitting: an already-registered EIP-6963 adapter means\n * the wallet is covered by the standards path and emitting would duplicate\n * it. Omit to always emit.\n */\n hasAnyEip6963Adapter?: () => boolean;\n /**\n * Most wallets announce within the first frame; 150ms leaves room for slow\n * announcements without stalling the picker.\n */\n settleMs?: number;\n /** Override the global `window` reference (tests, iframes). */\n target?: { ethereum?: Partial<Eip1193Provider> } | null;\n};\n\ndeclare global {\n // oxlint-disable-next-line typescript/consistent-type-definitions -- DOM globals require interface merging.\n interface Window {\n ethereum?: Partial<Eip1193Provider>;\n }\n}\n\nconst isEip1193Provider = (value: WalletSigner | undefined): value is Eip1193Provider =>\n value !== undefined &&\n \"on\" in value &&\n typeof value.on === \"function\" &&\n \"removeListener\" in value &&\n typeof value.removeListener === \"function\" &&\n \"request\" in value &&\n typeof value.request === \"function\";\n\nconst readEthereum = (target: InjectedDiscoveryOptions[\"target\"]): Eip1193Provider | null => {\n const host = target === undefined ? globalThis.window : target;\n return isEip1193Provider(host?.ethereum) ? host.ethereum : null;\n};\n\n/**\n * Last-resort path for the long tail of EVM wallets that expose only\n * `window.ethereum` and never announce via EIP-6963. The emitted adapter\n * carries `rdns: \"injected:legacy\"` so consumers can tell it apart.\n */\nconst discoverInjectedAdapter = (\n onAdapter: (adapter: WalletAdapter) => void,\n options: InjectedDiscoveryOptions = {},\n): (() => void) => {\n const settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;\n let cancelled = false;\n const timer = setTimeout(() => {\n if (cancelled) {\n return;\n }\n if (options.hasAnyEip6963Adapter?.() === true) {\n return;\n }\n const provider = readEthereum(options.target);\n if (provider === null) {\n return;\n }\n const info: Eip6963ProviderInfo = {\n icon: GENERIC_INJECTED_ICON,\n name: \"Browser wallet\",\n rdns: \"injected:legacy\",\n uuid: \"injected:legacy\",\n };\n onAdapter(buildEvmAdapter(info, provider));\n }, settleMs);\n\n return () => {\n cancelled = true;\n clearTimeout(timer);\n };\n};\n\nexport type { InjectedDiscoveryOptions };\nexport { GENERIC_INJECTED_ICON, discoverInjectedAdapter, isEip1193Provider };\n","import type { ChainBase } from \"@usebutr/core\";\n\n/**\n * Common EVM chain registry keyed by CAIP-2 id. Useful for chain-\n * switcher UIs. butr itself doesn't read this: `Connector.switchChain`\n * accepts any `ChainBase`.\n */\nconst EVM_CHAINS = {\n arbitrum: {\n id: \"eip155:42161\",\n name: \"Arbitrum One\",\n namespace: \"eip155\",\n reference: \"42161\",\n },\n base: { id: \"eip155:8453\", name: \"Base\", namespace: \"eip155\", reference: \"8453\" },\n bsc: { id: \"eip155:56\", name: \"BNB Smart Chain\", namespace: \"eip155\", reference: \"56\" },\n ethereum: { id: \"eip155:1\", name: \"Ethereum\", namespace: \"eip155\", reference: \"1\" },\n optimism: { id: \"eip155:10\", name: \"Optimism\", namespace: \"eip155\", reference: \"10\" },\n polygon: { id: \"eip155:137\", name: \"Polygon\", namespace: \"eip155\", reference: \"137\" },\n sepolia: { id: \"eip155:11155111\", name: \"Sepolia\", namespace: \"eip155\", reference: \"11155111\" },\n} as const satisfies Record<string, ChainBase>;\n\nconst EVM_CHAINS_LIST: ReadonlyArray<ChainBase> = Object.values(EVM_CHAINS);\n\nexport { EVM_CHAINS, EVM_CHAINS_LIST };\n","import type { PlatformDiscoverer } from \"@usebutr/core\";\n\nimport { discoverEvmAdapters } from \"./eip6963\";\nimport { discoverInjectedAdapter } from \"./injected\";\n\n/**\n * EIP-6963 is primary; the legacy `window.ethereum` fallback defers until\n * EIP-6963 has had a chance to fire.\n */\nconst evmDiscoverer: PlatformDiscoverer = {\n fallback: {\n subscribe: (onAdapter, opts) =>\n discoverInjectedAdapter(onAdapter, {\n hasAnyEip6963Adapter: opts.hasAnyPrimaryAdapter,\n }),\n },\n subscribe: discoverEvmAdapters,\n};\n\nexport { evmDiscoverer };\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,22 @@
1
1
  {
2
2
  "name": "@usebutr/evm",
3
- "version": "1.0.1",
3
+ "version": "1.1.1",
4
4
  "description": "EIP-1193 / EIP-6963 / injected wallet discovery + adapters for butr.",
5
+ "keywords": [
6
+ "butr",
7
+ "dapp",
8
+ "eip-6963",
9
+ "ethereum",
10
+ "metamask",
11
+ "react",
12
+ "typescript",
13
+ "wallet",
14
+ "web3"
15
+ ],
16
+ "homepage": "https://docs.usebutr.com/api/evm",
17
+ "bugs": {
18
+ "url": "https://github.com/pedroapfilho/usebutr/issues"
19
+ },
5
20
  "license": "MIT",
6
21
  "author": "Pedro Filho <pedro@filho.me>",
7
22
  "repository": {
@@ -10,7 +25,8 @@
10
25
  "directory": "packages/evm"
11
26
  },
12
27
  "files": [
13
- "dist"
28
+ "dist",
29
+ "CHANGELOG.md"
14
30
  ],
15
31
  "type": "module",
16
32
  "sideEffects": false,
@@ -25,10 +41,10 @@
25
41
  "access": "public"
26
42
  },
27
43
  "dependencies": {
28
- "@usebutr/core": "1.1.0"
44
+ "@usebutr/core": "2.0.1"
29
45
  },
30
46
  "devDependencies": {
31
- "typescript": "^6.0.3",
47
+ "typescript": "^7.0.2",
32
48
  "vitest": "^4.1.10",
33
49
  "@repo/config-vitest": "0.0.0",
34
50
  "@repo/typescript-config": "0.0.0"
@@ -37,7 +53,6 @@
37
53
  "build": "tsdown",
38
54
  "clean": "rm -rf dist",
39
55
  "dev": "tsdown --watch",
40
- "lint": "oxlint src",
41
56
  "test": "vitest run --passWithNoTests",
42
57
  "test:coverage": "vitest run --coverage --passWithNoTests",
43
58
  "typecheck": "tsc -p tsconfig.json --noEmit"