@usebutr/evm 0.2.2 → 0.2.4

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/dist/index.d.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import { ChainBase, PlatformDiscoverer, WalletAdapter, WalletCapabilities, bytesToHexPrefixed as bytesToHex, hexToBytes } from "@usebutr/core";
2
-
3
2
  //#region src/eip1193.d.ts
4
3
  /**
5
- * Minimal EIP-1193 / EIP-6963 type surface — declared inline rather
4
+ * Minimal EIP-1193 / EIP-6963 type surface; declared inline rather
6
5
  * than imported from `@types/ethereum` (which doesn't exist as a
7
6
  * cohesive package) or `viem` (which would expand butr's footprint).
8
7
  *
@@ -17,16 +16,19 @@ type Eip1193RequestArgs = {
17
16
  };
18
17
  type Eip1193Listener = (...args: ReadonlyArray<unknown>) => void;
19
18
  type Eip1193Provider = {
20
- on(event: string, listener: Eip1193Listener): void;
21
- removeListener(event: string, listener: Eip1193Listener): void;
22
- request(args: Eip1193RequestArgs): Promise<unknown>;
19
+ on: (event: string, listener: Eip1193Listener) => void;
20
+ removeListener: (event: string, listener: Eip1193Listener) => void;
21
+ request: (args: Eip1193RequestArgs) => Promise<unknown>;
23
22
  };
24
23
  type Eip6963ProviderInfo = {
25
- /** Data URI or absolute URL pointing at the wallet's icon. */icon: string; /** Human-readable wallet name shown in pickers. */
24
+ /** Data URI or absolute URL pointing at the wallet's icon. */
25
+ icon: string;
26
+ /** Human-readable wallet name shown in pickers. */
26
27
  name: string;
27
- /** Reverse-DNS identifier — `io.metamask`, `io.rabby`, `app.phantom`.
28
+ /** Reverse-DNS identifier: `io.metamask`, `io.rabby`, `app.phantom`.
28
29
  * Stable across versions, unique per wallet, used as the pool key. */
29
- rdns: string; /** Per-page UUID generated by the wallet. Not stable across reloads. */
30
+ rdns: string;
31
+ /** Per-page UUID generated by the wallet. Not stable across reloads. */
30
32
  uuid: string;
31
33
  };
