@usebutr/evm 1.1.0 → 2.0.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/CHANGELOG.md +208 -0
- package/LICENSE +21 -0
- package/README.md +55 -0
- package/dist/index.d.ts +28 -75
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +107 -192
- package/dist/index.js.map +1 -1
- package/package.json +21 -5
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# @usebutr/evm
|
|
2
|
+
|
|
3
|
+
## 2.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- [#203](https://github.com/pedroapfilho/usebutr/pull/203) [`1263f86`](https://github.com/pedroapfilho/usebutr/commit/1263f8666afd43b4f7dbf8df1763709c3edf09d9) Thanks [@pedroapfilho](https://github.com/pedroapfilho)! - Adapters now say what they can do by having the method: `capabilities` is gone, and an optional method (`sendTx`, `signMessage`, `getBalance`, `switchChain`, …) exists only when it works for that wallet. No adapter returns a placeholder balance or a receipt that stays pending anymore.
|
|
8
|
+
|
|
9
|
+
- `sendTx(tx, { account, chain })` replaces `sendTx` and `sendTxToChain`, and `signMessage`, `signTransaction` and `getBalance` take options objects. `chain` is a `ChainBase`. An adapter either routes the call to that chain, switches the wallet to it, or rejects; none ignores it.
|
|
10
|
+
- An `account` the wallet does not expose now rejects instead of signing with the first account. `chain.name` is the chain's name, never the wallet's.
|
|
11
|
+
- `getAccounts()` resolves the active account first. `getAccount` and `switchAccount` are removed.
|
|
12
|
+
- `getSigner()` resolves a tagged `WalletSigner` (`eip1193`, `wallet-standard`, `walletconnect`, `ledger-*`, …), so it can be narrowed with `switch (signer.kind)` instead of cast.
|
|
13
|
+
- `createWalletManager(config, { initialState })` owns discovery, hydration and persistence, and works without React. `config.sources` replaces the `discovery`, `connectors` and `createConnector` props. `fromAdapters` registers WalletConnect, Ledger and hand-built adapters, which now also appear in `useDiscoveredWallets()`.
|
|
14
|
+
- `WalletManagerProvider` takes `config` and `initialState`. `useSigner(wallet)` and `useBalance(wallet, { token, account })` take the wallet entry itself, and id-taking hooks read the active wallet only when the id is omitted (`null` reads none). The hooks are `useWalletManager`, `useWalletState`, `useConnect`, `useWallet`, `useSelectedWallet` (now typed per platform), `useAccounts`, `useConnectionStatus`, `useIsHydrated`, `useIsReconnecting`, `useDiscoveredWallets`, `useConnectedWallets`, the `…ByPlatform` groupings, `useBalance` and `useSigner`.
|
|
15
|
+
- `manager.connect()` and `useConnect().connectAsync()` resolve the `ConnectedWallet` and reject with a `ConnectionError`, now an `Error` subclass with a `kind` and a new `WalletNotFound` kind. `useConnect().connect()` never rejects: the failure lands in its `error`.
|
|
16
|
+
- `WalletPersistence` is `load()` / `save(state)`, and `createWalletStorage` replaces `new WalletStorage`. Storage keys are unchanged.
|
|
17
|
+
- Chain registries (`EVM_CHAINS`, `SVM_CHAINS`, …, `CHAINS_BY_PLATFORM`) move to `@usebutr/core`, and `BITCOIN_CHAINS` gains `testnet4`. `@usebutr/wallets` loads every platform's signer kinds, so an app that imports only it can narrow `signer.kind`.
|
|
18
|
+
- `@usebutr/react` requires React 19, which its `use()` call already needed.
|
|
19
|
+
- WalletConnect requests on Solana, Sui and Bitcoin now carry their chain, so they no longer fall through to the first namespace in a multi-namespace session. Solana takes `SVM_CHAINS` chains and maps them to the genesis-hash ids WalletConnect sessions use.
|
|
20
|
+
- Ledger's Solana `signTransaction` returns the full signed transaction, Sui signs the intent message, and Bitcoin `signMessage` returns a BIP-137 signature.
|
|
21
|
+
- `@usebutr/testing`'s fake adapter resolves a `signer` you pass instead of registering a test-only signer kind.
|
|
22
|
+
- `setAccount` selects an exposed account without changing any account's chain. Unknown accounts are ignored; chain changes come from the adapter.
|
|
23
|
+
- WalletConnect EVM events use the namespace in `session_event`, including empty account lists that end the connection. Local chain switches still update the manager.
|
|
24
|
+
- Failed persistence saves wait for every key write to finish before reporting failure, so delayed writes cannot overwrite a later disconnect.
|
|
25
|
+
- `@usebutr/svm/transaction` provides the shared legacy/v0 signing codec used by Ledger and WalletConnect, with consistent layout and signature validation.
|
|
26
|
+
|
|
27
|
+
See the migration guide at https://docs.usebutr.com/migration.
|
|
28
|
+
|
|
29
|
+
### Patch Changes
|
|
30
|
+
|
|
31
|
+
- Updated dependencies [[`1263f86`](https://github.com/pedroapfilho/usebutr/commit/1263f8666afd43b4f7dbf8df1763709c3edf09d9), [`5d1f234`](https://github.com/pedroapfilho/usebutr/commit/5d1f23403263f7a1f8a48be061727972fe749b5f)]:
|
|
32
|
+
- @usebutr/core@3.0.0
|
|
33
|
+
|
|
34
|
+
## 1.1.1
|
|
35
|
+
|
|
36
|
+
### Patch Changes
|
|
37
|
+
|
|
38
|
+
- [#179](https://github.com/pedroapfilho/usebutr/pull/179) [`bc5b58c`](https://github.com/pedroapfilho/usebutr/commit/bc5b58cd87601be38dbb77a14e16eaaa36e8f012) Thanks [@pedroapfilho](https://github.com/pedroapfilho)! - Include package READMEs, MIT licenses, and documentation and issue links on npm.
|
|
39
|
+
- Updated dependencies [[`bc5b58c`](https://github.com/pedroapfilho/usebutr/commit/bc5b58cd87601be38dbb77a14e16eaaa36e8f012)]:
|
|
40
|
+
- @usebutr/core@2.0.1
|
|
41
|
+
|
|
42
|
+
## 1.1.0
|
|
43
|
+
|
|
44
|
+
### Minor Changes
|
|
45
|
+
|
|
46
|
+
- 9b1caa2: Correctness pass across the storage, hydration and connector layers. Several of
|
|
47
|
+
these change observable behaviour; the ones worth knowing about before upgrading:
|
|
48
|
+
|
|
49
|
+
**Breaking**
|
|
50
|
+
|
|
51
|
+
- `SuiWallet.signTransaction` now returns `{ bytes, signature }` instead of
|
|
52
|
+
`Uint8Array`. Sui's `executeTransactionBlock` needs both, and each connector
|
|
53
|
+
previously returned a different half, so the old value could not be broadcast
|
|
54
|
+
and a consumer could not tell which half they held. SVM and Bitcoin keep their
|
|
55
|
+
single-`Uint8Array` shape.
|
|
56
|
+
- `@usebutr/svm`'s `sendTx` returns the signature base58-encoded, matching the
|
|
57
|
+
WalletConnect namespace, the Ledger app, Solana explorers and
|
|
58
|
+
`getSignatureStatuses`. It previously returned base64.
|
|
59
|
+
- `@usebutr/sui`'s feature input types drop the `string` arm from
|
|
60
|
+
`transaction`; wallets only ever accepted the `toJSON()` form. Strings and BCS
|
|
61
|
+
bytes are now wrapped for you, so `sendTx` accepts strictly more than before.
|
|
62
|
+
|
|
63
|
+
**Behaviour fixes**
|
|
64
|
+
|
|
65
|
+
- `WalletStorage.setPool` and `removePoolEntry` could self-deadlock on a corrupt
|
|
66
|
+
pool payload, leaving `connectWallet` unresolved and every later pool write
|
|
67
|
+
jammed for the page's life. Both are fixed, and `readWalletSnapshot` now shares
|
|
68
|
+
one codec with `WalletStorage` so the server and client decodes cannot drift.
|
|
69
|
+
- A seeded (SSR) entry whose silent reconnect failed used to stay in the pool
|
|
70
|
+
backed by a placeholder connector forever, so `useSigner`/`useBalance`
|
|
71
|
+
reported an error and `useConnectionStatus` reported `"reconnecting"`
|
|
72
|
+
permanently. Those entries are now dropped.
|
|
73
|
+
- `useSigner` and `useBalance` stay `idle` for a wallet that is still
|
|
74
|
+
reconnecting instead of surfacing a placeholder rejection as `status: "error"`.
|
|
75
|
+
- `useConnectionStatus` lets a live `"connecting"`/`"error"` take precedence over
|
|
76
|
+
the derived `"reconnecting"`, so an in-flight attempt and a connection error
|
|
77
|
+
are no longer hidden. New `useIsReconnecting(connectorId?)` answers the
|
|
78
|
+
per-wallet question directly.
|
|
79
|
+
- A background restore no longer writes the connect-attempt status or steals
|
|
80
|
+
`activeConnectorId`, and a superseded `CONNECT_FAILED` no longer overwrites the
|
|
81
|
+
current attempt.
|
|
82
|
+
- An externally-disconnected connector is now torn down, not just dropped from
|
|
83
|
+
the pool, so a cached adapter is not reused while still holding a session.
|
|
84
|
+
- EVM: a malformed provider response is treated as absent rather than `""`, which
|
|
85
|
+
previously produced chain `eip155:0`, zero-length signatures, empty transaction
|
|
86
|
+
hashes and a confident `0 ETH`. `switchChain` gained the same-chain
|
|
87
|
+
short-circuit `sendTxToChain` already had.
|
|
88
|
+
- WalletConnect: accounts carry their own chain instead of all being stamped with
|
|
89
|
+
the active one, so a multi-chain session no longer returns a wrong-chain
|
|
90
|
+
address. One pairing now covers every configured namespace, the pairing URI
|
|
91
|
+
survives a reconnect, and concurrent connects share one pairing.
|
|
92
|
+
- Bitcoin: the sats-connect (Xverse) adapter honours `silent`, so a reload no
|
|
93
|
+
longer triggers an unsolicited approval prompt, and exposes the payment address
|
|
94
|
+
as its single account instead of also presenting the ordinals address as
|
|
95
|
+
spendable.
|
|
96
|
+
- Polkadot: the injected adapter actually delivers events to subscribers,
|
|
97
|
+
`switchChain` notifies like its Wallet Standard sibling, and chain resolution
|
|
98
|
+
prefers the mainnets rather than whatever the wallet listed first.
|
|
99
|
+
- Ledger: the device session is committed atomically, so a locked device no
|
|
100
|
+
longer leaves an open transport that reports itself connected, and concurrent
|
|
101
|
+
connects open one transport.
|
|
102
|
+
- SVM: `sendTxToChain` submits to the chain you asked for instead of the
|
|
103
|
+
adapter's current one, and rejects a chain the wallet does not advertise.
|
|
104
|
+
- `@usebutr/svm`'s `switchChain` capability now counts only `solana:` chains, so
|
|
105
|
+
a multi-namespace wallet no longer advertises a method that always throws.
|
|
106
|
+
|
|
107
|
+
**Testing**
|
|
108
|
+
|
|
109
|
+
- `createFakePersistence` is now the real `WalletStorage` over memory drivers
|
|
110
|
+
rather than a parallel implementation, so it inherits upsert semantics,
|
|
111
|
+
validation and JSON round-tripping. Two divergences are fixed as a result:
|
|
112
|
+
`setPool` upserts rather than replaces, and `clearAll` leaves the
|
|
113
|
+
user-disconnected flag alone.
|
|
114
|
+
- `createFakeConnectedWallet` rejects being given both an `adapter` and explicit
|
|
115
|
+
`addresses`/`accounts`, a combination whose halves could disagree.
|
|
116
|
+
|
|
117
|
+
### Patch Changes
|
|
118
|
+
|
|
119
|
+
- Updated dependencies [9b1caa2]
|
|
120
|
+
- @usebutr/core@2.0.0
|
|
121
|
+
|
|
122
|
+
## 1.0.1
|
|
123
|
+
|
|
124
|
+
### Patch Changes
|
|
125
|
+
|
|
126
|
+
- Updated dependencies [8ecaf89]
|
|
127
|
+
- @usebutr/core@1.1.0
|
|
128
|
+
|
|
129
|
+
## 1.0.0
|
|
130
|
+
|
|
131
|
+
### Major Changes
|
|
132
|
+
|
|
133
|
+
- f0a5116: **Breaking:** the `platform` field is gone from the `PlatformDiscoverer` type in `@usebutr/core`, and from the `evmDiscoverer`, `svmDiscoverer`, `suiDiscoverer`, `bitcoinDiscoverer` and `polkadotDiscoverer` objects that implement it. Nothing read it: the aggregator keys discoverers by `ChainPlatform` in its own registry, so the field only restated the key.
|
|
134
|
+
|
|
135
|
+
Migration: read the platform from the `KNOWN_DISCOVERERS` key in `@usebutr/wallets` (`Object.entries(KNOWN_DISCOVERERS)`), or from `adapter.chainPlatform` on a discovered adapter. Custom `PlatformDiscoverer` implementations must drop the `platform` property; keeping it is now an excess-property error.
|
|
136
|
+
|
|
137
|
+
### Patch Changes
|
|
138
|
+
|
|
139
|
+
- Updated dependencies [7887cf0]
|
|
140
|
+
- Updated dependencies [7887cf0]
|
|
141
|
+
- Updated dependencies [f0a5116]
|
|
142
|
+
- Updated dependencies [f0a5116]
|
|
143
|
+
- @usebutr/core@1.0.0
|
|
144
|
+
|
|
145
|
+
## 0.2.5
|
|
146
|
+
|
|
147
|
+
### Patch Changes
|
|
148
|
+
|
|
149
|
+
- Updated dependencies [4467a5e]
|
|
150
|
+
- @usebutr/core@0.5.0
|
|
151
|
+
|
|
152
|
+
## 0.2.4
|
|
153
|
+
|
|
154
|
+
### Patch Changes
|
|
155
|
+
|
|
156
|
+
- Updated dependencies [c1309ee]
|
|
157
|
+
- @usebutr/core@0.4.2
|
|
158
|
+
|
|
159
|
+
## 0.2.3
|
|
160
|
+
|
|
161
|
+
### Patch Changes
|
|
162
|
+
|
|
163
|
+
- 937dfae: Bump runtime dependency floors (`@wallet-standard/app` 1.1.1, `@ledgerhq/*` latest minors, `@walletconnect/universal-provider` 2.23.10) and modernize public type declarations from method signatures to property function types (oxlint `method-signature-style`). Type-level only — no runtime behavior change.
|
|
164
|
+
- Updated dependencies [937dfae]
|
|
165
|
+
- @usebutr/core@0.4.1
|
|
166
|
+
|
|
167
|
+
## 0.2.2
|
|
168
|
+
|
|
169
|
+
### Patch Changes
|
|
170
|
+
|
|
171
|
+
- a46eecd: Ship unminified ESM so downstream bundlers (Vite/esbuild dep pre-bundling) process the package correctly; fixes a ReferenceError in consumer dev servers. The consuming app minifies once at its own build.
|
|
172
|
+
- Updated dependencies [b5322ae]
|
|
173
|
+
- Updated dependencies [d5f32c7]
|
|
174
|
+
- Updated dependencies [a46eecd]
|
|
175
|
+
- @usebutr/core@0.4.0
|
|
176
|
+
|
|
177
|
+
## 0.2.1
|
|
178
|
+
|
|
179
|
+
### Patch Changes
|
|
180
|
+
|
|
181
|
+
- Updated dependencies [886ee1d]
|
|
182
|
+
- @usebutr/core@0.3.0
|
|
183
|
+
|
|
184
|
+
## 0.2.0
|
|
185
|
+
|
|
186
|
+
### Minor Changes
|
|
187
|
+
|
|
188
|
+
- db5d7e9: The EIP-6963 adapter's `getBalance` now reads ERC20 balances. Called with no argument it returns the native ETH balance as before; called with a token address (`getBalance(tokenAddress)`) it reads `balanceOf` / `decimals` / `symbol` for the active account on the currently-connected chain via parallel `eth_call`s on the wallet's own provider (no separate RPC needed). Non-standard `bytes32` symbols are decoded best-effort.
|
|
189
|
+
|
|
190
|
+
### Patch Changes
|
|
191
|
+
|
|
192
|
+
- Updated dependencies [db5d7e9]
|
|
193
|
+
- @usebutr/core@0.2.2
|
|
194
|
+
|
|
195
|
+
## 0.1.2
|
|
196
|
+
|
|
197
|
+
### Patch Changes
|
|
198
|
+
|
|
199
|
+
- f846e77: Wallet-announced icons are trimmed of surrounding whitespace on ingestion. Some wallets ship data-URI icons with a leading newline, which strict consumers reject — Next.js's `<Image>` throws because `src` must not start with a control character. `@usebutr/core` exports a `sanitizeIcon` helper; the EIP-6963 and Wallet Standard adapters apply it, and an all-whitespace icon now resolves to `undefined` rather than a blank string.
|
|
200
|
+
- Updated dependencies [f846e77]
|
|
201
|
+
- @usebutr/core@0.2.1
|
|
202
|
+
|
|
203
|
+
## 0.1.1
|
|
204
|
+
|
|
205
|
+
### Patch Changes
|
|
206
|
+
|
|
207
|
+
- Updated dependencies [b77a477]
|
|
208
|
+
- @usebutr/core@0.2.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pedro Filho
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# @usebutr/evm
|
|
2
|
+
|
|
3
|
+
EIP-1193 / EIP-6963 / injected wallet discovery + adapters for butr.
|
|
4
|
+
|
|
5
|
+
Part of [butr](https://www.usebutr.com), a multi-chain wallet discovery and
|
|
6
|
+
connection-state library. Your application owns the picker UI and chain client.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @usebutr/react @usebutr/core @usebutr/evm zustand
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
React 19 is a peer dependency of the provider. Discovery does not render a connect modal; add your picker as the provider child. `discoverEvmAdapters` (EIP-6963) and `discoverInjectedAdapter` (the legacy `window.ethereum` fallback) are wallet sources as-is; `autoDiscovery()` from `@usebutr/wallets` combines both with the other platforms.
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
import type { ReactNode } from "react";
|
|
20
|
+
import type { WalletManagerConfig } from "@usebutr/core";
|
|
21
|
+
import { discoverEvmAdapters } from "@usebutr/evm";
|
|
22
|
+
import { WalletManagerProvider } from "@usebutr/react";
|
|
23
|
+
|
|
24
|
+
const config: WalletManagerConfig = { sources: [discoverEvmAdapters] };
|
|
25
|
+
|
|
26
|
+
export const WalletProvider = ({ children }: { children: ReactNode }) => (
|
|
27
|
+
<WalletManagerProvider config={config}>{children}</WalletManagerProvider>
|
|
28
|
+
);
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Every EVM adapter defines `requestAccounts`, `switchChain`, `signMessage`, `sendTx`, `getBalance`, `getTransactionReceipt` and `subscribe`. `getSigner()` resolves `{ kind: "eip1193", provider }` for your own viem or ethers client. Chain registries (`EVM_CHAINS`, `EVM_CHAINS_LIST`) live in `@usebutr/core`.
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import type { ConnectedWallet } from "@usebutr/core";
|
|
35
|
+
import { EVM_CHAINS } from "@usebutr/core";
|
|
36
|
+
|
|
37
|
+
// `sendTx` switches the wallet to `chain` first when it is elsewhere, and
|
|
38
|
+
// sends from `account`, which must be one the wallet exposes.
|
|
39
|
+
export const pay = (wallet: ConnectedWallet<"evm">, to: string) =>
|
|
40
|
+
wallet.connector.sendTx?.(
|
|
41
|
+
{ to, value: 10_000_000_000_000_000n },
|
|
42
|
+
{ account: wallet.account, chain: EVM_CHAINS.base },
|
|
43
|
+
);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Documentation
|
|
47
|
+
|
|
48
|
+
- [Package reference](https://docs.usebutr.com/api/evm)
|
|
49
|
+
- [Getting started](https://docs.usebutr.com/getting-started/quickstart)
|
|
50
|
+
- [Examples and source](https://github.com/pedroapfilho/usebutr)
|
|
51
|
+
- [Report an issue](https://github.com/pedroapfilho/usebutr/issues)
|
|
52
|
+
|
|
53
|
+
## License
|
|
54
|
+
|
|
55
|
+
[MIT](./LICENSE), copyright 2026 Pedro Filho.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EvmAdapter, PlatformDiscoverer } from "@usebutr/core";
|
|
2
2
|
//#region src/eip1193.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* Declared inline rather than taken from `viem`, which would expand butr's
|
|
5
5
|
* footprint. Loose on purpose: `request(...)` returns `unknown` and the
|
|
6
6
|
* `request*` helpers narrow it at the boundary.
|
|
7
7
|
*/
|
|
8
|
+
type Eip1193Object = {
|
|
9
|
+
[key: string]: Eip1193Value | undefined;
|
|
10
|
+
};
|
|
11
|
+
type Eip1193Value = bigint | boolean | number | string | null | Uint8Array | ReadonlyArray<Eip1193Value> | Eip1193Object;
|
|
8
12
|
type Eip1193RequestArgs = {
|
|
9
13
|
method: string;
|
|
10
|
-
params?: ReadonlyArray<
|
|
14
|
+
params?: ReadonlyArray<Eip1193Value> | Eip1193Object;
|
|
11
15
|
};
|
|
12
|
-
type Eip1193Listener = (...args: ReadonlyArray<
|
|
16
|
+
type Eip1193Listener = (...args: ReadonlyArray<Eip1193Value | undefined>) => void;
|
|
13
17
|
type Eip1193Provider = {
|
|
14
18
|
on: (event: string, listener: Eip1193Listener) => void;
|
|
15
19
|
removeListener: (event: string, listener: Eip1193Listener) => void;
|
|
16
|
-
request: (args: Eip1193RequestArgs) => Promise<
|
|
20
|
+
request: (args: Eip1193RequestArgs) => Promise<Eip1193Value | undefined>;
|
|
17
21
|
};
|
|
18
22
|
type Eip6963ProviderInfo = {
|
|
19
23
|
/** Data URI or absolute URL pointing at the wallet's icon. */
|
|
@@ -35,7 +39,7 @@ type Eip6963AnnounceEvent = CustomEvent<Eip6963ProviderDetail>;
|
|
|
35
39
|
//#region src/eip6963.d.ts
|
|
36
40
|
declare const ANNOUNCE_EVENT = "eip6963:announceProvider";
|
|
37
41
|
declare const REQUEST_EVENT = "eip6963:requestProvider";
|
|
38
|
-
type AdapterCallback = (adapter:
|
|
42
|
+
type AdapterCallback = (adapter: EvmAdapter, info: Eip6963ProviderInfo) => void;
|
|
39
43
|
type DiscoverOptions = {
|
|
40
44
|
/** Event target to listen on. Defaults to `window` in browser/RN/
|
|
41
45
|
* SSR'd environments; tests can pass a fresh `new EventTarget()` to
|
|
@@ -60,7 +64,7 @@ declare const chainIdDecimalToHex: (dec: string) => string;
|
|
|
60
64
|
* implement and silently ignore, so their own auto-reconnect may outlive it.
|
|
61
65
|
* `getBalance()` labels the native balance `"ETH"` on every EVM chain.
|
|
62
66
|
*/
|
|
63
|
-
declare const buildEvmAdapter: (info: Eip6963ProviderInfo, provider: Eip1193Provider) =>
|
|
67
|
+
declare const buildEvmAdapter: (info: Eip6963ProviderInfo, provider: Eip1193Provider) => EvmAdapter;
|
|
64
68
|
//#endregion
|
|
65
69
|
//#region src/injected.d.ts
|
|
66
70
|
/**
|
|
@@ -83,74 +87,24 @@ type InjectedDiscoveryOptions = {
|
|
|
83
87
|
settleMs?: number;
|
|
84
88
|
/** Override the global `window` reference (tests, iframes). */
|
|
85
89
|
target?: {
|
|
86
|
-
ethereum?:
|
|
90
|
+
ethereum?: Partial<Eip1193Provider>;
|
|
87
91
|
} | null;
|
|
88
92
|
};
|
|
93
|
+
declare global {
|
|
94
|
+
interface Window {
|
|
95
|
+
ethereum?: Partial<Eip1193Provider>;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/** Structural check for code that holds a provider of unknown origin, such
|
|
99
|
+
* as `window.ethereum`. A butr signer narrows on `kind === "eip1193"`
|
|
100
|
+
* instead. */
|
|
101
|
+
declare const isEip1193Provider: (value: unknown) => value is Eip1193Provider;
|
|
89
102
|
/**
|
|
90
103
|
* Last-resort path for the long tail of EVM wallets that expose only
|
|
91
104
|
* `window.ethereum` and never announce via EIP-6963. The emitted adapter
|
|
92
105
|
* carries `rdns: "injected:legacy"` so consumers can tell it apart.
|
|
93
106
|
*/
|
|
94
|
-
declare const discoverInjectedAdapter: (onAdapter: (adapter:
|
|
95
|
-
//#endregion
|
|
96
|
-
//#region src/chains.d.ts
|
|
97
|
-
/**
|
|
98
|
-
* Common EVM chain registry keyed by CAIP-2 id. Useful for chain-
|
|
99
|
-
* switcher UIs. butr itself doesn't read this: `Connector.switchChain`
|
|
100
|
-
* accepts any `ChainBase`.
|
|
101
|
-
*/
|
|
102
|
-
declare const EVM_CHAINS: {
|
|
103
|
-
readonly arbitrum: {
|
|
104
|
-
readonly id: "eip155:42161";
|
|
105
|
-
readonly name: "Arbitrum One";
|
|
106
|
-
readonly namespace: "eip155";
|
|
107
|
-
readonly reference: "42161";
|
|
108
|
-
};
|
|
109
|
-
readonly base: {
|
|
110
|
-
readonly id: "eip155:8453";
|
|
111
|
-
readonly name: "Base";
|
|
112
|
-
readonly namespace: "eip155";
|
|
113
|
-
readonly reference: "8453";
|
|
114
|
-
};
|
|
115
|
-
readonly bsc: {
|
|
116
|
-
readonly id: "eip155:56";
|
|
117
|
-
readonly name: "BNB Smart Chain";
|
|
118
|
-
readonly namespace: "eip155";
|
|
119
|
-
readonly reference: "56";
|
|
120
|
-
};
|
|
121
|
-
readonly ethereum: {
|
|
122
|
-
readonly id: "eip155:1";
|
|
123
|
-
readonly name: "Ethereum";
|
|
124
|
-
readonly namespace: "eip155";
|
|
125
|
-
readonly reference: "1";
|
|
126
|
-
};
|
|
127
|
-
readonly optimism: {
|
|
128
|
-
readonly id: "eip155:10";
|
|
129
|
-
readonly name: "Optimism";
|
|
130
|
-
readonly namespace: "eip155";
|
|
131
|
-
readonly reference: "10";
|
|
132
|
-
};
|
|
133
|
-
readonly polygon: {
|
|
134
|
-
readonly id: "eip155:137";
|
|
135
|
-
readonly name: "Polygon";
|
|
136
|
-
readonly namespace: "eip155";
|
|
137
|
-
readonly reference: "137";
|
|
138
|
-
};
|
|
139
|
-
readonly sepolia: {
|
|
140
|
-
readonly id: "eip155:11155111";
|
|
141
|
-
readonly name: "Sepolia";
|
|
142
|
-
readonly namespace: "eip155";
|
|
143
|
-
readonly reference: "11155111";
|
|
144
|
-
};
|
|
145
|
-
};
|
|
146
|
-
declare const EVM_CHAINS_LIST: ReadonlyArray<ChainBase>;
|
|
147
|
-
//#endregion
|
|
148
|
-
//#region src/capabilities.d.ts
|
|
149
|
-
declare const EIP6963_RDNS_WITH_REQUEST_ACCOUNTS: Set<string>;
|
|
150
|
-
type Eip6963CapabilityInput = {
|
|
151
|
-
rdns: string;
|
|
152
|
-
};
|
|
153
|
-
declare const resolveEip6963Capabilities: (input: Eip6963CapabilityInput) => WalletCapabilities;
|
|
107
|
+
declare const discoverInjectedAdapter: (onAdapter: (adapter: EvmAdapter) => void, options?: InjectedDiscoveryOptions) => (() => void);
|
|
154
108
|
//#endregion
|
|
155
109
|
//#region src/discoverer.d.ts
|
|
156
110
|
/**
|
|
@@ -160,16 +114,15 @@ declare const resolveEip6963Capabilities: (input: Eip6963CapabilityInput) => Wal
|
|
|
160
114
|
declare const evmDiscoverer: PlatformDiscoverer;
|
|
161
115
|
//#endregion
|
|
162
116
|
//#region src/signer-augmentation.d.ts
|
|
163
|
-
/**
|
|
164
|
-
*
|
|
165
|
-
* `packages/core/src/types/signer.ts` for why); this only changes what
|
|
166
|
-
* `SignerForPlatform["evm"]` evaluates to.
|
|
167
|
-
*/
|
|
117
|
+
/** Every EVM adapter built on an EIP-1193 provider (injected, EIP-6963,
|
|
118
|
+
* WalletConnect's EVM namespace) hands back that provider. */
|
|
168
119
|
declare module "@usebutr/core" {
|
|
169
|
-
interface
|
|
170
|
-
|
|
120
|
+
interface WalletSignerRegistry {
|
|
121
|
+
eip1193: {
|
|
122
|
+
provider: Eip1193Provider;
|
|
123
|
+
};
|
|
171
124
|
}
|
|
172
125
|
}
|
|
173
126
|
//#endregion
|
|
174
|
-
export { ANNOUNCE_EVENT,
|
|
127
|
+
export { ANNOUNCE_EVENT, type Eip1193Listener, type Eip1193Object, type Eip1193Provider, type Eip1193RequestArgs, type Eip1193Value, type Eip6963AnnounceEvent, type Eip6963ProviderDetail, type Eip6963ProviderInfo, GENERIC_INJECTED_ICON, type InjectedDiscoveryOptions, REQUEST_EVENT, buildEvmAdapter, chainIdDecimalToHex, chainIdHexToDecimal, discoverEvmAdapters, discoverInjectedAdapter, evmDiscoverer, formatEther, isEip1193Provider };
|
|
175
128
|
//# sourceMappingURL=index.d.ts.map
|
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/evm-balance.ts","../src/eip6963-adapter.ts","../src/injected.ts","../src/
|
|
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/discoverer.ts","../src/signer-augmentation.ts"],"mappings":";;;;;;;KAMK;GAAmB,cAAc;;KACjC,2DAMD,aACA,cAAc,gBACd;KAEC;EACH;EACA,SAAS,cAAc,gBAAgB;;KAGpC,sBAAsB,MAAM,cAAc;KAE1C;EACH,KAAK,eAAe,UAAU;EAC9B,iBAAiB,eAAe,UAAU;EAC1C,UAAU,MAAM,uBAAuB,QAAQ;;KAG5C;;EAEH;;EAEA;;;EAGA;;EAEA;;KAGG;EACH,MAAM;EACN,UAAU;;KAGP,uBAAuB,YAAY;;;cC1ClC;cACA;KAED,mBAAmB,SAAS,YAAY,MAAM;KAE9C;;;;EAIH,SAAS;;;;;;;cAmDL,sBAAmB,WACZ,iBAAe,UACjB;;;cClCL,cAAW;;;cCUX,sBAAmB;cACnB,sBAAmB;;;;;;cAyCnB,kBAAe,MAAU,qBAAmB,UAAY,oBAAkB;;;;;;;;cC3E1E;KAKD;;;;;;EAMH;;;;;EAKA;;EAEA;IAAW,WAAW,QAAQ;;;QAGxB;YAEI;IACR,WAAW,QAAQ;;;;;;cAOjB,oBAAiB,mBAAqB,SAAS;;;;;;cAoB/C,0BAAuB,YACf,SAAS,qBAAmB,UAC/B;;;;;;;cCtDL,eAAe;;;;;;YCHT;IACR;MAAW,UAAU"}
|
package/dist/index.js
CHANGED
|
@@ -1,21 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EVM_CHAINS_LIST, buildAccount, bytesToHexPrefixed, hexToBytes, resolveChain, sanitizeIcon } from "@usebutr/core";
|
|
2
2
|
|
|
3
|
-
//#region src/capabilities.ts
|
|
4
|
-
const EIP6963_RDNS_WITH_REQUEST_ACCOUNTS = /* @__PURE__ */ new Set(["io.metamask"]);
|
|
5
|
-
const resolveEip6963Capabilities = (input) => ({
|
|
6
|
-
getBalance: true,
|
|
7
|
-
getTransactionReceipt: true,
|
|
8
|
-
requestAccounts: EIP6963_RDNS_WITH_REQUEST_ACCOUNTS.has(input.rdns),
|
|
9
|
-
sendTransaction: true,
|
|
10
|
-
signIn: false,
|
|
11
|
-
signMessage: true,
|
|
12
|
-
signTransaction: false,
|
|
13
|
-
subscribe: true,
|
|
14
|
-
switchAccount: false,
|
|
15
|
-
switchChain: true
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
//#endregion
|
|
19
3
|
//#region src/eip1193.ts
|
|
20
4
|
/** `provider.request` is typed `unknown` by design; these helpers narrow
|
|
21
5
|
* the well-known EVM RPC shapes at the boundary with runtime guards so
|
|
@@ -33,7 +17,7 @@ const requestStringArray = async (provider, args) => {
|
|
|
33
17
|
|
|
34
18
|
//#endregion
|
|
35
19
|
//#region src/evm-balance.ts
|
|
36
|
-
const HEX_PREFIX
|
|
20
|
+
const HEX_PREFIX = "0x";
|
|
37
21
|
const ETH_DECIMALS = 18n;
|
|
38
22
|
const ERC20_BALANCE_OF_SELECTOR = "0x70a08231";
|
|
39
23
|
const ERC20_DECIMALS_SELECTOR = "0x313ce567";
|
|
@@ -54,14 +38,14 @@ const formatUnits = (raw, decimals) => {
|
|
|
54
38
|
const formatEther = (wei) => formatUnits(wei, Number(ETH_DECIMALS));
|
|
55
39
|
const padAddressForCall = (address) => address.replace(new RegExp("^0x", "v"), "").toLowerCase().padStart(64, "0");
|
|
56
40
|
const decodeAbiString = (hex) => {
|
|
57
|
-
const clean = hex.startsWith(HEX_PREFIX
|
|
41
|
+
const clean = hex.startsWith(HEX_PREFIX) ? hex.slice(2) : hex;
|
|
58
42
|
if (clean.length === 0) return "";
|
|
59
43
|
if (clean.length >= 128) {
|
|
60
44
|
const lengthHex = clean.slice(64, 128);
|
|
61
45
|
const length = Number(BigInt(`0x${lengthHex}`));
|
|
62
|
-
if (length > 0 && clean.length >= 128 + length * 2) return new TextDecoder().decode(hexToBytes
|
|
46
|
+
if (length > 0 && clean.length >= 128 + length * 2) return new TextDecoder().decode(hexToBytes(clean.slice(128, 128 + length * 2)));
|
|
63
47
|
}
|
|
64
|
-
const bytes = hexToBytes
|
|
48
|
+
const bytes = hexToBytes(clean.slice(0, 64));
|
|
65
49
|
let end = bytes.length;
|
|
66
50
|
while (end > 0 && bytes[end - 1] === 0) end -= 1;
|
|
67
51
|
return new TextDecoder().decode(bytes.subarray(0, end));
|
|
@@ -115,58 +99,73 @@ const readEvmBalance = async (provider, address, tokenAddress) => {
|
|
|
115
99
|
|
|
116
100
|
//#endregion
|
|
117
101
|
//#region src/eip6963-adapter.ts
|
|
118
|
-
const
|
|
102
|
+
const EVM_NAMESPACE = "eip155";
|
|
119
103
|
const toStringArray = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
120
|
-
/**
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const chainIdDecimalToHex = (dec) => `${HEX_PREFIX}${BigInt(dec).toString(16)}`;
|
|
129
|
-
const buildEvmChain = (chainIdHex, walletName) => {
|
|
130
|
-
const reference = chainIdHexToDecimal(chainIdHex);
|
|
131
|
-
return {
|
|
132
|
-
id: `eip155:${reference}`,
|
|
133
|
-
name: walletName,
|
|
134
|
-
namespace: "eip155",
|
|
135
|
-
reference
|
|
136
|
-
};
|
|
104
|
+
/** JSON-RPC cannot carry a `bigint`, so quantities go out as `0x` hex. */
|
|
105
|
+
const encodeValue = (value) => {
|
|
106
|
+
if (typeof value === "bigint") return `0x${value.toString(16)}`;
|
|
107
|
+
if (Array.isArray(value)) return value.map(encodeValue);
|
|
108
|
+
if (typeof value !== "object" || value === null) return value;
|
|
109
|
+
const result = {};
|
|
110
|
+
for (const [key, nested] of Object.entries(value)) result[key] = nested === void 0 ? void 0 : encodeValue(nested);
|
|
111
|
+
return result;
|
|
137
112
|
};
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const
|
|
148
|
-
if (accounts.length === 0) return null;
|
|
149
|
-
const chainIdHex = options.chainIdHex ?? await requestString(provider, { method: "eth_chainId" });
|
|
113
|
+
const chainIdHexToDecimal = (hex) => BigInt(hex).toString(10);
|
|
114
|
+
const chainIdDecimalToHex = (dec) => `0x${BigInt(dec).toString(16)}`;
|
|
115
|
+
const evmChainFromHex = (chainIdHex) => resolveChain(`${EVM_NAMESPACE}:${chainIdHexToDecimal(chainIdHex)}`, EVM_CHAINS_LIST);
|
|
116
|
+
/** Rejections propagate: `getAccounts` surfaces the wallet's own provider
|
|
117
|
+
* error, while the `subscribe` bridge swallows it because a later event
|
|
118
|
+
* retries synchronization. */
|
|
119
|
+
const readAccounts = async (provider, known = {}) => {
|
|
120
|
+
const addresses = known.addresses ?? await requestStringArray(provider, { method: "eth_accounts" });
|
|
121
|
+
if (addresses.length === 0) return [];
|
|
122
|
+
const chainIdHex = known.chainIdHex ?? await requestString(provider, { method: "eth_chainId" });
|
|
150
123
|
if (chainIdHex === null) throw new Error("Wallet returned a malformed eth_chainId response");
|
|
151
|
-
const chain =
|
|
152
|
-
|
|
153
|
-
const account = builtAccounts[0];
|
|
154
|
-
return account === void 0 ? null : {
|
|
155
|
-
account,
|
|
156
|
-
accounts: builtAccounts
|
|
157
|
-
};
|
|
124
|
+
const chain = evmChainFromHex(chainIdHex);
|
|
125
|
+
return addresses.map((address) => buildAccount(address, chain));
|
|
158
126
|
};
|
|
127
|
+
/** EIP-1474 / JSON-RPC codes wallets use for an unimplemented method.
|
|
128
|
+
* Coinbase wraps its own -32604 inside a -32603 `data.originalError`. */
|
|
129
|
+
const METHOD_NOT_SUPPORTED_CODES = /* @__PURE__ */ new Set([
|
|
130
|
+
4200,
|
|
131
|
+
-32601,
|
|
132
|
+
-32603,
|
|
133
|
+
-32604
|
|
134
|
+
]);
|
|
159
135
|
/**
|
|
160
136
|
* `disconnect` calls `wallet_revokePermissions`, which many wallets don't
|
|
161
137
|
* implement and silently ignore, so their own auto-reconnect may outlive it.
|
|
162
138
|
* `getBalance()` labels the native balance `"ETH"` on every EVM chain.
|
|
163
139
|
*/
|
|
164
140
|
const buildEvmAdapter = (info, provider) => {
|
|
141
|
+
/** The address to act as, in the wallet's own casing. Hex addresses are
|
|
142
|
+
* case-insensitive (EIP-55 casing is only a checksum), so they compare
|
|
143
|
+
* lowercased. */
|
|
144
|
+
const resolveAddress = async (account) => {
|
|
145
|
+
const exposed = await requestStringArray(provider, { method: "eth_accounts" });
|
|
146
|
+
if (account === void 0) {
|
|
147
|
+
const active = exposed.at(0);
|
|
148
|
+
if (active === void 0) throw new Error(`Wallet ${info.name} has no connected account`);
|
|
149
|
+
return active;
|
|
150
|
+
}
|
|
151
|
+
const wanted = account.walletAddress.toLowerCase();
|
|
152
|
+
const match = exposed.find((address) => address.toLowerCase() === wanted);
|
|
153
|
+
if (match === void 0) throw new Error(`Wallet ${info.name} does not expose account ${account.walletAddress}`);
|
|
154
|
+
return match;
|
|
155
|
+
};
|
|
156
|
+
const switchChain = async (chain) => {
|
|
157
|
+
if (chain.namespace !== EVM_NAMESPACE || !new RegExp("^\\d+$", "v").test(chain.reference)) throw new Error(`EVM adapter received non-EVM chain "${chain.id}". Pass a chain with namespace "${EVM_NAMESPACE}" and a numeric reference.`);
|
|
158
|
+
const target = chainIdDecimalToHex(chain.reference);
|
|
159
|
+
if ((await requestString(provider, { method: "eth_chainId" }))?.toLowerCase() === target) return;
|
|
160
|
+
await provider.request({
|
|
161
|
+
method: "wallet_switchEthereumChain",
|
|
162
|
+
params: [{ chainId: target }]
|
|
163
|
+
});
|
|
164
|
+
};
|
|
165
165
|
return {
|
|
166
|
-
capabilities: resolveEip6963Capabilities({ rdns: info.rdns }),
|
|
167
166
|
chainPlatform: "evm",
|
|
168
|
-
async connect(
|
|
169
|
-
if (
|
|
167
|
+
async connect(options) {
|
|
168
|
+
if (options?.silent === true) {
|
|
170
169
|
if ((await requestStringArray(provider, { method: "eth_accounts" })).length === 0) throw new Error("No authorized accounts for silent reconnect");
|
|
171
170
|
return;
|
|
172
171
|
}
|
|
@@ -180,22 +179,18 @@ const buildEvmAdapter = (info, provider) => {
|
|
|
180
179
|
});
|
|
181
180
|
} catch {}
|
|
182
181
|
},
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
async getAccounts() {
|
|
187
|
-
return (await readAccountState(provider, info.name))?.accounts ?? [];
|
|
182
|
+
getAccounts: () => readAccounts(provider),
|
|
183
|
+
async getBalance(options) {
|
|
184
|
+
return readEvmBalance(provider, await resolveAddress(options?.account), options?.token);
|
|
188
185
|
},
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
getSigner: () => Promise.resolve(provider),
|
|
195
|
-
async getTransactionReceipt(tx) {
|
|
186
|
+
getSigner: () => Promise.resolve({
|
|
187
|
+
kind: "eip1193",
|
|
188
|
+
provider
|
|
189
|
+
}),
|
|
190
|
+
async getTransactionReceipt(hash) {
|
|
196
191
|
const receipt = await provider.request({
|
|
197
192
|
method: "eth_getTransactionReceipt",
|
|
198
|
-
params: [
|
|
193
|
+
params: [hash]
|
|
199
194
|
});
|
|
200
195
|
if (receipt === null || typeof receipt !== "object" || !("status" in receipt)) return { status: "Pending" };
|
|
201
196
|
return { status: receipt.status === "0x1" ? "Success" : "Error" };
|
|
@@ -210,86 +205,66 @@ const buildEvmAdapter = (info, provider) => {
|
|
|
210
205
|
params: [{ eth_accounts: {} }]
|
|
211
206
|
});
|
|
212
207
|
} catch (error) {
|
|
213
|
-
const
|
|
214
|
-
const
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
await provider.request({ method: "eth_requestAccounts" });
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
throw error;
|
|
208
|
+
const outer = typeof error === "object" && error !== null ? error : {};
|
|
209
|
+
const data = "data" in outer ? outer.data : void 0;
|
|
210
|
+
const inner = typeof data === "object" && data !== null && "originalError" in data ? data.originalError : void 0;
|
|
211
|
+
if (!("code" in outer && METHOD_NOT_SUPPORTED_CODES.has(outer.code) || typeof inner === "object" && inner !== null && "code" in inner && METHOD_NOT_SUPPORTED_CODES.has(inner.code))) throw error;
|
|
212
|
+
await provider.request({ method: "eth_requestAccounts" });
|
|
222
213
|
}
|
|
223
214
|
},
|
|
224
|
-
async sendTx(tx,
|
|
225
|
-
const
|
|
215
|
+
async sendTx(tx, options) {
|
|
216
|
+
const from = await resolveAddress(options?.account);
|
|
217
|
+
if (options?.chain !== void 0) await switchChain(options.chain);
|
|
226
218
|
const hash = await requestString(provider, {
|
|
227
219
|
method: "eth_sendTransaction",
|
|
228
|
-
params: [
|
|
220
|
+
params: [encodeValue({
|
|
221
|
+
...tx,
|
|
222
|
+
from
|
|
223
|
+
})]
|
|
229
224
|
});
|
|
230
225
|
if (hash === null) throw new Error("Wallet returned no transaction hash");
|
|
231
226
|
return hash;
|
|
232
227
|
},
|
|
233
|
-
async
|
|
234
|
-
const
|
|
235
|
-
const targetHex = chainIdDecimalToHex(targetChainIdDecimal);
|
|
236
|
-
if (current?.toLowerCase() !== targetHex.toLowerCase()) {
|
|
237
|
-
await provider.request({
|
|
238
|
-
method: "wallet_switchEthereumChain",
|
|
239
|
-
params: [{ chainId: targetHex }]
|
|
240
|
-
});
|
|
241
|
-
cb?.();
|
|
242
|
-
}
|
|
243
|
-
const txWithFrom = account ? withFrom(tx, account.walletAddress) : tx;
|
|
244
|
-
const hash = await requestString(provider, {
|
|
245
|
-
method: "eth_sendTransaction",
|
|
246
|
-
params: [txWithFrom]
|
|
247
|
-
});
|
|
248
|
-
if (hash === null) throw new Error("Wallet returned no transaction hash");
|
|
249
|
-
return hash;
|
|
250
|
-
},
|
|
251
|
-
async signMessage(msg, account) {
|
|
252
|
-
let signer = account?.walletAddress;
|
|
253
|
-
if (signer === void 0 || signer === "") signer = (await requestStringArray(provider, { method: "eth_accounts" }))[0];
|
|
254
|
-
if (signer === void 0 || signer === "") throw new Error("No connected account");
|
|
228
|
+
async signMessage(message, options) {
|
|
229
|
+
const address = await resolveAddress(options?.account);
|
|
255
230
|
const signatureHex = await requestString(provider, {
|
|
256
231
|
method: "personal_sign",
|
|
257
|
-
params: [bytesToHexPrefixed(
|
|
232
|
+
params: [bytesToHexPrefixed(message), address]
|
|
258
233
|
});
|
|
259
234
|
if (signatureHex === null) throw new Error("Wallet returned a malformed personal_sign response");
|
|
260
235
|
return {
|
|
261
|
-
signature: hexToBytes
|
|
262
|
-
signedMessage:
|
|
236
|
+
signature: hexToBytes(signatureHex),
|
|
237
|
+
signedMessage: message
|
|
263
238
|
};
|
|
264
239
|
},
|
|
265
240
|
subscribe(listener) {
|
|
266
|
-
const
|
|
241
|
+
const synchronize = async (known) => {
|
|
267
242
|
try {
|
|
268
|
-
const
|
|
269
|
-
if (
|
|
270
|
-
|
|
271
|
-
type: "
|
|
243
|
+
const accounts = await readAccounts(provider, known);
|
|
244
|
+
if (accounts.length > 0) listener({
|
|
245
|
+
accounts,
|
|
246
|
+
type: "accountsChanged"
|
|
272
247
|
});
|
|
273
248
|
} catch {}
|
|
274
249
|
};
|
|
275
250
|
const onAccountsChanged = (...args) => {
|
|
276
|
-
const
|
|
277
|
-
if (
|
|
251
|
+
const addresses = toStringArray(args[0]);
|
|
252
|
+
if (addresses.length === 0) {
|
|
278
253
|
listener({ type: "disconnected" });
|
|
279
254
|
return;
|
|
280
255
|
}
|
|
281
|
-
|
|
256
|
+
synchronize({ addresses });
|
|
282
257
|
};
|
|
283
258
|
const onChainChanged = (...args) => {
|
|
284
259
|
const [chainId] = args;
|
|
285
260
|
const chainIdHex = typeof chainId === "string" && chainId.length > 0 ? chainId : void 0;
|
|
286
|
-
|
|
261
|
+
synchronize({ chainIdHex });
|
|
287
262
|
};
|
|
288
263
|
const onDisconnect = () => {
|
|
289
264
|
listener({ type: "disconnected" });
|
|
290
265
|
};
|
|
291
266
|
const onConnect = () => {
|
|
292
|
-
|
|
267
|
+
synchronize();
|
|
293
268
|
};
|
|
294
269
|
provider.on("accountsChanged", onAccountsChanged);
|
|
295
270
|
provider.on("chainChanged", onChainChanged);
|
|
@@ -302,14 +277,7 @@ const buildEvmAdapter = (info, provider) => {
|
|
|
302
277
|
provider.removeListener("disconnect", onDisconnect);
|
|
303
278
|
};
|
|
304
279
|
},
|
|
305
|
-
|
|
306
|
-
const targetHex = chainIdDecimalToHex(chain.reference);
|
|
307
|
-
if ((await requestString(provider, { method: "eth_chainId" }))?.toLowerCase() === targetHex.toLowerCase()) return;
|
|
308
|
-
await provider.request({
|
|
309
|
-
method: "wallet_switchEthereumChain",
|
|
310
|
-
params: [{ chainId: targetHex }]
|
|
311
|
-
});
|
|
312
|
-
}
|
|
280
|
+
switchChain
|
|
313
281
|
};
|
|
314
282
|
};
|
|
315
283
|
|
|
@@ -323,13 +291,12 @@ const isEip6963AnnounceEvent = (event) => {
|
|
|
323
291
|
if (typeof detail !== "object" || detail === null) return false;
|
|
324
292
|
if (!("info" in detail) || !("provider" in detail)) return false;
|
|
325
293
|
const { info, provider } = detail;
|
|
326
|
-
return typeof info === "object" && info !== null && "rdns" in info && typeof info.rdns === "string" && typeof provider === "object" && provider !== null;
|
|
294
|
+
return typeof info === "object" && info !== null && "icon" in info && typeof info.icon === "string" && "name" in info && typeof info.name === "string" && "rdns" in info && typeof info.rdns === "string" && "uuid" in info && typeof info.uuid === "string" && typeof provider === "object" && provider !== null && "on" in provider && typeof provider.on === "function" && "removeListener" in provider && typeof provider.removeListener === "function" && "request" in provider && typeof provider.request === "function";
|
|
327
295
|
};
|
|
328
|
-
const
|
|
296
|
+
const windowGlobal = globalThis;
|
|
329
297
|
const resolveTarget = (target) => {
|
|
330
298
|
if (target !== void 0) return target;
|
|
331
|
-
|
|
332
|
-
return isEventTarget(candidate) ? candidate : null;
|
|
299
|
+
return windowGlobal.window ?? null;
|
|
333
300
|
};
|
|
334
301
|
/**
|
|
335
302
|
* Spec: https://eips.ethereum.org/EIPS/eip-6963. Dedupe is by `info.rdns`
|
|
@@ -364,12 +331,13 @@ const discoverEvmAdapters = (onAdapter, options = {}) => {
|
|
|
364
331
|
*/
|
|
365
332
|
const GENERIC_INJECTED_ICON = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNMjEgMTJWN0g1YTIgMiAwIDAgMSAwLTQgaDE0djQiLz48cGF0aCBkPSJNMyA1djE0YTIgMiAwIDAgMCAyIDJoMTZ2LTVNMjEgMTIgaC0zYTIgMiAwIDAgMC0yIDJ2MGEyIDIgMCAwIDAgMiAyaDN6Ii8+PC9zdmc+";
|
|
366
333
|
const DEFAULT_SETTLE_MS = 150;
|
|
367
|
-
|
|
334
|
+
/** Structural check for code that holds a provider of unknown origin, such
|
|
335
|
+
* as `window.ethereum`. A butr signer narrows on `kind === "eip1193"`
|
|
336
|
+
* instead. */
|
|
337
|
+
const isEip1193Provider = (value) => typeof value === "object" && value !== null && "on" in value && typeof value.on === "function" && "removeListener" in value && typeof value.removeListener === "function" && "request" in value && typeof value.request === "function";
|
|
368
338
|
const readEthereum = (target) => {
|
|
369
|
-
const host = target === void 0 ?
|
|
370
|
-
|
|
371
|
-
const eth = host.ethereum;
|
|
372
|
-
return isEip1193Provider(eth) ? eth : null;
|
|
339
|
+
const host = target === void 0 ? globalThis.window : target;
|
|
340
|
+
return isEip1193Provider(host?.ethereum) ? host.ethereum : null;
|
|
373
341
|
};
|
|
374
342
|
/**
|
|
375
343
|
* Last-resort path for the long tail of EVM wallets that expose only
|
|
@@ -397,59 +365,6 @@ const discoverInjectedAdapter = (onAdapter, options = {}) => {
|
|
|
397
365
|
};
|
|
398
366
|
};
|
|
399
367
|
|
|
400
|
-
//#endregion
|
|
401
|
-
//#region src/chains.ts
|
|
402
|
-
/**
|
|
403
|
-
* Common EVM chain registry keyed by CAIP-2 id. Useful for chain-
|
|
404
|
-
* switcher UIs. butr itself doesn't read this: `Connector.switchChain`
|
|
405
|
-
* accepts any `ChainBase`.
|
|
406
|
-
*/
|
|
407
|
-
const EVM_CHAINS = {
|
|
408
|
-
arbitrum: {
|
|
409
|
-
id: "eip155:42161",
|
|
410
|
-
name: "Arbitrum One",
|
|
411
|
-
namespace: "eip155",
|
|
412
|
-
reference: "42161"
|
|
413
|
-
},
|
|
414
|
-
base: {
|
|
415
|
-
id: "eip155:8453",
|
|
416
|
-
name: "Base",
|
|
417
|
-
namespace: "eip155",
|
|
418
|
-
reference: "8453"
|
|
419
|
-
},
|
|
420
|
-
bsc: {
|
|
421
|
-
id: "eip155:56",
|
|
422
|
-
name: "BNB Smart Chain",
|
|
423
|
-
namespace: "eip155",
|
|
424
|
-
reference: "56"
|
|
425
|
-
},
|
|
426
|
-
ethereum: {
|
|
427
|
-
id: "eip155:1",
|
|
428
|
-
name: "Ethereum",
|
|
429
|
-
namespace: "eip155",
|
|
430
|
-
reference: "1"
|
|
431
|
-
},
|
|
432
|
-
optimism: {
|
|
433
|
-
id: "eip155:10",
|
|
434
|
-
name: "Optimism",
|
|
435
|
-
namespace: "eip155",
|
|
436
|
-
reference: "10"
|
|
437
|
-
},
|
|
438
|
-
polygon: {
|
|
439
|
-
id: "eip155:137",
|
|
440
|
-
name: "Polygon",
|
|
441
|
-
namespace: "eip155",
|
|
442
|
-
reference: "137"
|
|
443
|
-
},
|
|
444
|
-
sepolia: {
|
|
445
|
-
id: "eip155:11155111",
|
|
446
|
-
name: "Sepolia",
|
|
447
|
-
namespace: "eip155",
|
|
448
|
-
reference: "11155111"
|
|
449
|
-
}
|
|
450
|
-
};
|
|
451
|
-
const EVM_CHAINS_LIST = Object.values(EVM_CHAINS);
|
|
452
|
-
|
|
453
368
|
//#endregion
|
|
454
369
|
//#region src/discoverer.ts
|
|
455
370
|
/**
|
|
@@ -462,5 +377,5 @@ const evmDiscoverer = {
|
|
|
462
377
|
};
|
|
463
378
|
|
|
464
379
|
//#endregion
|
|
465
|
-
export { ANNOUNCE_EVENT,
|
|
380
|
+
export { ANNOUNCE_EVENT, GENERIC_INJECTED_ICON, REQUEST_EVENT, buildEvmAdapter, chainIdDecimalToHex, chainIdHexToDecimal, discoverEvmAdapters, discoverInjectedAdapter, evmDiscoverer, formatEther, isEip1193Provider };
|
|
466
381
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/eip1193.ts","../src/evm-balance.ts","../src/eip6963-adapter.ts","../src/eip6963.ts","../src/injected.ts","../src/discoverer.ts"],"sourcesContent":["/**\n * Declared inline rather than taken from `viem`, which would expand butr's\n * footprint. Loose on purpose: `request(...)` returns `unknown` and the\n * `request*` helpers narrow it at the boundary.\n */\n\ntype Eip1193Object = { [key: string]: Eip1193Value | undefined };\ntype Eip1193Value =\n | bigint\n | boolean\n | number\n | string\n | null\n | Uint8Array\n | ReadonlyArray<Eip1193Value>\n | Eip1193Object;\n\ntype Eip1193RequestArgs = {\n method: string;\n params?: ReadonlyArray<Eip1193Value> | Eip1193Object;\n};\n\ntype Eip1193Listener = (...args: ReadonlyArray<Eip1193Value | undefined>) => void;\n\ntype Eip1193Provider = {\n on: (event: string, listener: Eip1193Listener) => void;\n removeListener: (event: string, listener: Eip1193Listener) => void;\n request: (args: Eip1193RequestArgs) => Promise<Eip1193Value | undefined>;\n};\n\ntype Eip6963ProviderInfo = {\n /** Data URI or absolute URL pointing at the wallet's icon. */\n icon: string;\n /** Human-readable wallet name shown in pickers. */\n name: string;\n /** Reverse-DNS identifier: `io.metamask`, `io.rabby`, `app.phantom`.\n * Stable across versions, unique per wallet, used as the pool key. */\n rdns: string;\n /** Per-page UUID generated by the wallet. Not stable across reloads. */\n uuid: string;\n};\n\ntype Eip6963ProviderDetail = {\n info: Eip6963ProviderInfo;\n provider: Eip1193Provider;\n};\n\ntype Eip6963AnnounceEvent = CustomEvent<Eip6963ProviderDetail>;\n\n/** `provider.request` is typed `unknown` by design; these helpers narrow\n * the well-known EVM RPC shapes at the boundary with runtime guards so\n * call sites stay assertion-free. A malformed response is reported as\n * `null` / `[]` rather than a blank value, so call sites that cannot\n * proceed without a real one fail instead of inventing a default. */\nconst requestString = async (\n provider: Eip1193Provider,\n args: Eip1193RequestArgs,\n): Promise<string | null> => {\n const result = await provider.request(args);\n return typeof result === \"string\" ? result : null;\n};\n\nconst requestStringArray = async (\n provider: Eip1193Provider,\n args: Eip1193RequestArgs,\n): Promise<Array<string>> => {\n const result = await provider.request(args);\n return Array.isArray(result)\n ? result.filter((item): item is string => typeof item === \"string\")\n : [];\n};\n\nexport { requestString, requestStringArray };\n\nexport type {\n Eip1193Listener,\n Eip1193Object,\n Eip1193Provider,\n Eip1193RequestArgs,\n Eip1193Value,\n Eip6963AnnounceEvent,\n Eip6963ProviderDetail,\n Eip6963ProviderInfo,\n};\n","import { hexToBytes } from \"@usebutr/core\";\n\nimport type { Eip1193Provider } from \"./eip1193\";\nimport { requestString } from \"./eip1193\";\n\nconst HEX_PREFIX = \"0x\";\nconst ETH_DECIMALS = 18n;\nconst ERC20_BALANCE_OF_SELECTOR = \"0x70a08231\";\nconst ERC20_DECIMALS_SELECTOR = \"0x313ce567\";\nconst ERC20_SYMBOL_SELECTOR = \"0x95d89b41\";\n\n/** A failed read must not render as a confident zero balance. */\nconst requireHex = (hex: string | null, label: string): string => {\n if (hex === null) {\n throw new Error(`Wallet returned a malformed ${label} response`);\n }\n return hex;\n};\n\nconst formatUnits = (raw: bigint, decimals: number): string => {\n if (decimals === 0) {\n return raw.toString();\n }\n const unit = 10n ** BigInt(decimals);\n const integer = raw / unit;\n const remainder = raw % unit;\n if (remainder === 0n) {\n return integer.toString();\n }\n const fraction = remainder.toString().padStart(decimals, \"0\").replace(/0+$/v, \"\");\n return `${integer}.${fraction}`;\n};\n\nconst formatEther = (wei: bigint): string => formatUnits(wei, Number(ETH_DECIMALS));\n\nconst padAddressForCall = (address: string): string =>\n address.replace(/^0x/v, \"\").toLowerCase().padStart(64, \"0\");\n\nconst decodeAbiString = (hex: string): string => {\n const clean = hex.startsWith(HEX_PREFIX) ? hex.slice(2) : hex;\n if (clean.length === 0) {\n return \"\";\n }\n if (clean.length >= 128) {\n const lengthHex = clean.slice(64, 128);\n const length = Number(BigInt(`0x${lengthHex}`));\n if (length > 0 && clean.length >= 128 + length * 2) {\n return new TextDecoder().decode(hexToBytes(clean.slice(128, 128 + length * 2)));\n }\n }\n const bytes = hexToBytes(clean.slice(0, 64));\n let end = bytes.length;\n while (end > 0 && bytes[end - 1] === 0) {\n end -= 1;\n }\n return new TextDecoder().decode(bytes.subarray(0, end));\n};\n\nconst readEvmBalance = async (\n provider: Eip1193Provider,\n address: string,\n tokenAddress?: string,\n) => {\n if (tokenAddress === undefined || tokenAddress === \"\") {\n const balanceHex = await requestString(provider, {\n method: \"eth_getBalance\",\n params: [address, \"latest\"],\n });\n const value = BigInt(requireHex(balanceHex, \"eth_getBalance\"));\n return {\n decimals: Number(ETH_DECIMALS),\n formatted: formatEther(value),\n symbol: \"ETH\",\n value,\n };\n }\n\n const [balanceHex, decimalsHex, symbolHex] = await Promise.all([\n requestString(provider, {\n method: \"eth_call\",\n params: [\n { data: `${ERC20_BALANCE_OF_SELECTOR}${padAddressForCall(address)}`, to: tokenAddress },\n \"latest\",\n ],\n }),\n requestString(provider, {\n method: \"eth_call\",\n params: [{ data: ERC20_DECIMALS_SELECTOR, to: tokenAddress }, \"latest\"],\n }),\n requestString(provider, {\n method: \"eth_call\",\n params: [{ data: ERC20_SYMBOL_SELECTOR, to: tokenAddress }, \"latest\"],\n }),\n ]);\n const value = BigInt(requireHex(balanceHex, \"balanceOf()\"));\n const decimals = Number(BigInt(requireHex(decimalsHex, \"decimals()\")));\n return {\n decimals,\n formatted: formatUnits(value, decimals),\n symbol: decodeAbiString(symbolHex ?? \"\"),\n value,\n };\n};\n\nexport { formatEther, readEvmBalance };\n","import type { Account, ChainBase, EvmAdapter, EvmTransactionValue } from \"@usebutr/core\";\nimport {\n buildAccount,\n bytesToHexPrefixed,\n EVM_CHAINS_LIST,\n hexToBytes,\n resolveChain,\n sanitizeIcon,\n} from \"@usebutr/core\";\n\nimport type {\n Eip1193Listener,\n Eip1193Object,\n Eip1193Provider,\n Eip1193Value,\n Eip6963ProviderInfo,\n} from \"./eip1193\";\nimport { requestString, requestStringArray } from \"./eip1193\";\nimport { readEvmBalance } from \"./evm-balance\";\n\nconst EVM_NAMESPACE = \"eip155\";\n\nconst toStringArray = (value: Eip1193Value | undefined): Array<string> =>\n Array.isArray(value) ? value.filter((item): item is string => typeof item === \"string\") : [];\n\n/** JSON-RPC cannot carry a `bigint`, so quantities go out as `0x` hex. */\nconst encodeValue = (value: EvmTransactionValue): Eip1193Value => {\n if (typeof value === \"bigint\") {\n return `0x${value.toString(16)}`;\n }\n if (Array.isArray(value)) {\n return value.map(encodeValue);\n }\n if (typeof value !== \"object\" || value === null) {\n return value;\n }\n const result: Eip1193Object = {};\n for (const [key, nested] of Object.entries(value)) {\n result[key] = nested === undefined ? undefined : encodeValue(nested);\n }\n return result;\n};\n\nconst chainIdHexToDecimal = (hex: string): string => BigInt(hex).toString(10);\nconst chainIdDecimalToHex = (dec: string): string => `0x${BigInt(dec).toString(16)}`;\n\nconst evmChainFromHex = (chainIdHex: string): ChainBase =>\n resolveChain(`${EVM_NAMESPACE}:${chainIdHexToDecimal(chainIdHex)}`, EVM_CHAINS_LIST);\n\n/** An event may already carry the addresses or the chain; only the missing\n * half is read back from the wallet. */\ntype KnownAccountState = {\n addresses?: ReadonlyArray<string>;\n chainIdHex?: string;\n};\n\n/** Rejections propagate: `getAccounts` surfaces the wallet's own provider\n * error, while the `subscribe` bridge swallows it because a later event\n * retries synchronization. */\nconst readAccounts = async (\n provider: Eip1193Provider,\n known: KnownAccountState = {},\n): Promise<Array<Account>> => {\n const addresses =\n known.addresses ?? (await requestStringArray(provider, { method: \"eth_accounts\" }));\n if (addresses.length === 0) {\n return [];\n }\n const chainIdHex = known.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 = evmChainFromHex(chainIdHex);\n return addresses.map((address) => buildAccount(address, chain));\n};\n\n/** EIP-1474 / JSON-RPC codes wallets use for an unimplemented method.\n * Coinbase wraps its own -32604 inside a -32603 `data.originalError`. */\nconst METHOD_NOT_SUPPORTED_CODES = new Set<unknown>([4200, -32_601, -32_603, -32_604]);\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): EvmAdapter => {\n /** The address to act as, in the wallet's own casing. Hex addresses are\n * case-insensitive (EIP-55 casing is only a checksum), so they compare\n * lowercased. */\n const resolveAddress = async (account?: Account): Promise<string> => {\n const exposed = await requestStringArray(provider, { method: \"eth_accounts\" });\n if (account === undefined) {\n const active = exposed.at(0);\n if (active === undefined) {\n throw new Error(`Wallet ${info.name} has no connected account`);\n }\n return active;\n }\n const wanted = account.walletAddress.toLowerCase();\n const match = exposed.find((address) => address.toLowerCase() === wanted);\n if (match === undefined) {\n throw new Error(`Wallet ${info.name} does not expose account ${account.walletAddress}`);\n }\n return match;\n };\n\n const switchChain = async (chain: ChainBase): Promise<void> => {\n if (chain.namespace !== EVM_NAMESPACE || !/^\\d+$/v.test(chain.reference)) {\n throw new Error(\n `EVM adapter received non-EVM chain \"${chain.id}\". Pass a chain with namespace \"${EVM_NAMESPACE}\" and a numeric reference.`,\n );\n }\n const target = chainIdDecimalToHex(chain.reference);\n const current = await requestString(provider, { method: \"eth_chainId\" });\n if (current?.toLowerCase() === target) {\n return;\n }\n await provider.request({ method: \"wallet_switchEthereumChain\", params: [{ chainId: target }] });\n };\n\n return {\n chainPlatform: \"evm\",\n\n async connect(options) {\n if (options?.silent === true) {\n const accounts = await requestStringArray(provider, { method: \"eth_accounts\" });\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 getAccounts: () => readAccounts(provider),\n\n async getBalance(options) {\n return readEvmBalance(provider, await resolveAddress(options?.account), options?.token);\n },\n\n getSigner: () => Promise.resolve({ kind: \"eip1193\", provider }),\n\n async getTransactionReceipt(hash) {\n const receipt = await provider.request({\n method: \"eth_getTransactionReceipt\",\n params: [hash],\n });\n // `null` is the RPC's answer for a transaction not yet mined.\n if (receipt === null || typeof receipt !== \"object\" || !(\"status\" in receipt)) {\n return { status: \"Pending\" };\n }\n return { status: receipt.status === \"0x1\" ? \"Success\" : \"Error\" };\n },\n\n icon: sanitizeIcon(info.icon),\n id: info.rdns,\n name: info.name,\n\n async requestAccounts() {\n try {\n await provider.request({\n method: \"wallet_requestPermissions\",\n params: [{ eth_accounts: {} }],\n });\n } catch (error) {\n const outer = typeof error === \"object\" && error !== null ? error : {};\n const data = \"data\" in outer ? outer.data : undefined;\n const inner =\n typeof data === \"object\" && data !== null && \"originalError\" in data\n ? data.originalError\n : undefined;\n const unsupported =\n (\"code\" in outer && METHOD_NOT_SUPPORTED_CODES.has(outer.code)) ||\n (typeof inner === \"object\" &&\n inner !== null &&\n \"code\" in inner &&\n METHOD_NOT_SUPPORTED_CODES.has(inner.code));\n if (!unsupported) {\n throw error;\n }\n await provider.request({ method: \"eth_requestAccounts\" });\n }\n },\n\n // EVM wallets have one global network, so a `chain` switches it before\n // the send rather than routing the one call. `from` is always the\n // resolved account, replacing any `from` inside `tx`.\n async sendTx(tx, options) {\n const from = await resolveAddress(options?.account);\n if (options?.chain !== undefined) {\n await switchChain(options.chain);\n }\n const hash = await requestString(provider, {\n method: \"eth_sendTransaction\",\n params: [encodeValue({ ...tx, from })],\n });\n if (hash === null) {\n throw new Error(\"Wallet returned no transaction hash\");\n }\n return hash;\n },\n\n async signMessage(message, options) {\n const address = await resolveAddress(options?.account);\n const signatureHex = await requestString(provider, {\n method: \"personal_sign\",\n params: [bytesToHexPrefixed(message), address],\n });\n if (signatureHex === null) {\n throw new Error(\"Wallet returned a malformed personal_sign response\");\n }\n return { signature: hexToBytes(signatureHex), signedMessage: message };\n },\n\n subscribe(listener) {\n const synchronize = async (known?: KnownAccountState) => {\n try {\n const accounts = await readAccounts(provider, known);\n if (accounts.length > 0) {\n listener({ accounts, type: \"accountsChanged\" });\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 addresses = toStringArray(args[0]);\n if (addresses.length === 0) {\n listener({ type: \"disconnected\" });\n return;\n }\n void synchronize({ addresses });\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 synchronize({ chainIdHex });\n };\n\n const onDisconnect: Eip1193Listener = () => {\n listener({ type: \"disconnected\" });\n };\n\n const onConnect: Eip1193Listener = () => {\n void synchronize();\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 switchChain,\n };\n};\n\nexport { formatEther } from \"./evm-balance\";\nexport { buildEvmAdapter, chainIdDecimalToHex, chainIdHexToDecimal };\n","import type { EvmAdapter } 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: EvmAdapter, info: Eip6963ProviderInfo) => void;\n\ntype DiscoverOptions = {\n /** Event target to listen on. Defaults to `window` in browser/RN/\n * SSR'd environments; tests can pass a fresh `new EventTarget()` to\n * exercise the discovery loop without a DOM. */\n target?: EventTarget;\n};\n\nconst isEip6963AnnounceEvent = (event: Event): event is Eip6963AnnounceEvent => {\n if (!(\"detail\" in event)) {\n return false;\n }\n const detail: unknown = event.detail;\n if (typeof detail !== \"object\" || detail === null) {\n return false;\n }\n if (!(\"info\" in detail) || !(\"provider\" in detail)) {\n return false;\n }\n const { info, provider } = detail;\n return (\n typeof info === \"object\" &&\n info !== null &&\n \"icon\" in info &&\n typeof info.icon === \"string\" &&\n \"name\" in info &&\n typeof info.name === \"string\" &&\n \"rdns\" in info &&\n typeof info.rdns === \"string\" &&\n \"uuid\" in info &&\n typeof info.uuid === \"string\" &&\n typeof provider === \"object\" &&\n provider !== null &&\n \"on\" in provider &&\n typeof provider.on === \"function\" &&\n \"removeListener\" in provider &&\n typeof provider.removeListener === \"function\" &&\n \"request\" in provider &&\n typeof provider.request === \"function\"\n );\n};\n\nconst windowGlobal: Partial<Pick<typeof globalThis, \"window\">> = globalThis;\n\nconst resolveTarget = (target?: EventTarget): EventTarget | null => {\n if (target !== undefined) {\n return target;\n }\n return windowGlobal.window ?? null;\n};\n\n/**\n * Spec: https://eips.ethereum.org/EIPS/eip-6963. Dedupe is by `info.rdns`\n * because `info.uuid` is regenerated per page load. The listener stays\n * attached for the session so wallets that boot late still register.\n */\nconst discoverEvmAdapters = (\n onAdapter: AdapterCallback,\n options: DiscoverOptions = {},\n): (() => void) => {\n const target = resolveTarget(options.target);\n if (!target) {\n return () => {};\n }\n\n const seen = new Set<string>();\n const handler = (event: Event) => {\n if (!isEip6963AnnounceEvent(event)) {\n return;\n }\n const { info, provider } = event.detail;\n if (info.rdns.length === 0) {\n return;\n }\n if (seen.has(info.rdns)) {\n return;\n }\n seen.add(info.rdns);\n onAdapter(buildEvmAdapter(info, provider), info);\n };\n\n target.addEventListener(ANNOUNCE_EVENT, handler);\n target.dispatchEvent(new Event(REQUEST_EVENT));\n\n return () => {\n target.removeEventListener(ANNOUNCE_EVENT, handler);\n };\n};\n\nexport { ANNOUNCE_EVENT, REQUEST_EVENT, discoverEvmAdapters };\n","import type { EvmAdapter } from \"@usebutr/core\";\n\nimport type { Eip1193Provider, Eip6963ProviderInfo } from \"./eip1193\";\nimport { buildEvmAdapter } from \"./eip6963-adapter\";\n\n/**\n * Generic wallet icon (a stylised purse SVG, ~600 bytes). Used as\n * the fallback icon when an injected provider has no EIP-6963 info.\n * Inline rather than a network fetch so the picker doesn't flicker.\n */\nconst GENERIC_INJECTED_ICON =\n \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNMjEgMTJWN0g1YTIgMiAwIDAgMSAwLTQgaDE0djQiLz48cGF0aCBkPSJNMyA1djE0YTIgMiAwIDAgMCAyIDJoMTZ2LTVNMjEgMTIgaC0zYTIgMiAwIDAgMC0yIDJ2MGEyIDIgMCAwIDAgMiAyaDN6Ii8+PC9zdmc+\";\n\nconst DEFAULT_SETTLE_MS = 150;\n\ntype InjectedDiscoveryOptions = {\n /**\n * Consulted before emitting: an already-registered EIP-6963 adapter means\n * the wallet is covered by the standards path and emitting would duplicate\n * it. Omit to always emit.\n */\n hasAnyEip6963Adapter?: () => boolean;\n /**\n * Most wallets announce within the first frame; 150ms leaves room for slow\n * announcements without stalling the picker.\n */\n settleMs?: number;\n /** Override the global `window` reference (tests, iframes). */\n target?: { ethereum?: Partial<Eip1193Provider> } | null;\n};\n\ndeclare global {\n // oxlint-disable-next-line typescript/consistent-type-definitions -- DOM globals require interface merging.\n interface Window {\n ethereum?: Partial<Eip1193Provider>;\n }\n}\n\n/** Structural check for code that holds a provider of unknown origin, such\n * as `window.ethereum`. A butr signer narrows on `kind === \"eip1193\"`\n * instead. */\nconst isEip1193Provider = (value: unknown): value is Eip1193Provider =>\n typeof value === \"object\" &&\n value !== null &&\n \"on\" in value &&\n typeof value.on === \"function\" &&\n \"removeListener\" in value &&\n typeof value.removeListener === \"function\" &&\n \"request\" in value &&\n typeof value.request === \"function\";\n\nconst readEthereum = (target: InjectedDiscoveryOptions[\"target\"]): Eip1193Provider | null => {\n const host = target === undefined ? globalThis.window : target;\n return isEip1193Provider(host?.ethereum) ? host.ethereum : null;\n};\n\n/**\n * Last-resort path for the long tail of EVM wallets that expose only\n * `window.ethereum` and never announce via EIP-6963. The emitted adapter\n * carries `rdns: \"injected:legacy\"` so consumers can tell it apart.\n */\nconst discoverInjectedAdapter = (\n onAdapter: (adapter: EvmAdapter) => void,\n options: InjectedDiscoveryOptions = {},\n): (() => void) => {\n const settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;\n let cancelled = false;\n const timer = setTimeout(() => {\n if (cancelled) {\n return;\n }\n if (options.hasAnyEip6963Adapter?.() === true) {\n return;\n }\n const provider = readEthereum(options.target);\n if (provider === null) {\n return;\n }\n const info: Eip6963ProviderInfo = {\n icon: GENERIC_INJECTED_ICON,\n name: \"Browser wallet\",\n rdns: \"injected:legacy\",\n uuid: \"injected:legacy\",\n };\n onAdapter(buildEvmAdapter(info, provider));\n }, settleMs);\n\n return () => {\n cancelled = true;\n clearTimeout(timer);\n };\n};\n\nexport type { InjectedDiscoveryOptions };\nexport { GENERIC_INJECTED_ICON, discoverInjectedAdapter, isEip1193Provider };\n","import type { PlatformDiscoverer } from \"@usebutr/core\";\n\nimport { discoverEvmAdapters } from \"./eip6963\";\nimport { discoverInjectedAdapter } from \"./injected\";\n\n/**\n * EIP-6963 is primary; the legacy `window.ethereum` fallback defers until\n * EIP-6963 has had a chance to fire.\n */\nconst evmDiscoverer: PlatformDiscoverer = {\n fallback: {\n subscribe: (onAdapter, opts) =>\n discoverInjectedAdapter(onAdapter, {\n hasAnyEip6963Adapter: opts.hasAnyPrimaryAdapter,\n }),\n },\n subscribe: discoverEvmAdapters,\n};\n\nexport { evmDiscoverer };\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usebutr/evm",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "EIP-1193 / EIP-6963 / injected wallet discovery + adapters for butr.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"butr",
|
|
7
|
+
"dapp",
|
|
8
|
+
"eip-6963",
|
|
9
|
+
"ethereum",
|
|
10
|
+
"metamask",
|
|
11
|
+
"react",
|
|
12
|
+
"typescript",
|
|
13
|
+
"wallet",
|
|
14
|
+
"web3"
|
|
15
|
+
],
|
|
16
|
+
"homepage": "https://docs.usebutr.com/api/evm",
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/pedroapfilho/usebutr/issues"
|
|
19
|
+
},
|
|
5
20
|
"license": "MIT",
|
|
6
21
|
"author": "Pedro Filho <pedro@filho.me>",
|
|
7
22
|
"repository": {
|
|
@@ -10,7 +25,8 @@
|
|
|
10
25
|
"directory": "packages/evm"
|
|
11
26
|
},
|
|
12
27
|
"files": [
|
|
13
|
-
"dist"
|
|
28
|
+
"dist",
|
|
29
|
+
"CHANGELOG.md"
|
|
14
30
|
],
|
|
15
31
|
"type": "module",
|
|
16
32
|
"sideEffects": false,
|
|
@@ -25,11 +41,11 @@
|
|
|
25
41
|
"access": "public"
|
|
26
42
|
},
|
|
27
43
|
"dependencies": {
|
|
28
|
-
"@usebutr/core": "
|
|
44
|
+
"@usebutr/core": "3.0.0"
|
|
29
45
|
},
|
|
30
46
|
"devDependencies": {
|
|
31
|
-
"typescript": "^
|
|
32
|
-
"vitest": "^
|
|
47
|
+
"typescript": "^7.0.2",
|
|
48
|
+
"vitest": "^5.0.0",
|
|
33
49
|
"@repo/config-vitest": "0.0.0",
|
|
34
50
|
"@repo/typescript-config": "0.0.0"
|
|
35
51
|
},
|