@naculus/connect-appkit-core 0.2.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/LICENSE +21 -0
- package/dist/index.cjs +352 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +325 -0
- package/dist/index.d.ts +325 -0
- package/dist/index.js +301 -0
- package/dist/index.js.map +1 -0
- package/package.json +52 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { Namespace, UniversalWalletSession, TokenConfig, WalletError, WalletErrorCode, NameResolverConfig, NameResolver } from '@naculus/connect-core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The types the framework-neutral half of appkit is built on.
|
|
5
|
+
*
|
|
6
|
+
* They live here rather than in the React package because a Vue composable
|
|
7
|
+
* needs to name the same things, and importing them from a package called
|
|
8
|
+
* `-react` would be both odd and a lie about what the dependency is.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
type WalletChain = {
|
|
12
|
+
/**
|
|
13
|
+
* CAIP-2, and the chain's identity.
|
|
14
|
+
*
|
|
15
|
+
* This was `id: number` alongside a separate `namespace`, which could only
|
|
16
|
+
* ever describe an EIP-155 chain: a Solana reference is base58
|
|
17
|
+
* (`solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp`) and an XRPL one is an
|
|
18
|
+
* unsigned network id. The registry structurally could not hold a non-EVM
|
|
19
|
+
* chain, so every non-EVM path fell out to "unknown".
|
|
20
|
+
*
|
|
21
|
+
* `namespace` is gone rather than kept alongside: two fields that must
|
|
22
|
+
* agree are two fields that eventually do not.
|
|
23
|
+
*/
|
|
24
|
+
caip2: string;
|
|
25
|
+
name: string;
|
|
26
|
+
rpcUrl?: string;
|
|
27
|
+
explorerUrl?: string;
|
|
28
|
+
token?: string;
|
|
29
|
+
};
|
|
30
|
+
interface ChainInfo {
|
|
31
|
+
namespace: Namespace;
|
|
32
|
+
chainId: string;
|
|
33
|
+
name: string;
|
|
34
|
+
selected: boolean;
|
|
35
|
+
}
|
|
36
|
+
type ConnectionStatus = "disconnected" | "connecting" | "authenticating" | "connected" | "reconnecting";
|
|
37
|
+
interface Web3State {
|
|
38
|
+
status: ConnectionStatus;
|
|
39
|
+
session: UniversalWalletSession | null;
|
|
40
|
+
accounts: string[];
|
|
41
|
+
chainId: string | null;
|
|
42
|
+
error: Error | null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Which chains a session can actually switch to, and what the current one is.
|
|
47
|
+
*
|
|
48
|
+
* Framework-neutral, and deliberately built on `parseChainId` from
|
|
49
|
+
* `@naculus/connect-core` rather than on another `startsWith("eip155:")`.
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The EIP-155 chain number, or null when this is not an EVM chain.
|
|
54
|
+
*
|
|
55
|
+
* Callers that need a number — a viem client, an ERC-20 read — must handle
|
|
56
|
+
* the null rather than defaulting, because there is no number that means
|
|
57
|
+
* "Solana" and any stand-in would address a real EVM chain.
|
|
58
|
+
*/
|
|
59
|
+
declare function chainNumber(chain: WalletChain): number | null;
|
|
60
|
+
/** The namespace a chain belongs to, read from its CAIP-2 id. */
|
|
61
|
+
declare function chainNamespace(chain: WalletChain): string | null;
|
|
62
|
+
/**
|
|
63
|
+
* The configured chain for a CAIP-2 id, or null.
|
|
64
|
+
*
|
|
65
|
+
* A string comparison now that `WalletChain` is keyed by CAIP-2. It used to
|
|
66
|
+
* pull an integer out of the id and compare that, which no non-EVM chain
|
|
67
|
+
* could ever match.
|
|
68
|
+
*/
|
|
69
|
+
declare function resolveChain(chains: WalletChain[], chainId: string | null | undefined): WalletChain | null;
|
|
70
|
+
/**
|
|
71
|
+
* The chains worth offering for the session's namespace.
|
|
72
|
+
*
|
|
73
|
+
* This used to return every configured chain regardless of what was
|
|
74
|
+
* connected, so a Solana wallet was offered Ethereum and Polygon in the
|
|
75
|
+
* switcher. Clicking one asked a wallet that has no concept of EIP-155 to
|
|
76
|
+
* switch to chain 137 — an action the interface presented as available and
|
|
77
|
+
* that could never work.
|
|
78
|
+
*
|
|
79
|
+
* With no connected chain, everything is offered: nothing has been ruled out
|
|
80
|
+
* yet, and hiding the list would leave a user unable to pick.
|
|
81
|
+
*/
|
|
82
|
+
declare function chainsForNamespace(chains: WalletChain[], chainId: string | null | undefined): WalletChain[];
|
|
83
|
+
/**
|
|
84
|
+
* A description of the connected chain for display.
|
|
85
|
+
*
|
|
86
|
+
* Returns something for every namespace. It used to return null for anything
|
|
87
|
+
* that was not EIP-155, so a connected Solana wallet had no chain to show at
|
|
88
|
+
* all and the interface fell back to "Unknown Chain".
|
|
89
|
+
*/
|
|
90
|
+
declare function describeChain(chains: WalletChain[], chainId: string | null | undefined): ChainInfo | null;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The viem `Chain` shape for a configured EVM chain.
|
|
94
|
+
*
|
|
95
|
+
* Three hooks each built this inline — `useBalance`, `useTokenBalance` and
|
|
96
|
+
* `useViemClient` — with identical bodies and the same `?? "ETH"` in each.
|
|
97
|
+
* Three copies of a fallback is three places for it to be wrong.
|
|
98
|
+
*
|
|
99
|
+
* Returns a plain object rather than importing viem, so this package stays
|
|
100
|
+
* free of a dependency only the React bindings need. The shape satisfies
|
|
101
|
+
* viem's `Chain` structurally.
|
|
102
|
+
*/
|
|
103
|
+
|
|
104
|
+
interface ViemChainShape {
|
|
105
|
+
id: number;
|
|
106
|
+
name: string;
|
|
107
|
+
nativeCurrency: {
|
|
108
|
+
name: string;
|
|
109
|
+
symbol: string;
|
|
110
|
+
decimals: number;
|
|
111
|
+
};
|
|
112
|
+
rpcUrls: {
|
|
113
|
+
default: {
|
|
114
|
+
http: string[];
|
|
115
|
+
};
|
|
116
|
+
public: {
|
|
117
|
+
http: string[];
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Build the chain descriptor a viem client needs, or null when this chain is
|
|
123
|
+
* not one viem can talk to.
|
|
124
|
+
*
|
|
125
|
+
* Null for a non-EVM chain rather than a stand-in number: viem speaks to EVM
|
|
126
|
+
* nodes, and any number chosen to fill the gap addresses a real chain that is
|
|
127
|
+
* not the one connected.
|
|
128
|
+
*
|
|
129
|
+
* The native symbol comes from the chain's own `token`, then from the shared
|
|
130
|
+
* chain registry. For an unlisted chain it stays empty rather than claiming
|
|
131
|
+
* the asset is ETH. viem requires a string here; the user-facing symbol comes
|
|
132
|
+
* from `useBalance().symbol`, which stays null when it is unknown.
|
|
133
|
+
*/
|
|
134
|
+
declare function toViemChain(chain: WalletChain, chainNumber: number | null): ViemChainShape | null;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Which EVM chain does an account-abstraction operation belong to?
|
|
138
|
+
*
|
|
139
|
+
* The smart-account hooks each resolved this as
|
|
140
|
+
* `options.chainId ?? connectedChainId ?? "eip155:1"`, and each then guarded
|
|
141
|
+
* with `if (connectedChainId && connectedChainId !== target)`. The guard is
|
|
142
|
+
* skipped precisely when the mainnet default is used, so the one branch that
|
|
143
|
+
* assumes a chain is the one branch that never checks it.
|
|
144
|
+
*
|
|
145
|
+
* That branch is reachable: a caller passing its own `signer` needs no
|
|
146
|
+
* session, so `connectedChainId` is legitimately absent. It would then build
|
|
147
|
+
* and sign a UserOperation for chain 1 — with chain 1's EntryPoint — while its
|
|
148
|
+
* configured RPC and bundler point somewhere else. The chain ID is part of the
|
|
149
|
+
* UserOperation hash, so the user approves a commitment to a chain they were
|
|
150
|
+
* never shown.
|
|
151
|
+
*
|
|
152
|
+
* Resolving to undefined instead lets each caller refuse in whatever way suits
|
|
153
|
+
* it. Framework-agnostic on purpose: it must not import React.
|
|
154
|
+
*/
|
|
155
|
+
declare function isEvmChainId(value: string | null | undefined): boolean;
|
|
156
|
+
/**
|
|
157
|
+
* The chain to operate on, or undefined when it cannot be known.
|
|
158
|
+
*
|
|
159
|
+
* An explicitly requested chain wins over the connected one; a malformed value
|
|
160
|
+
* from either source resolves to undefined rather than being replaced by a
|
|
161
|
+
* guess, because the caller asked for something specific and got it wrong.
|
|
162
|
+
*/
|
|
163
|
+
declare function resolveEvmChainId(requested: string | null | undefined, connected: string | null | undefined): string | undefined;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Does this token belong to the chain the wallet is pointed at?
|
|
167
|
+
*
|
|
168
|
+
* `TokenConfig.chainId` existed but was only ever read as a display label and
|
|
169
|
+
* a cache key. Nothing compared it to the connected chain, so a hook handed a
|
|
170
|
+
* mainnet token config while the wallet sat on Polygon would read — or
|
|
171
|
+
* approve — whatever contract happens to occupy that address on Polygon.
|
|
172
|
+
* Usually that address holds nothing and the call reverts, which is merely
|
|
173
|
+
* confusing. When it holds a different token, `allowance()` returns a
|
|
174
|
+
* plausible number for the wrong asset, and `approve()` grants a spender
|
|
175
|
+
* rights over something the user never looked at.
|
|
176
|
+
*
|
|
177
|
+
* Framework-agnostic on purpose: it must not import React.
|
|
178
|
+
*/
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* A mismatch, or undefined when the token is on the active chain — or when
|
|
182
|
+
* there is not enough information to tell.
|
|
183
|
+
*
|
|
184
|
+
* Silence on "cannot tell" is deliberate. A token config without a chainId, or
|
|
185
|
+
* a session with no resolved chain, is a gap in what the caller supplied; a
|
|
186
|
+
* guess in either direction would be worse than letting the call proceed and
|
|
187
|
+
* fail on its own terms.
|
|
188
|
+
*/
|
|
189
|
+
declare function tokenChainMismatch(token: Pick<TokenConfig, "address" | "chainId">, activeChainId: number | undefined): WalletError | undefined;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Decimals cache keyed by the token it belongs to.
|
|
193
|
+
*
|
|
194
|
+
* The ERC-20 hooks cached decimals in a bare `useRef` seeded from the token
|
|
195
|
+
* prop. `useRef` only takes its initial value on mount, and nothing reset it,
|
|
196
|
+
* so a component that lets the user switch tokens kept the first token's
|
|
197
|
+
* precision: picking USDC (6) and then DAI (18) converted "100" with 6
|
|
198
|
+
* decimals and transferred 0.0000000001 DAI. The transaction succeeded, so
|
|
199
|
+
* nothing surfaced the mistake.
|
|
200
|
+
*
|
|
201
|
+
* Keying the cache by the token makes a stale hit impossible without needing
|
|
202
|
+
* an effect to fire first.
|
|
203
|
+
*/
|
|
204
|
+
interface TokenIdentity {
|
|
205
|
+
chainId?: number;
|
|
206
|
+
address: string;
|
|
207
|
+
}
|
|
208
|
+
interface DecimalsCache {
|
|
209
|
+
key: string;
|
|
210
|
+
value: number | undefined;
|
|
211
|
+
}
|
|
212
|
+
declare function tokenKey(token: TokenIdentity): string;
|
|
213
|
+
declare function createDecimalsCache(token: TokenIdentity, known?: number): DecimalsCache;
|
|
214
|
+
/**
|
|
215
|
+
* Cached decimals for `token`, or undefined when the cache holds another
|
|
216
|
+
* token's value. Resets the cache as a side effect so the next write lands
|
|
217
|
+
* against the right key.
|
|
218
|
+
*/
|
|
219
|
+
declare function readDecimals(cache: DecimalsCache, token: TokenIdentity, known?: number): number | undefined;
|
|
220
|
+
declare function writeDecimals(cache: DecimalsCache, token: TokenIdentity, value: number): void;
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Normalize an EIP-1193 provider error into a WalletError.
|
|
224
|
+
*
|
|
225
|
+
* Hooks were collapsing every non-WalletError into one code — `useSwitchChain`
|
|
226
|
+
* labelled a user pressing Cancel as `chain_unsupported`, so the UI told them
|
|
227
|
+
* the chain was unavailable when they had simply declined. The codes below are
|
|
228
|
+
* the ones wallets actually send, and they mean different things to a caller:
|
|
229
|
+
* one is retryable by asking again, the other is not retryable at all until
|
|
230
|
+
* the chain is added.
|
|
231
|
+
*
|
|
232
|
+
* Framework-agnostic on purpose: it must not import React.
|
|
233
|
+
*/
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Map a chain-switch failure onto the code that describes it.
|
|
237
|
+
*
|
|
238
|
+
* `fallback` is used when the wallet gave nothing to go on, so the caller
|
|
239
|
+
* still decides what an unexplained failure means in its own context.
|
|
240
|
+
*/
|
|
241
|
+
declare function toChainSwitchError(error: unknown, fallback?: WalletErrorCode): WalletError;
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Decode an EVM revert payload into something a user can act on.
|
|
245
|
+
*
|
|
246
|
+
* This text is what a wallet shows when a simulation says a transaction would
|
|
247
|
+
* fail, so it is worth getting right and worth sharing: the logic is pure and
|
|
248
|
+
* every binding needs the same answer.
|
|
249
|
+
*
|
|
250
|
+
* Two things are handled that the inlined version did not. The ABI length
|
|
251
|
+
* word is attacker-controlled, so a payload can declare a longer string than
|
|
252
|
+
* it carries; decoding that produced a run of NUL bytes rather than an honest
|
|
253
|
+
* "could not decode". And a Panic was reported as "Built-in failure" with the
|
|
254
|
+
* code discarded, which is the one piece of information that distinguishes an
|
|
255
|
+
* overflow from a division by zero.
|
|
256
|
+
*/
|
|
257
|
+
/**
|
|
258
|
+
* @param data Raw revert data from the node, with or without the 0x prefix.
|
|
259
|
+
* @returns The decoded reason, or undefined when the payload is not a shape
|
|
260
|
+
* this understands — callers should fall back to the RPC error text rather
|
|
261
|
+
* than presenting a guess.
|
|
262
|
+
*/
|
|
263
|
+
declare function decodeRevertReason(data: unknown): string | undefined;
|
|
264
|
+
/** Decoded reason if available, otherwise the node's own message. */
|
|
265
|
+
declare function resolveRevertReason(data: unknown, fallbackMessage: string): string;
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Shared NameResolver instance for the name-resolution hooks.
|
|
269
|
+
*
|
|
270
|
+
* useResolveName and useLookupAddress each carried an identical getResolver
|
|
271
|
+
* with its own module-level singleton, so the two directions of the same
|
|
272
|
+
* lookup ran against separate resolvers and separate caches. One definition,
|
|
273
|
+
* one instance.
|
|
274
|
+
*/
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* @param config When supplied, returns a dedicated resolver rather than the
|
|
278
|
+
* shared one — a caller asking for specific settings must not be handed an
|
|
279
|
+
* instance configured by someone else.
|
|
280
|
+
*/
|
|
281
|
+
declare function getResolver(config?: NameResolverConfig): NameResolver;
|
|
282
|
+
/** Test seam: drop the shared instance. */
|
|
283
|
+
declare function __resetResolverForTests(): void;
|
|
284
|
+
|
|
285
|
+
declare function withTimeout<T>(promise: Promise<T>, ms: number, label?: string): Promise<T>;
|
|
286
|
+
interface RetryOptions {
|
|
287
|
+
maxRetries: number;
|
|
288
|
+
baseDelay: number;
|
|
289
|
+
label?: string;
|
|
290
|
+
}
|
|
291
|
+
declare function withRetry<T>(fn: () => Promise<T>, options: RetryOptions): Promise<T>;
|
|
292
|
+
/** Convert EIP-1193 chainChanged values into the CAIP-2 form used by sessions. */
|
|
293
|
+
declare function normalizeEip155ChainId(value: unknown): string | undefined;
|
|
294
|
+
declare function toEip155Accounts(accounts: unknown[], chainId: string): string[];
|
|
295
|
+
/**
|
|
296
|
+
* Numeric EIP-155 chain id, or undefined for any other namespace.
|
|
297
|
+
*
|
|
298
|
+
* The previous implementation took the last CAIP segment and ran parseInt over
|
|
299
|
+
* it, which stops at the first non-digit: `solana:5eykt4Us…` became 5 — an
|
|
300
|
+
* EVM chain id — so asking for Solana mainnet's token list returned Goerli's,
|
|
301
|
+
* and `xrpl:0` became chain 0. Anchor the whole reference instead.
|
|
302
|
+
*/
|
|
303
|
+
declare function eip155ChainIdToNumber(chainId: string): number | undefined;
|
|
304
|
+
type Web3Action = {
|
|
305
|
+
type: "SET_STATUS";
|
|
306
|
+
payload: ConnectionStatus;
|
|
307
|
+
} | {
|
|
308
|
+
type: "SET_SESSION";
|
|
309
|
+
payload: Web3State["session"];
|
|
310
|
+
} | {
|
|
311
|
+
type: "SET_ACCOUNTS";
|
|
312
|
+
payload: string[];
|
|
313
|
+
} | {
|
|
314
|
+
type: "SET_CHAIN";
|
|
315
|
+
payload: string | null;
|
|
316
|
+
} | {
|
|
317
|
+
type: "SET_ERROR";
|
|
318
|
+
payload: Error | null;
|
|
319
|
+
} | {
|
|
320
|
+
type: "RESET";
|
|
321
|
+
};
|
|
322
|
+
declare const initialWeb3State: Web3State;
|
|
323
|
+
declare function web3Reducer(state: Web3State, action: Web3Action): Web3State;
|
|
324
|
+
|
|
325
|
+
export { type ChainInfo, type ConnectionStatus, type DecimalsCache, type RetryOptions, type TokenIdentity, type ViemChainShape, type WalletChain, type Web3Action, type Web3State, __resetResolverForTests, chainNamespace, chainNumber, chainsForNamespace, createDecimalsCache, decodeRevertReason, describeChain, eip155ChainIdToNumber, getResolver, initialWeb3State, isEvmChainId, normalizeEip155ChainId, readDecimals, resolveChain, resolveEvmChainId, resolveRevertReason, toChainSwitchError, toEip155Accounts, toViemChain, tokenChainMismatch, tokenKey, web3Reducer, withRetry, withTimeout, writeDecimals };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { Namespace, UniversalWalletSession, TokenConfig, WalletError, WalletErrorCode, NameResolverConfig, NameResolver } from '@naculus/connect-core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The types the framework-neutral half of appkit is built on.
|
|
5
|
+
*
|
|
6
|
+
* They live here rather than in the React package because a Vue composable
|
|
7
|
+
* needs to name the same things, and importing them from a package called
|
|
8
|
+
* `-react` would be both odd and a lie about what the dependency is.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
type WalletChain = {
|
|
12
|
+
/**
|
|
13
|
+
* CAIP-2, and the chain's identity.
|
|
14
|
+
*
|
|
15
|
+
* This was `id: number` alongside a separate `namespace`, which could only
|
|
16
|
+
* ever describe an EIP-155 chain: a Solana reference is base58
|
|
17
|
+
* (`solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp`) and an XRPL one is an
|
|
18
|
+
* unsigned network id. The registry structurally could not hold a non-EVM
|
|
19
|
+
* chain, so every non-EVM path fell out to "unknown".
|
|
20
|
+
*
|
|
21
|
+
* `namespace` is gone rather than kept alongside: two fields that must
|
|
22
|
+
* agree are two fields that eventually do not.
|
|
23
|
+
*/
|
|
24
|
+
caip2: string;
|
|
25
|
+
name: string;
|
|
26
|
+
rpcUrl?: string;
|
|
27
|
+
explorerUrl?: string;
|
|
28
|
+
token?: string;
|
|
29
|
+
};
|
|
30
|
+
interface ChainInfo {
|
|
31
|
+
namespace: Namespace;
|
|
32
|
+
chainId: string;
|
|
33
|
+
name: string;
|
|
34
|
+
selected: boolean;
|
|
35
|
+
}
|
|
36
|
+
type ConnectionStatus = "disconnected" | "connecting" | "authenticating" | "connected" | "reconnecting";
|
|
37
|
+
interface Web3State {
|
|
38
|
+
status: ConnectionStatus;
|
|
39
|
+
session: UniversalWalletSession | null;
|
|
40
|
+
accounts: string[];
|
|
41
|
+
chainId: string | null;
|
|
42
|
+
error: Error | null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Which chains a session can actually switch to, and what the current one is.
|
|
47
|
+
*
|
|
48
|
+
* Framework-neutral, and deliberately built on `parseChainId` from
|
|
49
|
+
* `@naculus/connect-core` rather than on another `startsWith("eip155:")`.
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The EIP-155 chain number, or null when this is not an EVM chain.
|
|
54
|
+
*
|
|
55
|
+
* Callers that need a number — a viem client, an ERC-20 read — must handle
|
|
56
|
+
* the null rather than defaulting, because there is no number that means
|
|
57
|
+
* "Solana" and any stand-in would address a real EVM chain.
|
|
58
|
+
*/
|
|
59
|
+
declare function chainNumber(chain: WalletChain): number | null;
|
|
60
|
+
/** The namespace a chain belongs to, read from its CAIP-2 id. */
|
|
61
|
+
declare function chainNamespace(chain: WalletChain): string | null;
|
|
62
|
+
/**
|
|
63
|
+
* The configured chain for a CAIP-2 id, or null.
|
|
64
|
+
*
|
|
65
|
+
* A string comparison now that `WalletChain` is keyed by CAIP-2. It used to
|
|
66
|
+
* pull an integer out of the id and compare that, which no non-EVM chain
|
|
67
|
+
* could ever match.
|
|
68
|
+
*/
|
|
69
|
+
declare function resolveChain(chains: WalletChain[], chainId: string | null | undefined): WalletChain | null;
|
|
70
|
+
/**
|
|
71
|
+
* The chains worth offering for the session's namespace.
|
|
72
|
+
*
|
|
73
|
+
* This used to return every configured chain regardless of what was
|
|
74
|
+
* connected, so a Solana wallet was offered Ethereum and Polygon in the
|
|
75
|
+
* switcher. Clicking one asked a wallet that has no concept of EIP-155 to
|
|
76
|
+
* switch to chain 137 — an action the interface presented as available and
|
|
77
|
+
* that could never work.
|
|
78
|
+
*
|
|
79
|
+
* With no connected chain, everything is offered: nothing has been ruled out
|
|
80
|
+
* yet, and hiding the list would leave a user unable to pick.
|
|
81
|
+
*/
|
|
82
|
+
declare function chainsForNamespace(chains: WalletChain[], chainId: string | null | undefined): WalletChain[];
|
|
83
|
+
/**
|
|
84
|
+
* A description of the connected chain for display.
|
|
85
|
+
*
|
|
86
|
+
* Returns something for every namespace. It used to return null for anything
|
|
87
|
+
* that was not EIP-155, so a connected Solana wallet had no chain to show at
|
|
88
|
+
* all and the interface fell back to "Unknown Chain".
|
|
89
|
+
*/
|
|
90
|
+
declare function describeChain(chains: WalletChain[], chainId: string | null | undefined): ChainInfo | null;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The viem `Chain` shape for a configured EVM chain.
|
|
94
|
+
*
|
|
95
|
+
* Three hooks each built this inline — `useBalance`, `useTokenBalance` and
|
|
96
|
+
* `useViemClient` — with identical bodies and the same `?? "ETH"` in each.
|
|
97
|
+
* Three copies of a fallback is three places for it to be wrong.
|
|
98
|
+
*
|
|
99
|
+
* Returns a plain object rather than importing viem, so this package stays
|
|
100
|
+
* free of a dependency only the React bindings need. The shape satisfies
|
|
101
|
+
* viem's `Chain` structurally.
|
|
102
|
+
*/
|
|
103
|
+
|
|
104
|
+
interface ViemChainShape {
|
|
105
|
+
id: number;
|
|
106
|
+
name: string;
|
|
107
|
+
nativeCurrency: {
|
|
108
|
+
name: string;
|
|
109
|
+
symbol: string;
|
|
110
|
+
decimals: number;
|
|
111
|
+
};
|
|
112
|
+
rpcUrls: {
|
|
113
|
+
default: {
|
|
114
|
+
http: string[];
|
|
115
|
+
};
|
|
116
|
+
public: {
|
|
117
|
+
http: string[];
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Build the chain descriptor a viem client needs, or null when this chain is
|
|
123
|
+
* not one viem can talk to.
|
|
124
|
+
*
|
|
125
|
+
* Null for a non-EVM chain rather than a stand-in number: viem speaks to EVM
|
|
126
|
+
* nodes, and any number chosen to fill the gap addresses a real chain that is
|
|
127
|
+
* not the one connected.
|
|
128
|
+
*
|
|
129
|
+
* The native symbol comes from the chain's own `token`, then from the shared
|
|
130
|
+
* chain registry. For an unlisted chain it stays empty rather than claiming
|
|
131
|
+
* the asset is ETH. viem requires a string here; the user-facing symbol comes
|
|
132
|
+
* from `useBalance().symbol`, which stays null when it is unknown.
|
|
133
|
+
*/
|
|
134
|
+
declare function toViemChain(chain: WalletChain, chainNumber: number | null): ViemChainShape | null;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Which EVM chain does an account-abstraction operation belong to?
|
|
138
|
+
*
|
|
139
|
+
* The smart-account hooks each resolved this as
|
|
140
|
+
* `options.chainId ?? connectedChainId ?? "eip155:1"`, and each then guarded
|
|
141
|
+
* with `if (connectedChainId && connectedChainId !== target)`. The guard is
|
|
142
|
+
* skipped precisely when the mainnet default is used, so the one branch that
|
|
143
|
+
* assumes a chain is the one branch that never checks it.
|
|
144
|
+
*
|
|
145
|
+
* That branch is reachable: a caller passing its own `signer` needs no
|
|
146
|
+
* session, so `connectedChainId` is legitimately absent. It would then build
|
|
147
|
+
* and sign a UserOperation for chain 1 — with chain 1's EntryPoint — while its
|
|
148
|
+
* configured RPC and bundler point somewhere else. The chain ID is part of the
|
|
149
|
+
* UserOperation hash, so the user approves a commitment to a chain they were
|
|
150
|
+
* never shown.
|
|
151
|
+
*
|
|
152
|
+
* Resolving to undefined instead lets each caller refuse in whatever way suits
|
|
153
|
+
* it. Framework-agnostic on purpose: it must not import React.
|
|
154
|
+
*/
|
|
155
|
+
declare function isEvmChainId(value: string | null | undefined): boolean;
|
|
156
|
+
/**
|
|
157
|
+
* The chain to operate on, or undefined when it cannot be known.
|
|
158
|
+
*
|
|
159
|
+
* An explicitly requested chain wins over the connected one; a malformed value
|
|
160
|
+
* from either source resolves to undefined rather than being replaced by a
|
|
161
|
+
* guess, because the caller asked for something specific and got it wrong.
|
|
162
|
+
*/
|
|
163
|
+
declare function resolveEvmChainId(requested: string | null | undefined, connected: string | null | undefined): string | undefined;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Does this token belong to the chain the wallet is pointed at?
|
|
167
|
+
*
|
|
168
|
+
* `TokenConfig.chainId` existed but was only ever read as a display label and
|
|
169
|
+
* a cache key. Nothing compared it to the connected chain, so a hook handed a
|
|
170
|
+
* mainnet token config while the wallet sat on Polygon would read — or
|
|
171
|
+
* approve — whatever contract happens to occupy that address on Polygon.
|
|
172
|
+
* Usually that address holds nothing and the call reverts, which is merely
|
|
173
|
+
* confusing. When it holds a different token, `allowance()` returns a
|
|
174
|
+
* plausible number for the wrong asset, and `approve()` grants a spender
|
|
175
|
+
* rights over something the user never looked at.
|
|
176
|
+
*
|
|
177
|
+
* Framework-agnostic on purpose: it must not import React.
|
|
178
|
+
*/
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* A mismatch, or undefined when the token is on the active chain — or when
|
|
182
|
+
* there is not enough information to tell.
|
|
183
|
+
*
|
|
184
|
+
* Silence on "cannot tell" is deliberate. A token config without a chainId, or
|
|
185
|
+
* a session with no resolved chain, is a gap in what the caller supplied; a
|
|
186
|
+
* guess in either direction would be worse than letting the call proceed and
|
|
187
|
+
* fail on its own terms.
|
|
188
|
+
*/
|
|
189
|
+
declare function tokenChainMismatch(token: Pick<TokenConfig, "address" | "chainId">, activeChainId: number | undefined): WalletError | undefined;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Decimals cache keyed by the token it belongs to.
|
|
193
|
+
*
|
|
194
|
+
* The ERC-20 hooks cached decimals in a bare `useRef` seeded from the token
|
|
195
|
+
* prop. `useRef` only takes its initial value on mount, and nothing reset it,
|
|
196
|
+
* so a component that lets the user switch tokens kept the first token's
|
|
197
|
+
* precision: picking USDC (6) and then DAI (18) converted "100" with 6
|
|
198
|
+
* decimals and transferred 0.0000000001 DAI. The transaction succeeded, so
|
|
199
|
+
* nothing surfaced the mistake.
|
|
200
|
+
*
|
|
201
|
+
* Keying the cache by the token makes a stale hit impossible without needing
|
|
202
|
+
* an effect to fire first.
|
|
203
|
+
*/
|
|
204
|
+
interface TokenIdentity {
|
|
205
|
+
chainId?: number;
|
|
206
|
+
address: string;
|
|
207
|
+
}
|
|
208
|
+
interface DecimalsCache {
|
|
209
|
+
key: string;
|
|
210
|
+
value: number | undefined;
|
|
211
|
+
}
|
|
212
|
+
declare function tokenKey(token: TokenIdentity): string;
|
|
213
|
+
declare function createDecimalsCache(token: TokenIdentity, known?: number): DecimalsCache;
|
|
214
|
+
/**
|
|
215
|
+
* Cached decimals for `token`, or undefined when the cache holds another
|
|
216
|
+
* token's value. Resets the cache as a side effect so the next write lands
|
|
217
|
+
* against the right key.
|
|
218
|
+
*/
|
|
219
|
+
declare function readDecimals(cache: DecimalsCache, token: TokenIdentity, known?: number): number | undefined;
|
|
220
|
+
declare function writeDecimals(cache: DecimalsCache, token: TokenIdentity, value: number): void;
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Normalize an EIP-1193 provider error into a WalletError.
|
|
224
|
+
*
|
|
225
|
+
* Hooks were collapsing every non-WalletError into one code — `useSwitchChain`
|
|
226
|
+
* labelled a user pressing Cancel as `chain_unsupported`, so the UI told them
|
|
227
|
+
* the chain was unavailable when they had simply declined. The codes below are
|
|
228
|
+
* the ones wallets actually send, and they mean different things to a caller:
|
|
229
|
+
* one is retryable by asking again, the other is not retryable at all until
|
|
230
|
+
* the chain is added.
|
|
231
|
+
*
|
|
232
|
+
* Framework-agnostic on purpose: it must not import React.
|
|
233
|
+
*/
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Map a chain-switch failure onto the code that describes it.
|
|
237
|
+
*
|
|
238
|
+
* `fallback` is used when the wallet gave nothing to go on, so the caller
|
|
239
|
+
* still decides what an unexplained failure means in its own context.
|
|
240
|
+
*/
|
|
241
|
+
declare function toChainSwitchError(error: unknown, fallback?: WalletErrorCode): WalletError;
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Decode an EVM revert payload into something a user can act on.
|
|
245
|
+
*
|
|
246
|
+
* This text is what a wallet shows when a simulation says a transaction would
|
|
247
|
+
* fail, so it is worth getting right and worth sharing: the logic is pure and
|
|
248
|
+
* every binding needs the same answer.
|
|
249
|
+
*
|
|
250
|
+
* Two things are handled that the inlined version did not. The ABI length
|
|
251
|
+
* word is attacker-controlled, so a payload can declare a longer string than
|
|
252
|
+
* it carries; decoding that produced a run of NUL bytes rather than an honest
|
|
253
|
+
* "could not decode". And a Panic was reported as "Built-in failure" with the
|
|
254
|
+
* code discarded, which is the one piece of information that distinguishes an
|
|
255
|
+
* overflow from a division by zero.
|
|
256
|
+
*/
|
|
257
|
+
/**
|
|
258
|
+
* @param data Raw revert data from the node, with or without the 0x prefix.
|
|
259
|
+
* @returns The decoded reason, or undefined when the payload is not a shape
|
|
260
|
+
* this understands — callers should fall back to the RPC error text rather
|
|
261
|
+
* than presenting a guess.
|
|
262
|
+
*/
|
|
263
|
+
declare function decodeRevertReason(data: unknown): string | undefined;
|
|
264
|
+
/** Decoded reason if available, otherwise the node's own message. */
|
|
265
|
+
declare function resolveRevertReason(data: unknown, fallbackMessage: string): string;
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Shared NameResolver instance for the name-resolution hooks.
|
|
269
|
+
*
|
|
270
|
+
* useResolveName and useLookupAddress each carried an identical getResolver
|
|
271
|
+
* with its own module-level singleton, so the two directions of the same
|
|
272
|
+
* lookup ran against separate resolvers and separate caches. One definition,
|
|
273
|
+
* one instance.
|
|
274
|
+
*/
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* @param config When supplied, returns a dedicated resolver rather than the
|
|
278
|
+
* shared one — a caller asking for specific settings must not be handed an
|
|
279
|
+
* instance configured by someone else.
|
|
280
|
+
*/
|
|
281
|
+
declare function getResolver(config?: NameResolverConfig): NameResolver;
|
|
282
|
+
/** Test seam: drop the shared instance. */
|
|
283
|
+
declare function __resetResolverForTests(): void;
|
|
284
|
+
|
|
285
|
+
declare function withTimeout<T>(promise: Promise<T>, ms: number, label?: string): Promise<T>;
|
|
286
|
+
interface RetryOptions {
|
|
287
|
+
maxRetries: number;
|
|
288
|
+
baseDelay: number;
|
|
289
|
+
label?: string;
|
|
290
|
+
}
|
|
291
|
+
declare function withRetry<T>(fn: () => Promise<T>, options: RetryOptions): Promise<T>;
|
|
292
|
+
/** Convert EIP-1193 chainChanged values into the CAIP-2 form used by sessions. */
|
|
293
|
+
declare function normalizeEip155ChainId(value: unknown): string | undefined;
|
|
294
|
+
declare function toEip155Accounts(accounts: unknown[], chainId: string): string[];
|
|
295
|
+
/**
|
|
296
|
+
* Numeric EIP-155 chain id, or undefined for any other namespace.
|
|
297
|
+
*
|
|
298
|
+
* The previous implementation took the last CAIP segment and ran parseInt over
|
|
299
|
+
* it, which stops at the first non-digit: `solana:5eykt4Us…` became 5 — an
|
|
300
|
+
* EVM chain id — so asking for Solana mainnet's token list returned Goerli's,
|
|
301
|
+
* and `xrpl:0` became chain 0. Anchor the whole reference instead.
|
|
302
|
+
*/
|
|
303
|
+
declare function eip155ChainIdToNumber(chainId: string): number | undefined;
|
|
304
|
+
type Web3Action = {
|
|
305
|
+
type: "SET_STATUS";
|
|
306
|
+
payload: ConnectionStatus;
|
|
307
|
+
} | {
|
|
308
|
+
type: "SET_SESSION";
|
|
309
|
+
payload: Web3State["session"];
|
|
310
|
+
} | {
|
|
311
|
+
type: "SET_ACCOUNTS";
|
|
312
|
+
payload: string[];
|
|
313
|
+
} | {
|
|
314
|
+
type: "SET_CHAIN";
|
|
315
|
+
payload: string | null;
|
|
316
|
+
} | {
|
|
317
|
+
type: "SET_ERROR";
|
|
318
|
+
payload: Error | null;
|
|
319
|
+
} | {
|
|
320
|
+
type: "RESET";
|
|
321
|
+
};
|
|
322
|
+
declare const initialWeb3State: Web3State;
|
|
323
|
+
declare function web3Reducer(state: Web3State, action: Web3Action): Web3State;
|
|
324
|
+
|
|
325
|
+
export { type ChainInfo, type ConnectionStatus, type DecimalsCache, type RetryOptions, type TokenIdentity, type ViemChainShape, type WalletChain, type Web3Action, type Web3State, __resetResolverForTests, chainNamespace, chainNumber, chainsForNamespace, createDecimalsCache, decodeRevertReason, describeChain, eip155ChainIdToNumber, getResolver, initialWeb3State, isEvmChainId, normalizeEip155ChainId, readDecimals, resolveChain, resolveEvmChainId, resolveRevertReason, toChainSwitchError, toEip155Accounts, toViemChain, tokenChainMismatch, tokenKey, web3Reducer, withRetry, withTimeout, writeDecimals };
|