32
34
  type Eip6963ProviderDetail = {
@@ -56,7 +58,7 @@ type DiscoverOptions = {
56
58
  * 3. Wallets that load later still dispatch their own announcement
57
59
  * when they boot, so the listener stays attached for the lifetime
58
60
  * of the discovery session.
59
- * 4. Dedup by `info.rdns` — wallets occasionally re-announce, and
61
+ * 4. Dedup by `info.rdns`; wallets occasionally re-announce, and
60
62
  * `info.uuid` is regenerated per page load so it's not stable.
61
63
  *
62
64
  * Returns an unsubscribe function that removes the listener.
@@ -97,8 +99,8 @@ declare const formatEther: (wei: bigint) => string;
97
99
  * account on the currently-connected chain. The token's own
98
100
  * `decimals()` and `symbol()` are queried for accurate formatting;
99
101
  * if `symbol()` is non-standard (bytes32) it's decoded as best-
100
- * effort. Routed through `eth_call` on the wallet's own provider
101
- * — the demo doesn't ship a separate RPC.
102
+ * effort. Routed through `eth_call` on the wallet's own provider;
103
+ * the demo doesn't ship a separate RPC.
102
104
  * - `getSigner` returns the raw EIP-1193 provider. Wrap it in viem's
103
105
  * `createWalletClient` or ethers' `BrowserProvider` at the call site.
104
106
  */
@@ -113,7 +115,7 @@ declare const buildEvmAdapter: (info: Eip6963ProviderInfo, provider: Eip1193Prov
113
115
  declare const GENERIC_INJECTED_ICON = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNMjEgMTJWN0g1YTIgMiAwIDAgMSAwLTQgaDE0djQiLz48cGF0aCBkPSJNMyA1djE0YTIgMiAwIDAgMCAyIDJoMTZ2LTVNMjEgMTIgaC0zYTIgMiAwIDAgMC0yIDJ2MGEyIDIgMCAwIDAgMiAyaDN6Ii8+PC9zdmc+";
114
116
  type InjectedDiscoveryOptions = {
115
117
  /**
116
- * Predicate the fallback consults before emitting — if any EIP-6963
118
+ * Predicate the fallback consults before emitting; if any EIP-6963
117
119
  * adapter has been registered by the time the settle timer fires,
118
120
  * the injected fallback is skipped (the wallet is already covered
119
121
  * via the standards path; we don't want a duplicate).
@@ -129,7 +131,8 @@ type InjectedDiscoveryOptions = {
129
131
  * frame; 150ms is a conservative settle window that keeps the
130
132
  * picker responsive while giving slow announcements time to land.
131
133
  */
132
- settleMs?: number; /** Override the global `window` reference (tests, iframes). */
134
+ settleMs?: number;
135
+ /** Override the global `window` reference (tests, iframes). */
133
136
  target?: {
134
137
  ethereum?: unknown;
135
138
  } | null;
@@ -145,7 +148,7 @@ type InjectedDiscoveryOptions = {
145
148
  * **Coordination**: the fallback waits `settleMs` (default 150ms)
146
149
  * after subscription, then consults `hasAnyEip6963Adapter` if
147
150
  * provided. If any EIP-6963 adapter has already been registered, we
148
- * skip emission — assume the same wallet is also speaking standards
151
+ * skip emission; assume the same wallet is also speaking standards
149
152
  * and we'd just be duplicating. If no EIP-6963 adapter showed up but
150
153
  * `window.ethereum` does, we emit a generic adapter.
151
154
  *
@@ -157,7 +160,7 @@ declare const discoverInjectedAdapter: (onAdapter: (adapter: WalletAdapter) => v
157
160
  //#region src/chains.d.ts
158
161
  /**
159
162
  * Common EVM chain registry keyed by CAIP-2 id. Useful for chain-
160
- * switcher UIs. butr itself doesn't read this — `Connector.switchChain`
163
+ * switcher UIs. butr itself doesn't read this: `Connector.switchChain`
161
164
  * accepts any `ChainBase`.
162
165
  */
163
166
  declare const EVM_CHAINS: {
@@ -240,7 +243,7 @@ declare module "@usebutr/core" {
240
243
  interface SignerForPlatform {
241
244
  evm: Eip1193Provider;
242
245
  }
243
- } //# sourceMappingURL=signer-augmentation.d.ts.map
246
+ }
244
247
  //#endregion
245
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 };
246
249
  //# 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,kBAAA;EACH,MAAA;EACA,MAAA,GAAS,aAAa;AAAA;AAAA,KAGnB,eAAA,OAAsB,IAAA,EAAM,aAAa;AAAA,KAEzC,eAAA;EACH,EAAA,CAAG,KAAA,UAAe,QAAA,EAAU,eAAA;EAC5B,cAAA,CAAe,KAAA,UAAe,QAAA,EAAU,eAAA;EACxC,OAAA,CAAQ,IAAA,EAAM,kBAAA,GAAqB,OAAA;AAAA;AAAA,KAGhC,mBAAA;EAJqC,8DAMxC,IAAA,UALmC;EAOnC,IAAA;EAP0C;;EAU1C,IAAA,UAZ4B;EAc5B,IAAA;AAAA;AAAA,KAGG,qBAAA;EACH,IAAA,EAAM,mBAAA;EACN,QAAA,EAAU,eAAe;AAAA;AAAA,KAGtB,oBAAA,GAAuB,WAAW,CAAC,qBAAA;;;cCpClC,cAAA;AAAA,cACA,aAAA;AAAA,KAED,eAAA,IAAmB,OAAA,EAAS,aAAA,EAAe,IAAA,EAAM,mBAAmB;AAAA,KAEpE,eAAA;EDGmB;;;ECCtB,MAAA,GAAS,WAAW;AAAA;ADDE;AAAA;;;;AAGsB;AAAA;;;;;;;;;;;;AAHtB,cCwClB,mBAAA,GACJ,SAAA,EAAW,eAAA,EACX,OAAA,GAAS,eAAoB;;;cC1CzB,mBAAA,GAAuB,GAAW;AAAA,cAClC,mBAAA,GAAuB,GAAW;;;cAIlC,WAAA,GAAe,GAAW;;;;AFLR;AAAA;;;;AAGsB;AAAA;;;;;;;;;;;;;;;;;;;;;;cEqHxC,eAAA,GAAmB,IAAA,EAAM,mBAAA,EAAqB,QAAA,EAAU,eAAA,KAAkB,aAAA;;;;;;;;cC3H1E,qBAAA;AAAA,KAKD,wBAAA;EHFH;;;AAAsB;AAAA;;;;AAGsB;AAAA;EGU5C,oBAAA;;;;;;;EAOA,QAAA,WHdA;EGgBA,MAAA;IAAW,QAAA;EAAA;AAAA;;;;;;;;;AHd+B;AAAA;;;;;;;;;cGmDtC,uBAAA,GACJ,SAAA,GAAY,OAAA,EAAS,aAAA,WACrB,OAAA,GAAS,wBAA6B;;;;;;;;cCnElC,UAAA;EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAeA,eAAA,EAAiB,aAAa,CAAC,SAAA;;;cCT/B,kCAAA,EAAkC,GAAA;AAAA,KAInC,sBAAA;EAA2B,IAAI;AAAA;AAAA,cAE9B,0BAAA,GAA8B,KAAA,EAAO,sBAAA,KAAyB,kBAelE;;;;;;;;;;;;;cCnBI,aAAA,EAAe,kBASpB;;;;;;;;;;;;;YCXW,iBAAA;IACR,GAAA,EAAK,eAAe;EAAA;AAAA"}
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"}
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { bytesToHexPrefixed as bytesToHex, hexToBytes, sanitizeIcon } from "@usebutr/core";
1
+ import { bytesToHexPrefixed, bytesToHexPrefixed as bytesToHex, hexToBytes, hexToBytes as hexToBytes$1, sanitizeIcon } from "@usebutr/core";
2
2
 
3
3
  //#region src/capabilities.ts
4
- const EIP6963_RDNS_WITH_REQUEST_ACCOUNTS = new Set(["io.metamask"]);
4
+ const EIP6963_RDNS_WITH_REQUEST_ACCOUNTS = /* @__PURE__ */ new Set(["io.metamask"]);
5
5
  const resolveEip6963Capabilities = (input) => ({
6
6
  getBalance: true,
7
7
  getTransactionReceipt: true,
@@ -20,6 +20,26 @@ const resolveEip6963Capabilities = (input) => ({
20
20
  const HEX_PREFIX = "0x";
21
21
  const ETH_DECIMALS = 18n;
22
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) => {
28
+ const result = await provider.request(args);
29
+ return Array.isArray(result) ? result.filter((item) => typeof item === "string") : [];
30
+ };
31
+ const requestString = async (provider, args) => {
32
+ const result = await provider.request(args);
33
+ return typeof result === "string" ? result : "";
34
+ };
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;
23
43
  const chainIdHexToDecimal = (hex) => BigInt(hex).toString(10);
24
44
  const chainIdDecimalToHex = (dec) => `${HEX_PREFIX}${BigInt(dec).toString(16)}`;
25
45
  /** Pure 18-decimal wei → ether formatter. Trims trailing zeros so
@@ -64,10 +84,10 @@ const decodeAbiString = (hex) => {
64
84
  const length = Number(BigInt(`0x${lengthHex}`));
65
85
  if (length > 0 && clean.length >= 128 + length * 2) {
66
86
  const dataHex = clean.slice(128, 128 + length * 2);
67
- return new TextDecoder().decode(hexToBytes(dataHex));
87
+ return new TextDecoder().decode(hexToBytes$1(dataHex));
68
88
  }
69
89
  }
70
- const buf = hexToBytes(clean.slice(0, 64));
90
+ const buf = hexToBytes$1(clean.slice(0, 64));
71
91
  let end = buf.length;
72
92
  while (end > 0 && buf[end - 1] === 0) end -= 1;
73
93
  return new TextDecoder().decode(buf.subarray(0, end));
@@ -112,8 +132,8 @@ const buildEvmAccount = (address, chain) => ({
112
132
  * account on the currently-connected chain. The token's own
113
133
  * `decimals()` and `symbol()` are queried for accurate formatting;
114
134
  * if `symbol()` is non-standard (bytes32) it's decoded as best-
115
- * effort. Routed through `eth_call` on the wallet's own provider
116
- * — the demo doesn't ship a separate RPC.
135
+ * effort. Routed through `eth_call` on the wallet's own provider;
136
+ * the demo doesn't ship a separate RPC.
117
137
  * - `getSigner` returns the raw EIP-1193 provider. Wrap it in viem's
118
138
  * `createWalletClient` or ethers' `BrowserProvider` at the call site.
119
139
  */
@@ -122,8 +142,8 @@ const buildEvmAdapter = (info, provider) => {
122
142
  capabilities: resolveEip6963Capabilities({ rdns: info.rdns }),
123
143
  chainPlatform: "evm",
124
144
  async connect(opts) {
125
- if (opts?.silent) {
126
- if ((await provider.request({ method: "eth_accounts" })).length === 0) throw new Error("No authorized accounts for silent reconnect");
145
+ if (opts?.silent === true) {
146
+ if ((await requestStringArray(provider, { method: "eth_accounts" })).length === 0) throw new Error("No authorized accounts for silent reconnect");
127
147
  return;
128
148
  }
129
149
  await provider.request({ method: "eth_requestAccounts" });
@@ -137,24 +157,26 @@ const buildEvmAdapter = (info, provider) => {
137
157
  } catch {}
138
158
  },
139
159
  async getAccount() {
140
- const accounts = await provider.request({ method: "eth_accounts" });
160
+ const accounts = await requestStringArray(provider, { method: "eth_accounts" });
141
161
  if (accounts.length === 0) return null;
142
- const chain = buildEvmChain(await provider.request({ method: "eth_chainId" }), info.name);
162
+ const chainIdHex = await requestString(provider, { method: "eth_chainId" });
163
+ const chain = buildEvmChain(chainIdHex, info.name);
143
164
  const first = accounts[0];
144
- if (!first) return null;
165
+ if (first === void 0) return null;
145
166
  return buildEvmAccount(first, chain);
146
167
  },
147
168
  async getAccounts() {
148
- const accounts = await provider.request({ method: "eth_accounts" });
169
+ const accounts = await requestStringArray(provider, { method: "eth_accounts" });
149
170
  if (accounts.length === 0) return [];
150
- const chain = buildEvmChain(await provider.request({ method: "eth_chainId" }), info.name);
171
+ const chainIdHex = await requestString(provider, { method: "eth_chainId" });
172
+ const chain = buildEvmChain(chainIdHex, info.name);
151
173
  return accounts.map((addr) => buildEvmAccount(addr, chain));
152
174
  },
153
175
  async getBalance(mint) {
154
- const first = (await provider.request({ method: "eth_accounts" }))[0];
155
- if (!first) throw new Error("No connected account");
156
- if (!mint) {
157
- const balanceHex = await provider.request({
176
+ const first = (await requestStringArray(provider, { method: "eth_accounts" }))[0];
177
+ if (first === void 0) throw new Error("No connected account");
178
+ if (mint === void 0 || mint === "") {
179
+ const balanceHex = await requestString(provider, {
158
180
  method: "eth_getBalance",
159
181
  params: [first, "latest"]
160
182
  });
@@ -168,21 +190,21 @@ const buildEvmAdapter = (info, provider) => {
168
190
  }
169
191
  const balanceCallData = `${ERC20_BALANCE_OF_SELECTOR}${padAddressForCall(first)}`;
170
192
  const [balanceHex, decimalsHex, symbolHex] = await Promise.all([
171
- provider.request({
193
+ requestString(provider, {
172
194
  method: "eth_call",
173
195
  params: [{
174
196
  data: balanceCallData,
175
197
  to: mint
176
198
  }, "latest"]
177
199
  }),
178
- provider.request({
200
+ requestString(provider, {
179
201
  method: "eth_call",
180
202
  params: [{
181
203
  data: ERC20_DECIMALS_SELECTOR,
182
204
  to: mint
183
205
  }, "latest"]
184
206
  }),
185
- provider.request({
207
+ requestString(provider, {
186
208
  method: "eth_call",
187
209
  params: [{
188
210
  data: ERC20_SYMBOL_SELECTOR,
@@ -200,15 +222,13 @@ const buildEvmAdapter = (info, provider) => {
200
222
  value
201
223
  };
202
224
  },
203
- getSigner() {
204
- return Promise.resolve(provider);
205
- },
225
+ getSigner: () => Promise.resolve(provider),
206
226
  async getTransactionReceipt(tx) {
207
227
  const receipt = await provider.request({
208
228
  method: "eth_getTransactionReceipt",
209
229
  params: [tx]
210
230
  });
211
- if (!receipt) return { status: "Pending" };
231
+ if (receipt === null || typeof receipt !== "object" || !("status" in receipt)) return { status: "Pending" };
212
232
  return { status: receipt.status === "0x1" ? "Success" : "Error" };
213
233
  },
214
234
  icon: sanitizeIcon(info.icon),
@@ -221,9 +241,10 @@ const buildEvmAdapter = (info, provider) => {
221
241
  params: [{ eth_accounts: {} }]
222
242
  });
223
243
  } catch (error) {
224
- const err = error;
225
- const outerCode = err?.code;
226
- const innerCode = err?.data?.originalError?.code;
244
+ const outerCode = typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
245
+ const errData = typeof error === "object" && error !== null && "data" in error ? error.data : void 0;
246
+ const originalError = typeof errData === "object" && errData !== null && "originalError" in errData ? errData.originalError : void 0;
247
+ const innerCode = typeof originalError === "object" && originalError !== null && "code" in originalError ? originalError.code : void 0;
227
248
  if (outerCode === 4200 || outerCode === -32601 || outerCode === -32603 || innerCode === 4200 || innerCode === -32601 || innerCode === -32604) {
228
249
  await provider.request({ method: "eth_requestAccounts" });
229
250
  return;
@@ -232,17 +253,14 @@ const buildEvmAdapter = (info, provider) => {
232
253
  }
233
254
  },
234
255
  async sendTx(tx, account) {
235
- const txWithFrom = account ? {
236
- ...tx,
237
- from: account.walletAddress
238
- } : tx;
239
- return await provider.request({
256
+ const txWithFrom = account ? withFrom(tx, account.walletAddress) : tx;
257
+ return await requestString(provider, {
240
258
  method: "eth_sendTransaction",
241
259
  params: [txWithFrom]
242
260
  });
243
261
  },
244
262
  async sendTxToChain(tx, targetChainIdDecimal, account, cb) {
245
- const current = await provider.request({ method: "eth_chainId" });
263
+ const current = await requestString(provider, { method: "eth_chainId" });
246
264
  const targetHex = chainIdDecimalToHex(targetChainIdDecimal);
247
265
  if (current.toLowerCase() !== targetHex.toLowerCase()) {
248
266
  await provider.request({
@@ -251,39 +269,36 @@ const buildEvmAdapter = (info, provider) => {
251
269
  });
252
270
  cb?.();
253
271
  }
254
- const txWithFrom = account ? {
255
- ...tx,
256
- from: account.walletAddress
257
- } : tx;
258
- return await provider.request({
272
+ const txWithFrom = account ? withFrom(tx, account.walletAddress) : tx;
273
+ return await requestString(provider, {
259
274
  method: "eth_sendTransaction",
260
275
  params: [txWithFrom]
261
276
  });
262
277
  },
263
278
  async signMessage(msg, account) {
264
279
  let signer = account?.walletAddress;
265
- if (!signer) signer = (await provider.request({ method: "eth_accounts" }))[0];
266
- if (!signer) throw new Error("No connected account");
280
+ if (signer === void 0 || signer === "") signer = (await requestStringArray(provider, { method: "eth_accounts" }))[0];
281
+ if (signer === void 0 || signer === "") throw new Error("No connected account");
267
282
  return {
268
- signature: hexToBytes(await provider.request({
283
+ signature: hexToBytes$1(await requestString(provider, {
269
284
  method: "personal_sign",
270
- params: [bytesToHex(msg), signer]
285
+ params: [bytesToHexPrefixed(msg), signer]
271
286
  })),
272
287
  signedMessage: msg
273
288
  };
274
289
  },
275
290
  subscribe(listener) {
276
291
  const onAccountsChanged = (...args) => {
277
- const accs = args[0];
292
+ const accs = toStringArray(args[0]);
278
293
  if (accs.length === 0) {
279
294
  listener({ type: "disconnected" });
280
295
  return;
281
296
  }
282
297
  provider.request({ method: "eth_chainId" }).then((chainIdHex) => {
283
- const chain = buildEvmChain(chainIdHex, info.name);
298
+ const chain = buildEvmChain(typeof chainIdHex === "string" ? chainIdHex : "", info.name);
284
299
  const accounts = accs.map((addr) => buildEvmAccount(addr, chain));
285
300
  const first = accounts[0];
286
- if (!first) return;
301
+ if (first === void 0) return;
287
302
  listener({
288
303
  account: first,
289
304
  accounts,
@@ -292,14 +307,14 @@ const buildEvmAdapter = (info, provider) => {
292
307
  }).catch(() => {});
293
308
  };
294
309
  const onChainChanged = (...args) => {
295
- const chainIdHex = args[0];
310
+ const chainIdHex = typeof args[0] === "string" ? args[0] : "";
296
311
  provider.request({ method: "eth_accounts" }).then((accounts) => {
297
- const accs = accounts;
312
+ const accs = toStringArray(accounts);
298
313
  if (accs.length === 0) return;
299
314
  const chain = buildEvmChain(chainIdHex, info.name);
300
315
  const built = accs.map((addr) => buildEvmAccount(addr, chain));
301
316
  const first = built[0];
302
- if (!first) return;
317
+ if (first === void 0) return;
303
318
  listener({
304
319
  account: first,
305
320
  accounts: built,
@@ -312,12 +327,12 @@ const buildEvmAdapter = (info, provider) => {
312
327
  };
313
328
  const onConnect = () => {
314
329
  Promise.all([provider.request({ method: "eth_accounts" }), provider.request({ method: "eth_chainId" })]).then(([accountsRaw, chainIdHex]) => {
315
- const accs = accountsRaw;
330
+ const accs = toStringArray(accountsRaw);
316
331
  if (accs.length === 0) return;
317
- const chain = buildEvmChain(chainIdHex, info.name);
332
+ const chain = buildEvmChain(typeof chainIdHex === "string" ? chainIdHex : "", info.name);
318
333
  const built = accs.map((addr) => buildEvmAccount(addr, chain));
319
334
  const first = built[0];
320
- if (!first) return;
335
+ if (first === void 0) return;
321
336
  listener({
322
337
  account: first,
323
338
  accounts: built,
@@ -349,11 +364,19 @@ const buildEvmAdapter = (info, provider) => {
349
364
  //#region src/eip6963.ts
350
365
  const ANNOUNCE_EVENT = "eip6963:announceProvider";
351
366
  const REQUEST_EVENT = "eip6963:requestProvider";
367
+ const isEip6963AnnounceEvent = (event) => {
368
+ if (!("detail" in event)) return false;
369
+ const detail = event.detail;
370
+ if (typeof detail !== "object" || detail === null) return false;
371
+ if (!("info" in detail) || !("provider" in detail)) return false;
372
+ const { info, provider } = detail;
373
+ return typeof info === "object" && info !== null && "rdns" in info && typeof info.rdns === "string" && typeof provider === "object" && provider !== null;
374
+ };
375
+ const isEventTarget = (value) => typeof value === "object" && value !== null && "addEventListener" in value && typeof value.addEventListener === "function";
352
376
  const resolveTarget = (target) => {
353
- if (target) return target;
377
+ if (target !== void 0) return target;
354
378
  const candidate = globalThis.window;
355
- if (!candidate || typeof candidate.addEventListener !== "function") return null;
356
- return candidate;
379
+ return isEventTarget(candidate) ? candidate : null;
357
380
  };
358
381
  /**
359
382
  * Subscribe to EIP-6963 wallet announcements. Spec:
@@ -366,7 +389,7 @@ const resolveTarget = (target) => {
366
389
  * 3. Wallets that load later still dispatch their own announcement
367
390
  * when they boot, so the listener stays attached for the lifetime
368
391
  * of the discovery session.
369
- * 4. Dedup by `info.rdns` — wallets occasionally re-announce, and
392
+ * 4. Dedup by `info.rdns`; wallets occasionally re-announce, and
370
393
  * `info.uuid` is regenerated per page load so it's not stable.
371
394
  *
372
395
  * Returns an unsubscribe function that removes the listener.
@@ -378,10 +401,9 @@ const discoverEvmAdapters = (onAdapter, options = {}) => {
378
401
  if (!target) return () => {};
379
402
  const seen = /* @__PURE__ */ new Set();
380
403
  const handler = (event) => {
381
- const detail = event.detail;
382
- if (!detail || !detail.info || !detail.provider) return;
383
- const { info, provider } = detail;
384
- if (typeof info.rdns !== "string" || info.rdns.length === 0) return;
404
+ if (!isEip6963AnnounceEvent(event)) return;
405
+ const { info, provider } = event.detail;
406
+ if (info.rdns.length === 0) return;
385
407
  if (seen.has(info.rdns)) return;
386
408
  seen.add(info.rdns);
387
409
  onAdapter(buildEvmAdapter(info, provider), info);
@@ -402,12 +424,12 @@ const discoverEvmAdapters = (onAdapter, options = {}) => {
402
424
  */
403
425
  const GENERIC_INJECTED_ICON = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNMjEgMTJWN0g1YTIgMiAwIDAgMSAwLTQgaDE0djQiLz48cGF0aCBkPSJNMyA1djE0YTIgMiAwIDAgMCAyIDJoMTZ2LTVNMjEgMTIgaC0zYTIgMiAwIDAgMC0yIDJ2MGEyIDIgMCAwIDAgMiAyaDN6Ii8+PC9zdmc+";
404
426
  const DEFAULT_SETTLE_MS = 150;
427
+ const isEip1193Provider = (value) => typeof value === "object" && value !== null && "request" in value && typeof value.request === "function";
405
428
  const readEthereum = (target) => {
406
429
  const host = target === void 0 ? typeof window === "undefined" ? null : window : target;
407
- if (!host) return null;
430
+ if (typeof host !== "object" || host === null || !("ethereum" in host)) return null;
408
431
  const eth = host.ethereum;
409
- if (!eth || typeof eth.request !== "function") return null;
410
- return eth;
432
+ return isEip1193Provider(eth) ? eth : null;
411
433
  };
412
434
  /**
413
435
  * Last-resort discovery path for EVM wallets that DON'T announce via
@@ -420,7 +442,7 @@ const readEthereum = (target) => {
420
442
  * **Coordination**: the fallback waits `settleMs` (default 150ms)
421
443
  * after subscription, then consults `hasAnyEip6963Adapter` if
422
444
  * provided. If any EIP-6963 adapter has already been registered, we
423
- * skip emission — assume the same wallet is also speaking standards
445
+ * skip emission; assume the same wallet is also speaking standards
424
446
  * and we'd just be duplicating. If no EIP-6963 adapter showed up but
425
447
  * `window.ethereum` does, we emit a generic adapter.
426
448
  *
@@ -432,9 +454,9 @@ const discoverInjectedAdapter = (onAdapter, options = {}) => {
432
454
  let cancelled = false;
433
455
  const timer = setTimeout(() => {
434
456
  if (cancelled) return;
435
- if (options.hasAnyEip6963Adapter?.()) return;
457
+ if (options.hasAnyEip6963Adapter?.() === true) return;
436
458
  const provider = readEthereum(options.target);
437
- if (!provider) return;
459
+ if (provider === null) return;
438
460
  onAdapter(buildEvmAdapter({
439
461
  icon: GENERIC_INJECTED_ICON,
440
462
  name: "Browser wallet",
@@ -452,7 +474,7 @@ const discoverInjectedAdapter = (onAdapter, options = {}) => {
452
474
  //#region src/chains.ts
453
475
  /**
454
476
  * Common EVM chain registry keyed by CAIP-2 id. Useful for chain-
455
- * switcher UIs. butr itself doesn't read this — `Connector.switchChain`
477
+ * switcher UIs. butr itself doesn't read this: `Connector.switchChain`
456
478
  * accepts any `ChainBase`.
457
479
  */
458
480
  const EVM_CHAINS = {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["announce"],"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 // SIWS is Solana-only.\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 // EIP-1193 has no silent \"use address X\" RPC.\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\";\n// EVM uses 0x-prefixed hex; alias the shared prefixed variant to the\n// existing local name to keep the package's public `bytesToHex`/`hexToBytes`\n// contract (and the \"0xdeadbeef\" test) identical.\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\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\n// ERC20 function selectors (first 4 bytes of keccak256 of the\n// signature). Hard-coding avoids pulling in an ABI encoder.\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 // Length-prefixed string (standard ERC20)\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) {\n // EIP-1193 has no silent variant of `eth_requestAccounts`. The\n // closest non-interactive probe is `eth_accounts`, which returns\n // the already-authorized set without a prompt. Empty → reject so\n // hydration drops the stale entry instead of restoring nothing.\n const accounts = (await provider.request({\n method: \"eth_accounts\",\n })) as Array<string>;\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 provider.request({ method: \"eth_accounts\" })) as Array<string>;\n if (accounts.length === 0) {\n return null;\n }\n const chainIdHex = (await provider.request({ method: \"eth_chainId\" })) as string;\n const chain = buildEvmChain(chainIdHex, info.name);\n const first = accounts[0];\n if (!first) {\n return null;\n }\n return buildEvmAccount(first, chain);\n },\n\n async getAccounts() {\n const accounts = (await provider.request({ method: \"eth_accounts\" })) as Array<string>;\n if (accounts.length === 0) {\n return [];\n }\n const chainIdHex = (await provider.request({ method: \"eth_chainId\" })) as string;\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 provider.request({ method: \"eth_accounts\" })) as Array<string>;\n const first = accounts[0];\n if (!first) {\n throw new Error(\"No connected account\");\n }\n if (!mint) {\n // Native ETH path.\n const balanceHex = (await provider.request({\n method: \"eth_getBalance\",\n params: [first, \"latest\"],\n })) as string;\n const value = BigInt(balanceHex);\n return {\n decimals: Number(ETH_DECIMALS),\n formatted: formatEther(value),\n symbol: \"ETH\",\n value,\n };\n }\n // ERC20 path. Three `eth_call`s in parallel — the wallet's RPC\n // handles them locally so we don't need an external provider.\n const balanceCallData = `${ERC20_BALANCE_OF_SELECTOR}${padAddressForCall(first)}`;\n const [balanceHex, decimalsHex, symbolHex] = (await Promise.all([\n provider.request({\n method: \"eth_call\",\n params: [{ data: balanceCallData, to: mint }, \"latest\"],\n }),\n provider.request({\n method: \"eth_call\",\n params: [{ data: ERC20_DECIMALS_SELECTOR, to: mint }, \"latest\"],\n }),\n provider.request({\n method: \"eth_call\",\n params: [{ data: ERC20_SYMBOL_SELECTOR, to: mint }, \"latest\"],\n }),\n ])) as [string, string, string];\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() {\n // Consumers cast this to a viem WalletClient / ethers\n // BrowserProvider / their preferred wrapper.\n return Promise.resolve(provider);\n },\n\n async getTransactionReceipt(tx) {\n const receipt = (await provider.request({\n method: \"eth_getTransactionReceipt\",\n params: [tx],\n })) as { status: string } | null;\n if (!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 //\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 err = error as { code?: number; data?: { originalError?: { code?: number } } } | null;\n const outerCode = err?.code;\n const innerCode = err?.data?.originalError?.code;\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\n ? { ...(tx as Record<string, unknown>), from: account.walletAddress }\n : tx;\n const hash = (await provider.request({\n method: \"eth_sendTransaction\",\n params: [txWithFrom],\n })) as string;\n return hash;\n },\n\n async sendTxToChain(tx, targetChainIdDecimal, account, cb) {\n const current = (await provider.request({ method: \"eth_chainId\" })) as string;\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\n ? { ...(tx as Record<string, unknown>), from: account.walletAddress }\n : tx;\n const hash = (await provider.request({\n method: \"eth_sendTransaction\",\n params: [txWithFrom],\n })) as string;\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) {\n const accounts = (await provider.request({ method: \"eth_accounts\" })) as Array<string>;\n signer = accounts[0];\n }\n if (!signer) {\n throw new Error(\"No connected account\");\n }\n const signatureHex = (await provider.request({\n method: \"personal_sign\",\n params: [bytesToHex(msg), signer],\n })) as string;\n return { signature: hexToBytes(signatureHex), signedMessage: msg };\n },\n\n subscribe(listener) {\n const onAccountsChanged: Eip1193Listener = (...args) => {\n const accs = args[0] as Array<string>;\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(chainIdHex as string, info.name);\n const accounts = accs.map((addr) => buildEvmAccount(addr, chain));\n const first = accounts[0];\n if (!first) {\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 = args[0] as string;\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 = accounts as Array<string>;\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) {\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 // EIP-1193 `connect` fires when the provider (re)connects to a\n // chain — e.g. the wallet comes back after being unreachable.\n // Re-read accounts + chain and resync the pool entry; state is\n // otherwise only derived from `accountsChanged`/`chainChanged`.\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 = accountsRaw as Array<string>;\n if (accs.length === 0) {\n return undefined;\n }\n const chain = buildEvmChain(chainIdHex as string, info.name);\n const built = accs.map((addr) => buildEvmAccount(addr, chain));\n const first = built[0];\n if (!first) {\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 {\n buildEvmAdapter,\n bytesToHex,\n chainIdDecimalToHex,\n chainIdHexToDecimal,\n formatEther,\n hexToBytes,\n};\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 resolveTarget = (target?: EventTarget): EventTarget | null => {\n if (target) {\n return target;\n }\n // Use globalThis.window when present and EventTarget-shaped. Some\n // test environments inject a `window` stub that isn't a real\n // EventTarget; in that case fall back to null so callers short-circuit\n // rather than throw on `.addEventListener`.\n const candidate = (globalThis as { window?: unknown }).window;\n if (\n !candidate ||\n typeof (candidate as { addEventListener?: unknown }).addEventListener !== \"function\"\n ) {\n return null;\n }\n return candidate as EventTarget;\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 const announce = event as Eip6963AnnounceEvent;\n const detail = announce.detail;\n if (!detail || !detail.info || !detail.provider) {\n return;\n }\n const { info, provider } = detail;\n if (typeof info.rdns !== \"string\" || 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 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 =\n typeof window === \"undefined\" ? null : (window as unknown as { ethereum?: unknown });\n const host = target === undefined ? globalWindow : target;\n if (!host) {\n return null;\n }\n const eth = (host as { ethereum?: unknown }).ethereum;\n if (!eth || typeof (eth as { request?: unknown }).request !== \"function\") {\n return null;\n }\n return eth as Eip1193Provider;\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?.()) {\n return;\n }\n const provider = readEthereum(options.target);\n if (!provider) {\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 platform: \"evm\",\n subscribe: discoverEvmAdapters,\n};\n\nexport { evmDiscoverer };\n"],"mappings":"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"}
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 platform: \"evm\",\n subscribe: discoverEvmAdapters,\n};\n\nexport { evmDiscoverer };\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usebutr/evm",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "EIP-1193 / EIP-6963 / injected wallet discovery + adapters for butr.",
5
5
  "license": "MIT",
6
6
  "author": "Pedro Filho <pedro@filho.me>",
@@ -25,11 +25,11 @@
25
25
  "access": "public"
26
26
  },
27
27
  "dependencies": {
28
- "@usebutr/core": "0.4.0"
28
+ "@usebutr/core": "0.4.2"
29
29
  },
30
30
  "devDependencies": {
31
31
  "typescript": "^6.0.3",
32
- "vitest": "^4.1.8",
32
+ "vitest": "^4.1.10",
33
33
  "@repo/config-vitest": "0.0.0",
34
34
  "@repo/typescript-config": "0.0.0"
35
35
  },