@usebutr/evm 1.0.0 → 1.1.0
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 +23 -97
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +151 -230
- package/dist/index.js.map +1 -1
- package/package.json +2 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
import { ChainBase, PlatformDiscoverer, WalletAdapter, WalletCapabilities, bytesToHexPrefixed as bytesToHex, hexToBytes } from "@usebutr/core";
|
|
2
2
|
//#region src/eip1193.d.ts
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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
|
*/
|
|
13
8
|
type Eip1193RequestArgs = {
|
|
14
9
|
method: string;
|
|
@@ -48,61 +43,22 @@ type DiscoverOptions = {
|
|
|
48
43
|
target?: EventTarget;
|
|
49
44
|
};
|
|
50
45
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
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.
|
|
46
|
+
* Spec: https://eips.ethereum.org/EIPS/eip-6963. Dedupe is by `info.rdns`
|
|
47
|
+
* because `info.uuid` is regenerated per page load. The listener stays
|
|
48
|
+
* attached for the session so wallets that boot late still register.
|
|
67
49
|
*/
|
|
68
50
|
declare const discoverEvmAdapters: (onAdapter: AdapterCallback, options?: DiscoverOptions) => (() => void);
|
|
69
51
|
//#endregion
|
|
52
|
+
//#region src/evm-balance.d.ts
|
|
53
|
+
declare const formatEther: (wei: bigint) => string;
|
|
54
|
+
//#endregion
|
|
70
55
|
//#region src/eip6963-adapter.d.ts
|
|
71
56
|
declare const chainIdHexToDecimal: (hex: string) => string;
|
|
72
57
|
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
58
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
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.
|
|
59
|
+
* `disconnect` calls `wallet_revokePermissions`, which many wallets don't
|
|
60
|
+
* implement and silently ignore, so their own auto-reconnect may outlive it.
|
|
61
|
+
* `getBalance()` labels the native balance `"ETH"` on every EVM chain.
|
|
106
62
|
*/
|
|
107
63
|
declare const buildEvmAdapter: (info: Eip6963ProviderInfo, provider: Eip1193Provider) => WalletAdapter;
|
|
108
64
|
//#endregion
|
|
@@ -115,21 +71,14 @@ declare const buildEvmAdapter: (info: Eip6963ProviderInfo, provider: Eip1193Prov
|
|
|
115
71
|
declare const GENERIC_INJECTED_ICON = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNMjEgMTJWN0g1YTIgMiAwIDAgMSAwLTQgaDE0djQiLz48cGF0aCBkPSJNMyA1djE0YTIgMiAwIDAgMCAyIDJoMTZ2LTVNMjEgMTIgaC0zYTIgMiAwIDAgMC0yIDJ2MGEyIDIgMCAwIDAgMiAyaDN6Ii8+PC9zdmc+";
|
|
116
72
|
type InjectedDiscoveryOptions = {
|
|
117
73
|
/**
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
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).
|
|
74
|
+
* Consulted before emitting: an already-registered EIP-6963 adapter means
|
|
75
|
+
* the wallet is covered by the standards path and emitting would duplicate
|
|
76
|
+
* it. Omit to always emit.
|
|
126
77
|
*/
|
|
127
78
|
hasAnyEip6963Adapter?: () => boolean;
|
|
128
79
|
/**
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
* frame; 150ms is a conservative settle window that keeps the
|
|
132
|
-
* picker responsive while giving slow announcements time to land.
|
|
80
|
+
* Most wallets announce within the first frame; 150ms leaves room for slow
|
|
81
|
+
* announcements without stalling the picker.
|
|
133
82
|
*/
|
|
134
83
|
settleMs?: number;
|
|
135
84
|
/** Override the global `window` reference (tests, iframes). */
|
|
@@ -138,22 +87,9 @@ type InjectedDiscoveryOptions = {
|
|
|
138
87
|
} | null;
|
|
139
88
|
};
|
|
140
89
|
/**
|
|
141
|
-
* Last-resort
|
|
142
|
-
*
|
|
143
|
-
*
|
|
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.
|
|
90
|
+
* Last-resort path for the long tail of EVM wallets that expose only
|
|
91
|
+
* `window.ethereum` and never announce via EIP-6963. The emitted adapter
|
|
92
|
+
* carries `rdns: "injected:legacy"` so consumers can tell it apart.
|
|
157
93
|
*/
|
|
158
94
|
declare const discoverInjectedAdapter: (onAdapter: (adapter: WalletAdapter) => void, options?: InjectedDiscoveryOptions) => (() => void);
|
|
159
95
|
//#endregion
|
|
@@ -218,26 +154,16 @@ declare const resolveEip6963Capabilities: (input: Eip6963CapabilityInput) => Wal
|
|
|
218
154
|
//#endregion
|
|
219
155
|
//#region src/discoverer.d.ts
|
|
220
156
|
/**
|
|
221
|
-
*
|
|
222
|
-
* legacy `window.ethereum` injected fallback wired to defer until
|
|
157
|
+
* EIP-6963 is primary; the legacy `window.ethereum` fallback defers until
|
|
223
158
|
* 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
159
|
*/
|
|
230
160
|
declare const evmDiscoverer: PlatformDiscoverer;
|
|
231
161
|
//#endregion
|
|
232
162
|
//#region src/signer-augmentation.d.ts
|
|
233
163
|
/**
|
|
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
164
|
* `getSigner()` itself still returns `Promise<unknown>` (see
|
|
239
|
-
* `packages/core/src/types/signer.ts` for why); this
|
|
240
|
-
*
|
|
165
|
+
* `packages/core/src/types/signer.ts` for why); this only changes what
|
|
166
|
+
* `SignerForPlatform["evm"]` evaluates to.
|
|
241
167
|
*/
|
|
242
168
|
declare module "@usebutr/core" {
|
|
243
169
|
interface SignerForPlatform {
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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":"
|
|
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;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;;;cC/BlC;cACA;KAED,mBAAmB,SAAS,eAAe,MAAM;KAEjD;;;;EAIH,SAAS;;;;;;;cA4CL,sBACJ,WAAW,iBACX,UAAS;;;cC3BL,cAAe;;;cCdf,sBAAuB;cACvB,sBAAuB;;;;;;cAoDvB,kBAAmB,MAAM,qBAAqB,UAAU,oBAAkB;;;;;;;;cC9D1E;KAKD;;;;;;EAMH;;;;;EAKA;;EAEA;IAAW;;;;;;;;cAwBP,0BACJ,YAAY,SAAS,wBACrB,UAAS;;;;;;;;cC/CL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAeA,iBAAiB,cAAc;;;cCpB/B,oCAAkC;KAInC;EAA2B;;cAE1B,6BAA8B,OAAO,2BAAyB;;;;;;;cCC9D,eAAe;;;;;;;;;YCAT;IACR,KAAK"}
|
package/dist/index.js
CHANGED
|
@@ -16,82 +16,116 @@ const resolveEip6963Capabilities = (input) => ({
|
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
//#endregion
|
|
19
|
-
//#region src/
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
*
|
|
25
|
-
|
|
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
|
|
27
|
+
return typeof result === "string" ? result : null;
|
|
30
28
|
};
|
|
31
|
-
const
|
|
29
|
+
const requestStringArray = async (provider, args) => {
|
|
32
30
|
const result = await provider.request(args);
|
|
33
|
-
return
|
|
31
|
+
return Array.isArray(result) ? result.filter((item) => typeof item === "string") : [];
|
|
34
32
|
};
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
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(/* @__PURE__ */ 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
|
-
|
|
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
|
-
|
|
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
|
|
66
|
-
const
|
|
67
|
-
const ERC20_SYMBOL_SELECTOR = "0x95d89b41";
|
|
68
|
-
const padAddressForCall = (address) => address.replace(/* @__PURE__ */ 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
|
-
*/
|
|
54
|
+
const formatEther = (wei) => formatUnits(wei, Number(ETH_DECIMALS));
|
|
55
|
+
const padAddressForCall = (address) => address.replace(new RegExp("^0x", "v"), "").toLowerCase().padStart(64, "0");
|
|
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
|
-
|
|
87
|
-
|
|
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
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
+
};
|
|
94
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
|
+
/** Merge a `from` address into a transaction object of unknown shape.
|
|
121
|
+
* butr's `sendTx` accepts `tx: unknown` (viem/ethers/raw all differ);
|
|
122
|
+
* when it's an object we overlay `from`, otherwise pass it through. */
|
|
123
|
+
const withFrom = (tx, from) => typeof tx === "object" && tx !== null ? {
|
|
124
|
+
...tx,
|
|
125
|
+
from
|
|
126
|
+
} : tx;
|
|
127
|
+
const chainIdHexToDecimal = (hex) => BigInt(hex).toString(10);
|
|
128
|
+
const chainIdDecimalToHex = (dec) => `${HEX_PREFIX}${BigInt(dec).toString(16)}`;
|
|
95
129
|
const buildEvmChain = (chainIdHex, walletName) => {
|
|
96
130
|
const reference = chainIdHexToDecimal(chainIdHex);
|
|
97
131
|
return {
|
|
@@ -106,36 +140,26 @@ const buildEvmAccount = (address, chain) => ({
|
|
|
106
140
|
id: `${chain.id}:${address.toLowerCase()}`,
|
|
107
141
|
walletAddress: address
|
|
108
142
|
});
|
|
143
|
+
/** Rejections propagate: `getAccount`/`getAccounts` surface the wallet's
|
|
144
|
+
* own provider error, while the `subscribe` bridge swallows it because a
|
|
145
|
+
* later event retries synchronization. */
|
|
146
|
+
const readAccountState = async (provider, walletName, options = {}) => {
|
|
147
|
+
const accounts = options.accounts ?? await requestStringArray(provider, { method: "eth_accounts" });
|
|
148
|
+
if (accounts.length === 0) return null;
|
|
149
|
+
const chainIdHex = options.chainIdHex ?? await requestString(provider, { method: "eth_chainId" });
|
|
150
|
+
if (chainIdHex === null) throw new Error("Wallet returned a malformed eth_chainId response");
|
|
151
|
+
const chain = buildEvmChain(chainIdHex, walletName);
|
|
152
|
+
const builtAccounts = accounts.map((address) => buildEvmAccount(address, chain));
|
|
153
|
+
const account = builtAccounts[0];
|
|
154
|
+
return account === void 0 ? null : {
|
|
155
|
+
account,
|
|
156
|
+
accounts: builtAccounts
|
|
157
|
+
};
|
|
158
|
+
};
|
|
109
159
|
/**
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
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.
|
|
160
|
+
* `disconnect` calls `wallet_revokePermissions`, which many wallets don't
|
|
161
|
+
* implement and silently ignore, so their own auto-reconnect may outlive it.
|
|
162
|
+
* `getBalance()` labels the native balance `"ETH"` on every EVM chain.
|
|
139
163
|
*/
|
|
140
164
|
const buildEvmAdapter = (info, provider) => {
|
|
141
165
|
return {
|
|
@@ -157,70 +181,15 @@ const buildEvmAdapter = (info, provider) => {
|
|
|
157
181
|
} catch {}
|
|
158
182
|
},
|
|
159
183
|
async getAccount() {
|
|
160
|
-
|
|
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);
|
|
184
|
+
return (await readAccountState(provider, info.name))?.account ?? null;
|
|
167
185
|
},
|
|
168
186
|
async getAccounts() {
|
|
169
|
-
|
|
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));
|
|
187
|
+
return (await readAccountState(provider, info.name))?.accounts ?? [];
|
|
174
188
|
},
|
|
175
189
|
async getBalance(mint) {
|
|
176
190
|
const first = (await requestStringArray(provider, { method: "eth_accounts" }))[0];
|
|
177
191
|
if (first === void 0) throw new Error("No connected account");
|
|
178
|
-
|
|
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
|
-
};
|
|
192
|
+
return readEvmBalance(provider, first, mint);
|
|
224
193
|
},
|
|
225
194
|
getSigner: () => Promise.resolve(provider),
|
|
226
195
|
async getTransactionReceipt(tx) {
|
|
@@ -254,15 +223,17 @@ const buildEvmAdapter = (info, provider) => {
|
|
|
254
223
|
},
|
|
255
224
|
async sendTx(tx, account) {
|
|
256
225
|
const txWithFrom = account ? withFrom(tx, account.walletAddress) : tx;
|
|
257
|
-
|
|
226
|
+
const hash = await requestString(provider, {
|
|
258
227
|
method: "eth_sendTransaction",
|
|
259
228
|
params: [txWithFrom]
|
|
260
229
|
});
|
|
230
|
+
if (hash === null) throw new Error("Wallet returned no transaction hash");
|
|
231
|
+
return hash;
|
|
261
232
|
},
|
|
262
233
|
async sendTxToChain(tx, targetChainIdDecimal, account, cb) {
|
|
263
234
|
const current = await requestString(provider, { method: "eth_chainId" });
|
|
264
235
|
const targetHex = chainIdDecimalToHex(targetChainIdDecimal);
|
|
265
|
-
if (current
|
|
236
|
+
if (current?.toLowerCase() !== targetHex.toLowerCase()) {
|
|
266
237
|
await provider.request({
|
|
267
238
|
method: "wallet_switchEthereumChain",
|
|
268
239
|
params: [{ chainId: targetHex }]
|
|
@@ -270,75 +241,55 @@ const buildEvmAdapter = (info, provider) => {
|
|
|
270
241
|
cb?.();
|
|
271
242
|
}
|
|
272
243
|
const txWithFrom = account ? withFrom(tx, account.walletAddress) : tx;
|
|
273
|
-
|
|
244
|
+
const hash = await requestString(provider, {
|
|
274
245
|
method: "eth_sendTransaction",
|
|
275
246
|
params: [txWithFrom]
|
|
276
247
|
});
|
|
248
|
+
if (hash === null) throw new Error("Wallet returned no transaction hash");
|
|
249
|
+
return hash;
|
|
277
250
|
},
|
|
278
251
|
async signMessage(msg, account) {
|
|
279
252
|
let signer = account?.walletAddress;
|
|
280
253
|
if (signer === void 0 || signer === "") signer = (await requestStringArray(provider, { method: "eth_accounts" }))[0];
|
|
281
254
|
if (signer === void 0 || signer === "") throw new Error("No connected account");
|
|
255
|
+
const signatureHex = await requestString(provider, {
|
|
256
|
+
method: "personal_sign",
|
|
257
|
+
params: [bytesToHexPrefixed(msg), signer]
|
|
258
|
+
});
|
|
259
|
+
if (signatureHex === null) throw new Error("Wallet returned a malformed personal_sign response");
|
|
282
260
|
return {
|
|
283
|
-
signature: hexToBytes$1(
|
|
284
|
-
method: "personal_sign",
|
|
285
|
-
params: [bytesToHexPrefixed(msg), signer]
|
|
286
|
-
})),
|
|
261
|
+
signature: hexToBytes$1(signatureHex),
|
|
287
262
|
signedMessage: msg
|
|
288
263
|
};
|
|
289
264
|
},
|
|
290
265
|
subscribe(listener) {
|
|
266
|
+
const synchronizeAccount = async (options) => {
|
|
267
|
+
try {
|
|
268
|
+
const state = await readAccountState(provider, info.name, options);
|
|
269
|
+
if (state !== null) listener({
|
|
270
|
+
...state,
|
|
271
|
+
type: "accountChanged"
|
|
272
|
+
});
|
|
273
|
+
} catch {}
|
|
274
|
+
};
|
|
291
275
|
const onAccountsChanged = (...args) => {
|
|
292
276
|
const accs = toStringArray(args[0]);
|
|
293
277
|
if (accs.length === 0) {
|
|
294
278
|
listener({ type: "disconnected" });
|
|
295
279
|
return;
|
|
296
280
|
}
|
|
297
|
-
|
|
298
|
-
const chain = buildEvmChain(typeof chainIdHex === "string" ? chainIdHex : "", info.name);
|
|
299
|
-
const accounts = accs.map((addr) => buildEvmAccount(addr, chain));
|
|
300
|
-
const first = accounts[0];
|
|
301
|
-
if (first === void 0) return;
|
|
302
|
-
listener({
|
|
303
|
-
account: first,
|
|
304
|
-
accounts,
|
|
305
|
-
type: "accountChanged"
|
|
306
|
-
});
|
|
307
|
-
}).catch(() => {});
|
|
281
|
+
synchronizeAccount({ accounts: accs });
|
|
308
282
|
};
|
|
309
283
|
const onChainChanged = (...args) => {
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
if (accs.length === 0) return;
|
|
314
|
-
const chain = buildEvmChain(chainIdHex, info.name);
|
|
315
|
-
const built = accs.map((addr) => buildEvmAccount(addr, chain));
|
|
316
|
-
const first = built[0];
|
|
317
|
-
if (first === void 0) return;
|
|
318
|
-
listener({
|
|
319
|
-
account: first,
|
|
320
|
-
accounts: built,
|
|
321
|
-
type: "accountChanged"
|
|
322
|
-
});
|
|
323
|
-
}).catch(() => {});
|
|
284
|
+
const [chainId] = args;
|
|
285
|
+
const chainIdHex = typeof chainId === "string" && chainId.length > 0 ? chainId : void 0;
|
|
286
|
+
synchronizeAccount({ chainIdHex });
|
|
324
287
|
};
|
|
325
288
|
const onDisconnect = () => {
|
|
326
289
|
listener({ type: "disconnected" });
|
|
327
290
|
};
|
|
328
291
|
const onConnect = () => {
|
|
329
|
-
|
|
330
|
-
const accs = toStringArray(accountsRaw);
|
|
331
|
-
if (accs.length === 0) return;
|
|
332
|
-
const chain = buildEvmChain(typeof chainIdHex === "string" ? chainIdHex : "", info.name);
|
|
333
|
-
const built = accs.map((addr) => buildEvmAccount(addr, chain));
|
|
334
|
-
const first = built[0];
|
|
335
|
-
if (first === void 0) return;
|
|
336
|
-
listener({
|
|
337
|
-
account: first,
|
|
338
|
-
accounts: built,
|
|
339
|
-
type: "accountChanged"
|
|
340
|
-
});
|
|
341
|
-
}).catch(() => {});
|
|
292
|
+
synchronizeAccount();
|
|
342
293
|
};
|
|
343
294
|
provider.on("accountsChanged", onAccountsChanged);
|
|
344
295
|
provider.on("chainChanged", onChainChanged);
|
|
@@ -352,9 +303,11 @@ const buildEvmAdapter = (info, provider) => {
|
|
|
352
303
|
};
|
|
353
304
|
},
|
|
354
305
|
async switchChain(chain) {
|
|
306
|
+
const targetHex = chainIdDecimalToHex(chain.reference);
|
|
307
|
+
if ((await requestString(provider, { method: "eth_chainId" }))?.toLowerCase() === targetHex.toLowerCase()) return;
|
|
355
308
|
await provider.request({
|
|
356
309
|
method: "wallet_switchEthereumChain",
|
|
357
|
-
params: [{ chainId:
|
|
310
|
+
params: [{ chainId: targetHex }]
|
|
358
311
|
});
|
|
359
312
|
}
|
|
360
313
|
};
|
|
@@ -379,22 +332,9 @@ const resolveTarget = (target) => {
|
|
|
379
332
|
return isEventTarget(candidate) ? candidate : null;
|
|
380
333
|
};
|
|
381
334
|
/**
|
|
382
|
-
*
|
|
383
|
-
*
|
|
384
|
-
*
|
|
385
|
-
* Flow:
|
|
386
|
-
* 1. Page registers a listener for `eip6963:announceProvider`.
|
|
387
|
-
* 2. Page dispatches `eip6963:requestProvider` to kick wallets that
|
|
388
|
-
* have already loaded.
|
|
389
|
-
* 3. Wallets that load later still dispatch their own announcement
|
|
390
|
-
* when they boot, so the listener stays attached for the lifetime
|
|
391
|
-
* of the discovery session.
|
|
392
|
-
* 4. Dedup by `info.rdns`; wallets occasionally re-announce, and
|
|
393
|
-
* `info.uuid` is regenerated per page load so it's not stable.
|
|
394
|
-
*
|
|
395
|
-
* Returns an unsubscribe function that removes the listener.
|
|
396
|
-
* In non-browser environments (SSR, tests with no window) returns
|
|
397
|
-
* a noop unsubscribe and never fires the callback.
|
|
335
|
+
* Spec: https://eips.ethereum.org/EIPS/eip-6963. Dedupe is by `info.rdns`
|
|
336
|
+
* because `info.uuid` is regenerated per page load. The listener stays
|
|
337
|
+
* attached for the session so wallets that boot late still register.
|
|
398
338
|
*/
|
|
399
339
|
const discoverEvmAdapters = (onAdapter, options = {}) => {
|
|
400
340
|
const target = resolveTarget(options.target);
|
|
@@ -432,22 +372,9 @@ const readEthereum = (target) => {
|
|
|
432
372
|
return isEip1193Provider(eth) ? eth : null;
|
|
433
373
|
};
|
|
434
374
|
/**
|
|
435
|
-
* Last-resort
|
|
436
|
-
*
|
|
437
|
-
*
|
|
438
|
-
* (`discoverEvmAdapters`) covers them. This module catches the long
|
|
439
|
-
* tail: regional wallets (KuCoin, Bitget, BitKeep, Okto, Frontier)
|
|
440
|
-
* and older builds that only expose `window.ethereum`.
|
|
441
|
-
*
|
|
442
|
-
* **Coordination**: the fallback waits `settleMs` (default 150ms)
|
|
443
|
-
* after subscription, then consults `hasAnyEip6963Adapter` if
|
|
444
|
-
* provided. If any EIP-6963 adapter has already been registered, we
|
|
445
|
-
* skip emission; assume the same wallet is also speaking standards
|
|
446
|
-
* and we'd just be duplicating. If no EIP-6963 adapter showed up but
|
|
447
|
-
* `window.ethereum` does, we emit a generic adapter.
|
|
448
|
-
*
|
|
449
|
-
* The emitted adapter has `rdns: "injected:legacy"` so consumers can
|
|
450
|
-
* distinguish it from EIP-6963 adapters in the picker UI.
|
|
375
|
+
* Last-resort path for the long tail of EVM wallets that expose only
|
|
376
|
+
* `window.ethereum` and never announce via EIP-6963. The emitted adapter
|
|
377
|
+
* carries `rdns: "injected:legacy"` so consumers can tell it apart.
|
|
451
378
|
*/
|
|
452
379
|
const discoverInjectedAdapter = (onAdapter, options = {}) => {
|
|
453
380
|
const settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;
|
|
@@ -526,14 +453,8 @@ const EVM_CHAINS_LIST = Object.values(EVM_CHAINS);
|
|
|
526
453
|
//#endregion
|
|
527
454
|
//#region src/discoverer.ts
|
|
528
455
|
/**
|
|
529
|
-
*
|
|
530
|
-
* legacy `window.ethereum` injected fallback wired to defer until
|
|
456
|
+
* EIP-6963 is primary; the legacy `window.ethereum` fallback defers until
|
|
531
457
|
* EIP-6963 has had a chance to fire.
|
|
532
|
-
*
|
|
533
|
-
* Consumers using `@usebutr/wallets`'s `autoDiscovery()` get this
|
|
534
|
-
* wired in automatically. Direct consumers who don't want the
|
|
535
|
-
* injected fallback build their own discovery source from
|
|
536
|
-
* `discoverEvmAdapters` alone.
|
|
537
458
|
*/
|
|
538
459
|
const evmDiscoverer = {
|
|
539
460
|
fallback: { subscribe: (onAdapter, opts) => discoverInjectedAdapter(onAdapter, { hasAnyEip6963Adapter: opts.hasAnyPrimaryAdapter }) },
|
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 Eip1193RequestArgs = {\n method: string;\n params?: ReadonlyArray<unknown> | object;\n};\n\ntype Eip1193Listener = (...args: ReadonlyArray<unknown>) => void;\n\ntype Eip1193Provider = {\n on: (event: string, listener: Eip1193Listener) => void;\n removeListener: (event: string, listener: Eip1193Listener) => void;\n request: (args: Eip1193RequestArgs) => Promise<unknown>;\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 Eip1193Provider,\n Eip1193RequestArgs,\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 { 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\";\nimport { requestString, requestStringArray } from \"./eip1193\";\nimport { readEvmBalance } from \"./evm-balance\";\n\nconst HEX_PREFIX = \"0x\";\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\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: 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 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 = account ? withFrom(tx, account.walletAddress) : tx;\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 \"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 * 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 } 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?: 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 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 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 };\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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usebutr/evm",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
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,7 +25,7 @@
|
|
|
25
25
|
"access": "public"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@usebutr/core": "
|
|
28
|
+
"@usebutr/core": "2.0.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"typescript": "^6.0.3",
|
|
@@ -37,7 +37,6 @@
|
|
|
37
37
|
"build": "tsdown",
|
|
38
38
|
"clean": "rm -rf dist",
|
|
39
39
|
"dev": "tsdown --watch",
|
|
40
|
-
"lint": "oxlint src",
|
|
41
40
|
"test": "vitest run --passWithNoTests",
|
|
42
41
|
"test:coverage": "vitest run --coverage --passWithNoTests",
|
|
43
42
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|