@cfxdevkit/wallet-connect 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,550 @@
1
+ // src/auth-context.tsx
2
+ import {
3
+ createContext,
4
+ useCallback,
5
+ useContext,
6
+ useEffect,
7
+ useRef,
8
+ useState
9
+ } from "react";
10
+ import { SiweMessage } from "siwe";
11
+ import { useAccount, useDisconnect, useSignMessage } from "wagmi";
12
+ import { jsx } from "react/jsx-runtime";
13
+ var AuthContext = createContext(null);
14
+ var API_BASE = process.env.NEXT_PUBLIC_API_URL ?? "/api";
15
+ var TOKEN_KEY = "cas_jwt";
16
+ function getJwtExpiry(token) {
17
+ try {
18
+ const payload = JSON.parse(atob(token.split(".")[1]));
19
+ return payload.exp ? payload.exp * 1e3 : 0;
20
+ } catch {
21
+ return 0;
22
+ }
23
+ }
24
+ function validToken(token) {
25
+ if (!token) return null;
26
+ const exp = getJwtExpiry(token);
27
+ return exp === 0 || exp > Date.now() ? token : null;
28
+ }
29
+ function AuthProvider({ children }) {
30
+ const { address, isConnected, chainId } = useAccount();
31
+ const { signMessageAsync } = useSignMessage();
32
+ const { disconnect } = useDisconnect();
33
+ const [token, setToken] = useState(() => {
34
+ if (typeof window === "undefined") return null;
35
+ const stored = localStorage.getItem(TOKEN_KEY);
36
+ const valid = validToken(stored);
37
+ if (stored && !valid) {
38
+ localStorage.removeItem(TOKEN_KEY);
39
+ }
40
+ return valid;
41
+ });
42
+ const [isLoading, setIsLoading] = useState(false);
43
+ const [error, setError] = useState(null);
44
+ const autoSignedForRef = useRef(null);
45
+ const prevAddressRef = useRef(null);
46
+ const mountedRef = useRef(false);
47
+ useEffect(() => {
48
+ if (!mountedRef.current) {
49
+ mountedRef.current = true;
50
+ if (address) prevAddressRef.current = address;
51
+ return;
52
+ }
53
+ if (address) {
54
+ if (prevAddressRef.current && prevAddressRef.current !== address) {
55
+ setToken(null);
56
+ localStorage.removeItem(TOKEN_KEY);
57
+ autoSignedForRef.current = null;
58
+ }
59
+ prevAddressRef.current = address;
60
+ }
61
+ }, [address]);
62
+ const login = useCallback(async () => {
63
+ if (!address) {
64
+ setError("No wallet connected");
65
+ return;
66
+ }
67
+ setIsLoading(true);
68
+ setError(null);
69
+ try {
70
+ const nonceRes = await fetch(
71
+ `${API_BASE}/auth/nonce?address=${encodeURIComponent(address)}`
72
+ );
73
+ if (!nonceRes.ok) throw new Error("Failed to fetch nonce");
74
+ const { nonce } = await nonceRes.json();
75
+ const message = new SiweMessage({
76
+ domain: window.location.host,
77
+ address,
78
+ statement: "Sign in to Conflux Automation Service",
79
+ uri: window.location.origin,
80
+ version: "1",
81
+ chainId: chainId ?? (process.env.NEXT_PUBLIC_NETWORK === "mainnet" ? 1030 : 71),
82
+ nonce
83
+ });
84
+ const prepared = message.prepareMessage();
85
+ const signature = await signMessageAsync({ message: prepared });
86
+ const verifyRes = await fetch(`${API_BASE}/auth/verify`, {
87
+ method: "POST",
88
+ headers: { "Content-Type": "application/json" },
89
+ body: JSON.stringify({ message: prepared, signature })
90
+ });
91
+ if (!verifyRes.ok) {
92
+ const body = await verifyRes.json().catch(() => ({}));
93
+ throw new Error(body.error ?? "Verification failed");
94
+ }
95
+ const { token: jwt } = await verifyRes.json();
96
+ localStorage.setItem(TOKEN_KEY, jwt);
97
+ setToken(jwt);
98
+ setError(null);
99
+ } catch (err) {
100
+ const msg = err instanceof Error ? err.message : "Login failed";
101
+ if (msg.includes("getChainId is not a function")) {
102
+ setError(
103
+ "Wallet connection issue. Please reconnect your wallet or try a different provider."
104
+ );
105
+ } else {
106
+ setError(
107
+ msg.toLowerCase().includes("rejected") ? "Signature rejected \u2014 try again." : msg
108
+ );
109
+ }
110
+ } finally {
111
+ setIsLoading(false);
112
+ }
113
+ }, [address, chainId, signMessageAsync]);
114
+ useEffect(() => {
115
+ if (isConnected && address && !token && !isLoading && autoSignedForRef.current !== address) {
116
+ autoSignedForRef.current = address;
117
+ void login();
118
+ }
119
+ }, [isConnected, address, token, isLoading, login]);
120
+ const refreshAuth = useCallback(() => {
121
+ setToken(null);
122
+ setError(null);
123
+ localStorage.removeItem(TOKEN_KEY);
124
+ autoSignedForRef.current = null;
125
+ }, []);
126
+ const logout = useCallback(() => {
127
+ setToken(null);
128
+ setError(null);
129
+ localStorage.removeItem(TOKEN_KEY);
130
+ autoSignedForRef.current = null;
131
+ disconnect();
132
+ }, [disconnect]);
133
+ return /* @__PURE__ */ jsx(
134
+ AuthContext.Provider,
135
+ {
136
+ value: {
137
+ token,
138
+ address: address ?? null,
139
+ isLoading,
140
+ isAuthenticated: Boolean(token && address),
141
+ error,
142
+ login,
143
+ logout,
144
+ refreshAuth
145
+ },
146
+ children
147
+ }
148
+ );
149
+ }
150
+ function useAuthContext() {
151
+ const ctx = useContext(AuthContext);
152
+ if (!ctx)
153
+ throw new Error("useAuthContext must be used inside <AuthProvider>");
154
+ return ctx;
155
+ }
156
+ function useAuthFetch() {
157
+ const { token, refreshAuth } = useAuthContext();
158
+ return useCallback(
159
+ async (input, init) => {
160
+ const headers = new Headers(init?.headers);
161
+ if (token) headers.set("Authorization", `Bearer ${token}`);
162
+ const res = await fetch(input, { ...init, headers });
163
+ if (res.status === 401) {
164
+ refreshAuth();
165
+ }
166
+ return res;
167
+ },
168
+ [token, refreshAuth]
169
+ );
170
+ }
171
+
172
+ // src/useNetworkSwitch.ts
173
+ import { useCallback as useCallback2, useEffect as useEffect2, useState as useState2 } from "react";
174
+ import { useAccount as useAccount2, useSwitchChain } from "wagmi";
175
+ var IS_MAINNET = process.env.NEXT_PUBLIC_NETWORK === "mainnet";
176
+ var EXPECTED_CHAIN_ID = IS_MAINNET ? 1030 : 71;
177
+ var EXPECTED_CHAIN_NAME = IS_MAINNET ? "Conflux eSpace" : "Conflux eSpace Testnet";
178
+ var CHAIN_PARAMS = IS_MAINNET ? {
179
+ chainId: "0x406",
180
+ // 1030
181
+ chainName: "Conflux eSpace",
182
+ nativeCurrency: { name: "CFX", symbol: "CFX", decimals: 18 },
183
+ rpcUrls: ["https://evm.confluxrpc.com"],
184
+ blockExplorerUrls: ["https://evm.confluxscan.org"]
185
+ } : {
186
+ chainId: "0x47",
187
+ // 71
188
+ chainName: "Conflux eSpace Testnet",
189
+ nativeCurrency: { name: "CFX", symbol: "CFX", decimals: 18 },
190
+ rpcUrls: ["https://evmtestnet.confluxrpc.com"],
191
+ blockExplorerUrls: ["https://evmtestnet.confluxscan.org"]
192
+ };
193
+ function useNetworkSwitch() {
194
+ const { isConnected, chainId } = useAccount2();
195
+ const { switchChain, isPending: isSwitching } = useSwitchChain();
196
+ const [switchError, setSwitchError] = useState2(null);
197
+ const isWrongNetwork = isConnected && chainId !== EXPECTED_CHAIN_ID;
198
+ useEffect2(() => {
199
+ if (!isWrongNetwork) setSwitchError(null);
200
+ }, [isWrongNetwork]);
201
+ const handleSwitchNetwork = useCallback2(async () => {
202
+ setSwitchError(null);
203
+ try {
204
+ switchChain({ chainId: EXPECTED_CHAIN_ID });
205
+ } catch {
206
+ const provider = window.ethereum;
207
+ if (!provider) {
208
+ setSwitchError(
209
+ "No wallet found \u2014 add the network manually in MetaMask."
210
+ );
211
+ return;
212
+ }
213
+ try {
214
+ await provider.request({
215
+ method: "wallet_addEthereumChain",
216
+ params: [CHAIN_PARAMS]
217
+ });
218
+ } catch (addErr) {
219
+ const msg = addErr instanceof Error ? addErr.message : String(addErr);
220
+ if (!msg.toLowerCase().includes("user rejected")) {
221
+ setSwitchError(`Could not switch: ${msg}`);
222
+ }
223
+ }
224
+ }
225
+ }, [switchChain]);
226
+ return { isWrongNetwork, isSwitching, switchError, handleSwitchNetwork };
227
+ }
228
+
229
+ // src/WalletConnect.tsx
230
+ import {
231
+ AlertTriangle,
232
+ Check,
233
+ Copy,
234
+ Loader2,
235
+ Power,
236
+ Wallet
237
+ } from "lucide-react";
238
+ import { useState as useState3 } from "react";
239
+ import { injected, useAccount as useAccount3, useConnect } from "wagmi";
240
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
241
+ function AddressChip({
242
+ address,
243
+ status
244
+ }) {
245
+ const [copied, setCopied] = useState3(false);
246
+ function handleCopy() {
247
+ void navigator.clipboard.writeText(address).then(() => {
248
+ setCopied(true);
249
+ setTimeout(() => setCopied(false), 1800);
250
+ });
251
+ }
252
+ const dotEl = status === "loading" ? /* @__PURE__ */ jsxs(
253
+ "svg",
254
+ {
255
+ className: "h-2.5 w-2.5 animate-spin text-slate-400 flex-shrink-0",
256
+ viewBox: "0 0 24 24",
257
+ fill: "none",
258
+ stroke: "currentColor",
259
+ strokeWidth: "2.5",
260
+ children: [
261
+ /* @__PURE__ */ jsx2("title", { children: "Loading" }),
262
+ /* @__PURE__ */ jsx2(
263
+ "path",
264
+ {
265
+ d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83",
266
+ strokeLinecap: "round"
267
+ }
268
+ )
269
+ ]
270
+ }
271
+ ) : status === "signed" ? /* @__PURE__ */ jsx2("span", { className: "h-2 w-2 rounded-full bg-green-400 shadow-[0_0_5px_#4ade80] flex-shrink-0" }) : /* @__PURE__ */ jsx2("span", { className: "h-2 w-2 rounded-full bg-orange-400 shadow-[0_0_5px_#fb923c] flex-shrink-0" });
272
+ return /* @__PURE__ */ jsxs(
273
+ "button",
274
+ {
275
+ type: "button",
276
+ onClick: handleCopy,
277
+ title: copied ? "Copied!" : address,
278
+ className: "group flex items-center gap-1.5 rounded-lg border border-slate-600 bg-slate-800\n px-2.5 py-1 transition-colors hover:border-conflux-500 hover:bg-slate-700",
279
+ children: [
280
+ dotEl,
281
+ /* @__PURE__ */ jsxs("span", { className: "font-mono text-xs text-slate-200", children: [
282
+ address.slice(0, 6),
283
+ "\u2026",
284
+ address.slice(-4)
285
+ ] }),
286
+ copied ? /* @__PURE__ */ jsx2(
287
+ Check,
288
+ {
289
+ className: "h-3 w-3 text-green-400 flex-shrink-0",
290
+ strokeWidth: 3
291
+ }
292
+ ) : /* @__PURE__ */ jsx2(Copy, { className: "h-3 w-3 text-slate-500 group-hover:text-conflux-400 flex-shrink-0 transition-colors" })
293
+ ]
294
+ }
295
+ );
296
+ }
297
+ function WalletConnect() {
298
+ const { isConnected } = useAccount3();
299
+ const { connect } = useConnect();
300
+ const { address, token, isLoading, error, login, logout } = useAuthContext();
301
+ const { isWrongNetwork, isSwitching, switchError, handleSwitchNetwork } = useNetworkSwitch();
302
+ if (!isConnected) {
303
+ return /* @__PURE__ */ jsxs(
304
+ "button",
305
+ {
306
+ type: "button",
307
+ onClick: () => connect({ connector: injected() }),
308
+ className: "group flex items-center gap-2 bg-conflux-600 hover:bg-conflux-500 text-white text-sm font-semibold py-2 px-4 rounded-xl transition-all shadow-[0_0_20px_-5px_rgba(0,120,200,0.5)] hover:shadow-[0_0_25px_-5px_rgba(0,120,200,0.7)]",
309
+ children: [
310
+ /* @__PURE__ */ jsx2(Wallet, { className: "h-4 w-4 group-hover:scale-110 transition-transform" }),
311
+ "Connect Wallet"
312
+ ]
313
+ }
314
+ );
315
+ }
316
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
317
+ address && /* @__PURE__ */ jsx2(
318
+ AddressChip,
319
+ {
320
+ address,
321
+ status: isLoading ? "loading" : token ? "signed" : "unsigned"
322
+ }
323
+ ),
324
+ isWrongNetwork ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-end gap-1", children: [
325
+ /* @__PURE__ */ jsxs(
326
+ "button",
327
+ {
328
+ type: "button",
329
+ onClick: handleSwitchNetwork,
330
+ disabled: isSwitching,
331
+ className: "flex items-center gap-1.5 text-xs bg-amber-600/20 border border-amber-600/50 hover:bg-amber-600/30\n disabled:opacity-50 text-amber-500 py-1.5 px-3 rounded-lg transition-colors whitespace-nowrap font-medium",
332
+ title: `Switch wallet to ${EXPECTED_CHAIN_NAME}`,
333
+ children: [
334
+ /* @__PURE__ */ jsx2(AlertTriangle, { className: "h-3.5 w-3.5" }),
335
+ isSwitching ? "Switching\u2026" : `Switch to ${EXPECTED_CHAIN_NAME}`
336
+ ]
337
+ }
338
+ ),
339
+ switchError && /* @__PURE__ */ jsx2("span", { className: "text-xs text-red-400 max-w-[180px] text-right leading-tight", children: switchError })
340
+ ] }) : isLoading ? /* @__PURE__ */ jsxs(
341
+ "button",
342
+ {
343
+ type: "button",
344
+ disabled: true,
345
+ className: "flex items-center gap-1.5 text-xs border border-slate-700/50 bg-slate-800/50 text-slate-500\n py-1.5 px-3 rounded-lg cursor-not-allowed",
346
+ children: [
347
+ /* @__PURE__ */ jsx2(Loader2, { className: "h-3.5 w-3.5 animate-spin text-slate-400" }),
348
+ "Signing\u2026"
349
+ ]
350
+ }
351
+ ) : !token ? /* @__PURE__ */ jsx2(
352
+ "button",
353
+ {
354
+ type: "button",
355
+ onClick: () => void login(),
356
+ title: error ?? "Sign a message to authenticate",
357
+ className: "text-xs bg-conflux-600 hover:bg-conflux-500 text-white py-1.5 px-4 rounded-lg transition-colors font-medium shadow-[0_0_15px_-5px_rgba(0,120,200,0.5)]",
358
+ children: "Sign In"
359
+ }
360
+ ) : /* @__PURE__ */ jsxs(
361
+ "button",
362
+ {
363
+ type: "button",
364
+ onClick: () => logout(),
365
+ title: "Disconnect wallet",
366
+ className: "flex items-center gap-1.5 text-xs border border-slate-700 bg-slate-800 hover:bg-red-500/10 hover:border-red-500/50\n text-slate-400 hover:text-red-400 py-1.5 px-2.5 rounded-lg transition-colors",
367
+ children: [
368
+ /* @__PURE__ */ jsx2(Power, { className: "h-3.5 w-3.5 flex-shrink-0" }),
369
+ /* @__PURE__ */ jsx2("span", { className: "hidden sm:inline font-medium", children: "Disconnect" })
370
+ ]
371
+ }
372
+ )
373
+ ] });
374
+ }
375
+
376
+ // src/wagmi.ts
377
+ import { getDefaultConfig } from "connectkit";
378
+ import { createConfig, http } from "wagmi";
379
+ import { mainnet, sepolia } from "wagmi/chains";
380
+ var confluxCore = {
381
+ id: 1029,
382
+ name: "Conflux Core",
383
+ network: "conflux-core",
384
+ nativeCurrency: {
385
+ decimals: 18,
386
+ name: "CFX",
387
+ symbol: "CFX"
388
+ },
389
+ rpcUrls: {
390
+ default: {
391
+ http: ["https://main.confluxrpc.com"]
392
+ },
393
+ public: {
394
+ http: ["https://main.confluxrpc.com"]
395
+ }
396
+ },
397
+ blockExplorers: {
398
+ default: { name: "ConfluxScan", url: "https://confluxscan.io" }
399
+ }
400
+ };
401
+ var confluxESpace = {
402
+ id: 1030,
403
+ name: "Conflux eSpace",
404
+ network: "conflux-espace",
405
+ nativeCurrency: {
406
+ decimals: 18,
407
+ name: "CFX",
408
+ symbol: "CFX"
409
+ },
410
+ rpcUrls: {
411
+ default: {
412
+ http: ["https://evm.confluxrpc.com"]
413
+ },
414
+ public: {
415
+ http: ["https://evm.confluxrpc.com"]
416
+ }
417
+ },
418
+ blockExplorers: {
419
+ default: { name: "ConfluxScan", url: "https://evmtestnet.confluxscan.io" }
420
+ }
421
+ };
422
+ var confluxCoreTestnet = {
423
+ id: 1001,
424
+ name: "Conflux Core Testnet",
425
+ network: "conflux-core-testnet",
426
+ nativeCurrency: {
427
+ decimals: 18,
428
+ name: "CFX",
429
+ symbol: "CFX"
430
+ },
431
+ rpcUrls: {
432
+ default: {
433
+ http: ["https://test.confluxrpc.com"]
434
+ },
435
+ public: {
436
+ http: ["https://test.confluxrpc.com"]
437
+ }
438
+ },
439
+ blockExplorers: {
440
+ default: { name: "ConfluxScan", url: "https://testnet.confluxscan.io" }
441
+ }
442
+ };
443
+ var confluxESpaceTestnet = {
444
+ id: 71,
445
+ name: "Conflux eSpace Testnet",
446
+ network: "conflux-espace-testnet",
447
+ nativeCurrency: {
448
+ decimals: 18,
449
+ name: "CFX",
450
+ symbol: "CFX"
451
+ },
452
+ rpcUrls: {
453
+ default: {
454
+ http: ["https://evmtestnet.confluxrpc.com"]
455
+ },
456
+ public: {
457
+ http: ["https://evmtestnet.confluxrpc.com"]
458
+ }
459
+ },
460
+ blockExplorers: {
461
+ default: { name: "ConfluxScan", url: "https://evmtestnet.confluxscan.io" }
462
+ }
463
+ };
464
+ var confluxLocalCore = {
465
+ id: 2029,
466
+ name: "Conflux Local Core",
467
+ network: "conflux-local-core",
468
+ nativeCurrency: {
469
+ decimals: 18,
470
+ name: "CFX",
471
+ symbol: "CFX"
472
+ },
473
+ rpcUrls: {
474
+ default: {
475
+ http: ["http://localhost:12537"]
476
+ },
477
+ public: {
478
+ http: ["http://localhost:12537"]
479
+ }
480
+ }
481
+ };
482
+ var confluxLocalESpace = {
483
+ id: 2030,
484
+ name: "Conflux Local eSpace",
485
+ network: "conflux-local-espace",
486
+ nativeCurrency: {
487
+ decimals: 18,
488
+ name: "CFX",
489
+ symbol: "CFX"
490
+ },
491
+ rpcUrls: {
492
+ default: {
493
+ http: ["http://localhost:8545"]
494
+ },
495
+ public: {
496
+ http: ["http://localhost:8545"]
497
+ }
498
+ }
499
+ };
500
+ var wagmiConfig = createConfig(
501
+ getDefaultConfig({
502
+ // Prioritize EVM-compatible chains (most wallets support these)
503
+ // Conflux eSpace uses standard EVM wallet infrastructure
504
+ chains: [
505
+ confluxESpace,
506
+ // Conflux eSpace Mainnet (primary - EVM compatible)
507
+ confluxLocalESpace,
508
+ // Local dev node eSpace
509
+ confluxESpaceTestnet,
510
+ // Testnet eSpace
511
+ mainnet,
512
+ // Ethereum mainnet
513
+ sepolia
514
+ // Ethereum testnet
515
+ // Note: Core chains removed from default list as few wallets support non-EVM chains
516
+ // Users can manually add confluxCore via network settings if their wallet supports it
517
+ ],
518
+ transports: {
519
+ [confluxESpace.id]: http(),
520
+ [confluxLocalESpace.id]: http(),
521
+ [confluxESpaceTestnet.id]: http(),
522
+ [mainnet.id]: http(),
523
+ [sepolia.id]: http()
524
+ },
525
+ walletConnectProjectId: process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID || "conflux-devkit-local",
526
+ appName: "Conflux DevKit",
527
+ appDescription: "Development toolkit for Conflux blockchain",
528
+ appUrl: "https://conflux-devkit.dev",
529
+ appIcon: "https://conflux-devkit.dev/icon.png"
530
+ // Note: Get a real project ID from https://cloud.walletconnect.com/ for production
531
+ })
532
+ );
533
+ export {
534
+ AuthProvider,
535
+ EXPECTED_CHAIN_ID,
536
+ EXPECTED_CHAIN_NAME,
537
+ IS_MAINNET,
538
+ WalletConnect,
539
+ confluxCore,
540
+ confluxCoreTestnet,
541
+ confluxESpace,
542
+ confluxESpaceTestnet,
543
+ confluxLocalCore,
544
+ confluxLocalESpace,
545
+ useAuthContext,
546
+ useAuthFetch,
547
+ useNetworkSwitch,
548
+ wagmiConfig
549
+ };
550
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/auth-context.tsx","../src/useNetworkSwitch.ts","../src/WalletConnect.tsx","../src/wagmi.ts"],"sourcesContent":["'use client';\n\n/**\n * AuthContext – single source of truth for wallet + SIWE auth state.\n *\n * Design:\n * - The context is provided once at layout level (inside WagmiProvider).\n * - All components that need auth state consume useAuthContext() rather than\n * calling useAuth() directly. This guarantees every consumer sees the same\n * token and loading flags with no risk of desync.\n *\n * Auto-sign flow:\n * - When wagmi reports isConnected = true and no JWT exists, the context\n * automatically kicks off the SIWE login() call without requiring the user\n * to click a separate \"Sign In\" button.\n * - If the user rejects the signature the error is surfaced and a manual\n * retry button appears in the NavBar — the auto-trigger fires only ONCE\n * per connection event to avoid a runaway prompt loop.\n * - When the wallet disconnects or the address changes, the JWT is cleared.\n */\n\nimport {\n createContext,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n} from 'react';\nimport { SiweMessage } from 'siwe';\nimport { useAccount, useDisconnect, useSignMessage } from 'wagmi';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface AuthCtx {\n /** Persisted JWT, null when not authenticated. */\n token: string | null;\n /** Checksummed wallet address, null when not connected. */\n address: string | null;\n /** True while the SIWE nonce-fetch → sign → verify round-trip is in progress. */\n isLoading: boolean;\n /** True when token is non-null and wallet is connected. */\n isAuthenticated: boolean;\n /** Last login error message (e.g. \"User rejected request\"). */\n error: string | null;\n /** Manually trigger SIWE login (e.g. retry after rejection). */\n login: () => Promise<void>;\n /** Clear JWT + disconnect wallet. */\n logout: () => void;\n /**\n * Clear the stored JWT and reset the auto-sign guard so a fresh SIWE is\n * triggered on the next render cycle. Call this when any API request\n * returns 401 (expired or invalid token) to transparently re-authenticate\n * the same wallet without the user having to do anything manually.\n */\n refreshAuth: () => void;\n}\n\n// ─── Context ──────────────────────────────────────────────────────────────────\n\nconst AuthContext = createContext<AuthCtx | null>(null);\n\nconst API_BASE = process.env.NEXT_PUBLIC_API_URL ?? '/api';\nconst TOKEN_KEY = 'cas_jwt';\n\n/**\n * Decode a JWT payload (no signature verification — just parse the base64\n * body) and return the expiry timestamp in milliseconds, or 0 if unreadable.\n */\nfunction getJwtExpiry(token: string): number {\n try {\n const payload = JSON.parse(atob(token.split('.')[1])) as { exp?: number };\n return payload.exp ? payload.exp * 1000 : 0;\n } catch {\n return 0;\n }\n}\n\n/** Return `token` only if it hasn't expired yet; otherwise `null`. */\nfunction validToken(token: string | null): string | null {\n if (!token) return null;\n const exp = getJwtExpiry(token);\n return exp === 0 || exp > Date.now() ? token : null;\n}\n\n// ─── Provider ─────────────────────────────────────────────────────────────────\n\nexport function AuthProvider({ children }: { children: ReactNode }) {\n const { address, isConnected, chainId } = useAccount();\n const { signMessageAsync } = useSignMessage();\n const { disconnect } = useDisconnect();\n\n // Initialise synchronously from localStorage so there is no flash of\n // \"unauthenticated\" on page reload when a token already exists.\n // Discard tokens that have already expired so the auto-sign effect fires\n // immediately on the next render rather than waiting for the first 401.\n const [token, setToken] = useState<string | null>(() => {\n if (typeof window === 'undefined') return null;\n const stored = localStorage.getItem(TOKEN_KEY);\n const valid = validToken(stored);\n if (stored && !valid) {\n // Proactively clear expired token so auto-sign picks it up.\n localStorage.removeItem(TOKEN_KEY);\n }\n return valid;\n });\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n // Guard: tracks the address that triggered the latest auto-signed attempt so\n // we don't fire login() repeatedly if the effect re-runs (e.g. StrictMode).\n const autoSignedForRef = useRef<string | null>(null);\n // Track the previous non-null address so we can detect a wallet switch.\n const prevAddressRef = useRef<string | null>(null);\n\n // ── Clear token when the wallet SWITCHES to a different address ─────────────\n // We do NOT clear on address → undefined (transient disconnect / wagmi\n // re-initialise) because that would force re-sign on every page navigation\n // or tab switch where wagmi briefly loses the account before reconnecting.\n // Token expiry and explicit logouts are handled separately.\n const mountedRef = useRef(false);\n useEffect(() => {\n if (!mountedRef.current) {\n mountedRef.current = true;\n if (address) prevAddressRef.current = address;\n return;\n }\n if (address) {\n // Address became known or changed.\n if (prevAddressRef.current && prevAddressRef.current !== address) {\n // Different wallet connected — previous session is invalid.\n setToken(null);\n localStorage.removeItem(TOKEN_KEY);\n autoSignedForRef.current = null;\n }\n prevAddressRef.current = address;\n }\n // When address becomes undefined we intentionally do nothing:\n // the token stays so the user doesn't have to re-sign after a brief\n // wagmi reconnect cycle or a Next.js client-side navigation.\n }, [address]);\n\n // ── Core login function ───────────────────────────────────────────────────\n const login = useCallback(async () => {\n if (!address) {\n setError('No wallet connected');\n return;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n // 1. Nonce\n const nonceRes = await fetch(\n `${API_BASE}/auth/nonce?address=${encodeURIComponent(address)}`\n );\n if (!nonceRes.ok) throw new Error('Failed to fetch nonce');\n const { nonce } = (await nonceRes.json()) as { nonce: string };\n\n // 2. Build + sign SIWE message\n const message = new SiweMessage({\n domain: window.location.host,\n address,\n statement: 'Sign in to Conflux Automation Service',\n uri: window.location.origin,\n version: '1',\n chainId:\n chainId ??\n (process.env.NEXT_PUBLIC_NETWORK === 'mainnet' ? 1030 : 71),\n nonce,\n });\n const prepared = message.prepareMessage();\n const signature = await signMessageAsync({ message: prepared });\n\n // 3. Verify with backend → JWT\n const verifyRes = await fetch(`${API_BASE}/auth/verify`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ message: prepared, signature }),\n });\n if (!verifyRes.ok) {\n const body = (await verifyRes.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(body.error ?? 'Verification failed');\n }\n const { token: jwt } = (await verifyRes.json()) as { token: string };\n\n localStorage.setItem(TOKEN_KEY, jwt);\n setToken(jwt);\n setError(null);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : 'Login failed';\n // Don't surface \"User rejected\" as a scary error — just show retry.\n if (msg.includes('getChainId is not a function')) {\n setError(\n 'Wallet connection issue. Please reconnect your wallet or try a different provider.'\n );\n } else {\n setError(\n msg.toLowerCase().includes('rejected')\n ? 'Signature rejected — try again.'\n : msg\n );\n }\n } finally {\n setIsLoading(false);\n }\n }, [address, chainId, signMessageAsync]);\n\n // ── Auto-sign on connect (fires once per address) ─────────────────────────\n useEffect(() => {\n if (\n isConnected &&\n address &&\n !token &&\n !isLoading &&\n autoSignedForRef.current !== address // only once per address\n ) {\n autoSignedForRef.current = address;\n void login();\n }\n }, [isConnected, address, token, isLoading, login]);\n\n // ── Refresh auth (re-sign with the same wallet — used on 401 responses) ──\n const refreshAuth = useCallback(() => {\n setToken(null);\n setError(null);\n localStorage.removeItem(TOKEN_KEY);\n // Reset the guard so the auto-sign effect fires again for this address.\n autoSignedForRef.current = null;\n }, []);\n\n // ── Logout ────────────────────────────────────────────────────────────────\n const logout = useCallback(() => {\n setToken(null);\n setError(null);\n localStorage.removeItem(TOKEN_KEY);\n autoSignedForRef.current = null;\n disconnect();\n }, [disconnect]);\n\n return (\n <AuthContext.Provider\n value={{\n token,\n address: address ?? null,\n isLoading,\n isAuthenticated: Boolean(token && address),\n error,\n login,\n logout,\n refreshAuth,\n }}\n >\n {children}\n </AuthContext.Provider>\n );\n}\n\n// ─── Consumer hook ────────────────────────────────────────────────────────────\n\nexport function useAuthContext(): AuthCtx {\n const ctx = useContext(AuthContext);\n if (!ctx)\n throw new Error('useAuthContext must be used inside <AuthProvider>');\n return ctx;\n}\n\n// ─── Fetch helper ─────────────────────────────────────────────────────────────\n\n/**\n * useAuthFetch — returns a `fetch`-compatible function that:\n * 1. Automatically injects `Authorization: Bearer <token>` on every request.\n * 2. Calls `refreshAuth()` when the server responds with HTTP 401, so the\n * SIWE auto-sign flow re-fires and the user is silently re-authenticated\n * with the same wallet (no manual action required on any device).\n *\n * Usage:\n * const authFetch = useAuthFetch();\n * const res = await authFetch('/api/jobs');\n */\nexport function useAuthFetch(): (\n input: RequestInfo | URL,\n init?: RequestInit\n) => Promise<Response> {\n const { token, refreshAuth } = useAuthContext();\n\n return useCallback(\n async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n const headers = new Headers(init?.headers);\n if (token) headers.set('Authorization', `Bearer ${token}`);\n\n const res = await fetch(input, { ...init, headers });\n\n if (res.status === 401) {\n refreshAuth();\n }\n\n return res;\n },\n [token, refreshAuth]\n );\n}\n","'use client';\n\nimport { useCallback, useEffect, useState } from 'react';\nimport { useAccount, useSwitchChain } from 'wagmi';\n\nexport const IS_MAINNET = process.env.NEXT_PUBLIC_NETWORK === 'mainnet';\nexport const EXPECTED_CHAIN_ID = IS_MAINNET ? 1030 : 71;\nexport const EXPECTED_CHAIN_NAME = IS_MAINNET\n ? 'Conflux eSpace'\n : 'Conflux eSpace Testnet';\n\n// Full EIP-3085 chain parameters for wallet_addEthereumChain fallback.\nconst CHAIN_PARAMS = IS_MAINNET\n ? {\n chainId: '0x406', // 1030\n chainName: 'Conflux eSpace',\n nativeCurrency: { name: 'CFX', symbol: 'CFX', decimals: 18 },\n rpcUrls: ['https://evm.confluxrpc.com'],\n blockExplorerUrls: ['https://evm.confluxscan.org'],\n }\n : {\n chainId: '0x47', // 71\n chainName: 'Conflux eSpace Testnet',\n nativeCurrency: { name: 'CFX', symbol: 'CFX', decimals: 18 },\n rpcUrls: ['https://evmtestnet.confluxrpc.com'],\n blockExplorerUrls: ['https://evmtestnet.confluxscan.org'],\n };\n\nexport function useNetworkSwitch() {\n const { isConnected, chainId } = useAccount();\n const { switchChain, isPending: isSwitching } = useSwitchChain();\n const [switchError, setSwitchError] = useState<string | null>(null);\n\n const isWrongNetwork = isConnected && chainId !== EXPECTED_CHAIN_ID;\n\n useEffect(() => {\n if (!isWrongNetwork) setSwitchError(null);\n }, [isWrongNetwork]);\n\n const handleSwitchNetwork = useCallback(async () => {\n setSwitchError(null);\n try {\n switchChain({ chainId: EXPECTED_CHAIN_ID });\n } catch {\n // Fallback: manually add+switch via EIP-3085\n const provider = (\n window as Window & {\n ethereum?: { request: (a: unknown) => Promise<unknown> };\n }\n ).ethereum;\n if (!provider) {\n setSwitchError(\n 'No wallet found — add the network manually in MetaMask.'\n );\n return;\n }\n try {\n await provider.request({\n method: 'wallet_addEthereumChain',\n params: [CHAIN_PARAMS],\n });\n } catch (addErr: unknown) {\n const msg = addErr instanceof Error ? addErr.message : String(addErr);\n if (!msg.toLowerCase().includes('user rejected')) {\n setSwitchError(`Could not switch: ${msg}`);\n }\n }\n }\n }, [switchChain]);\n\n return { isWrongNetwork, isSwitching, switchError, handleSwitchNetwork };\n}\n","'use client';\n\nimport {\n AlertTriangle,\n Check,\n Copy,\n Loader2,\n Power,\n Wallet,\n} from 'lucide-react';\nimport { useState } from 'react';\nimport { injected, useAccount, useConnect } from 'wagmi';\nimport { useAuthContext } from './auth-context.js';\nimport { EXPECTED_CHAIN_NAME, useNetworkSwitch } from './useNetworkSwitch.js';\n\n// ─── Copy-to-clipboard address chip with inline status dot ───────────────────\ntype SignStatus = 'signed' | 'unsigned' | 'loading';\n\nfunction AddressChip({\n address,\n status,\n}: {\n address: string;\n status: SignStatus;\n}) {\n const [copied, setCopied] = useState(false);\n\n function handleCopy() {\n void navigator.clipboard.writeText(address).then(() => {\n setCopied(true);\n setTimeout(() => setCopied(false), 1800);\n });\n }\n\n const dotEl =\n status === 'loading' ? (\n <svg\n className=\"h-2.5 w-2.5 animate-spin text-slate-400 flex-shrink-0\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n >\n <title>Loading</title>\n <path\n d=\"M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83\"\n strokeLinecap=\"round\"\n />\n </svg>\n ) : status === 'signed' ? (\n <span className=\"h-2 w-2 rounded-full bg-green-400 shadow-[0_0_5px_#4ade80] flex-shrink-0\" />\n ) : (\n <span className=\"h-2 w-2 rounded-full bg-orange-400 shadow-[0_0_5px_#fb923c] flex-shrink-0\" />\n );\n\n return (\n <button\n type=\"button\"\n onClick={handleCopy}\n title={copied ? 'Copied!' : address}\n className=\"group flex items-center gap-1.5 rounded-lg border border-slate-600 bg-slate-800\n px-2.5 py-1 transition-colors hover:border-conflux-500 hover:bg-slate-700\"\n >\n {dotEl}\n <span className=\"font-mono text-xs text-slate-200\">\n {address.slice(0, 6)}…{address.slice(-4)}\n </span>\n {/* copy / check icon */}\n {copied ? (\n <Check\n className=\"h-3 w-3 text-green-400 flex-shrink-0\"\n strokeWidth={3}\n />\n ) : (\n <Copy className=\"h-3 w-3 text-slate-500 group-hover:text-conflux-400 flex-shrink-0 transition-colors\" />\n )}\n </button>\n );\n}\n\nexport function WalletConnect() {\n const { isConnected } = useAccount();\n const { connect } = useConnect();\n const { address, token, isLoading, error, login, logout } = useAuthContext();\n const { isWrongNetwork, isSwitching, switchError, handleSwitchNetwork } =\n useNetworkSwitch();\n\n if (!isConnected) {\n return (\n <button\n type=\"button\"\n onClick={() => connect({ connector: injected() })}\n className=\"group flex items-center gap-2 bg-conflux-600 hover:bg-conflux-500 text-white text-sm font-semibold py-2 px-4 rounded-xl transition-all shadow-[0_0_20px_-5px_rgba(0,120,200,0.5)] hover:shadow-[0_0_25px_-5px_rgba(0,120,200,0.7)]\"\n >\n <Wallet className=\"h-4 w-4 group-hover:scale-110 transition-transform\" />\n Connect Wallet\n </button>\n );\n }\n\n return (\n <div className=\"flex items-center gap-2\">\n {/* Address chip — dot reflects sign status, click to copy */}\n {address && (\n <AddressChip\n address={address}\n status={isLoading ? 'loading' : token ? 'signed' : 'unsigned'}\n />\n )}\n\n {/* Adaptive action button: wrong-network → switch | signing → disabled | unsigned → sign | signed → disconnect */}\n {isWrongNetwork ? (\n <div className=\"flex flex-col items-end gap-1\">\n <button\n type=\"button\"\n onClick={handleSwitchNetwork}\n disabled={isSwitching}\n className=\"flex items-center gap-1.5 text-xs bg-amber-600/20 border border-amber-600/50 hover:bg-amber-600/30\n disabled:opacity-50 text-amber-500 py-1.5 px-3 rounded-lg transition-colors whitespace-nowrap font-medium\"\n title={`Switch wallet to ${EXPECTED_CHAIN_NAME}`}\n >\n <AlertTriangle className=\"h-3.5 w-3.5\" />\n {isSwitching ? 'Switching…' : `Switch to ${EXPECTED_CHAIN_NAME}`}\n </button>\n {switchError && (\n <span className=\"text-xs text-red-400 max-w-[180px] text-right leading-tight\">\n {switchError}\n </span>\n )}\n </div>\n ) : isLoading ? (\n <button\n type=\"button\"\n disabled\n className=\"flex items-center gap-1.5 text-xs border border-slate-700/50 bg-slate-800/50 text-slate-500\n py-1.5 px-3 rounded-lg cursor-not-allowed\"\n >\n <Loader2 className=\"h-3.5 w-3.5 animate-spin text-slate-400\" />\n Signing…\n </button>\n ) : !token ? (\n <button\n type=\"button\"\n onClick={() => void login()}\n title={error ?? 'Sign a message to authenticate'}\n className=\"text-xs bg-conflux-600 hover:bg-conflux-500 text-white py-1.5 px-4 rounded-lg transition-colors font-medium shadow-[0_0_15px_-5px_rgba(0,120,200,0.5)]\"\n >\n Sign In\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={() => logout()}\n title=\"Disconnect wallet\"\n className=\"flex items-center gap-1.5 text-xs border border-slate-700 bg-slate-800 hover:bg-red-500/10 hover:border-red-500/50\n text-slate-400 hover:text-red-400 py-1.5 px-2.5 rounded-lg transition-colors\"\n >\n <Power className=\"h-3.5 w-3.5 flex-shrink-0\" />\n <span className=\"hidden sm:inline font-medium\">Disconnect</span>\n </button>\n )}\n </div>\n );\n}\n","/*\n * Copyright 2025 Conflux DevKit Team\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { getDefaultConfig } from 'connectkit';\nimport { createConfig, http } from 'wagmi';\nimport { mainnet, sepolia } from 'wagmi/chains';\n\n// Define Conflux chains\nexport const confluxCore = {\n id: 1029,\n name: 'Conflux Core',\n network: 'conflux-core',\n nativeCurrency: {\n decimals: 18,\n name: 'CFX',\n symbol: 'CFX',\n },\n rpcUrls: {\n default: {\n http: ['https://main.confluxrpc.com'],\n },\n public: {\n http: ['https://main.confluxrpc.com'],\n },\n },\n blockExplorers: {\n default: { name: 'ConfluxScan', url: 'https://confluxscan.io' },\n },\n} as const;\n\nexport const confluxESpace = {\n id: 1030,\n name: 'Conflux eSpace',\n network: 'conflux-espace',\n nativeCurrency: {\n decimals: 18,\n name: 'CFX',\n symbol: 'CFX',\n },\n rpcUrls: {\n default: {\n http: ['https://evm.confluxrpc.com'],\n },\n public: {\n http: ['https://evm.confluxrpc.com'],\n },\n },\n blockExplorers: {\n default: { name: 'ConfluxScan', url: 'https://evmtestnet.confluxscan.io' },\n },\n} as const;\n\nexport const confluxCoreTestnet = {\n id: 1001,\n name: 'Conflux Core Testnet',\n network: 'conflux-core-testnet',\n nativeCurrency: {\n decimals: 18,\n name: 'CFX',\n symbol: 'CFX',\n },\n rpcUrls: {\n default: {\n http: ['https://test.confluxrpc.com'],\n },\n public: {\n http: ['https://test.confluxrpc.com'],\n },\n },\n blockExplorers: {\n default: { name: 'ConfluxScan', url: 'https://testnet.confluxscan.io' },\n },\n} as const;\n\nexport const confluxESpaceTestnet = {\n id: 71,\n name: 'Conflux eSpace Testnet',\n network: 'conflux-espace-testnet',\n nativeCurrency: {\n decimals: 18,\n name: 'CFX',\n symbol: 'CFX',\n },\n rpcUrls: {\n default: {\n http: ['https://evmtestnet.confluxrpc.com'],\n },\n public: {\n http: ['https://evmtestnet.confluxrpc.com'],\n },\n },\n blockExplorers: {\n default: { name: 'ConfluxScan', url: 'https://evmtestnet.confluxscan.io' },\n },\n} as const;\n\nexport const confluxLocalCore = {\n id: 2029,\n name: 'Conflux Local Core',\n network: 'conflux-local-core',\n nativeCurrency: {\n decimals: 18,\n name: 'CFX',\n symbol: 'CFX',\n },\n rpcUrls: {\n default: {\n http: ['http://localhost:12537'],\n },\n public: {\n http: ['http://localhost:12537'],\n },\n },\n} as const;\n\nexport const confluxLocalESpace = {\n id: 2030,\n name: 'Conflux Local eSpace',\n network: 'conflux-local-espace',\n nativeCurrency: {\n decimals: 18,\n name: 'CFX',\n symbol: 'CFX',\n },\n rpcUrls: {\n default: {\n http: ['http://localhost:8545'],\n },\n public: {\n http: ['http://localhost:8545'],\n },\n },\n} as const;\n\nexport const wagmiConfig = createConfig(\n getDefaultConfig({\n // Prioritize EVM-compatible chains (most wallets support these)\n // Conflux eSpace uses standard EVM wallet infrastructure\n chains: [\n confluxESpace, // Conflux eSpace Mainnet (primary - EVM compatible)\n confluxLocalESpace, // Local dev node eSpace\n confluxESpaceTestnet, // Testnet eSpace\n mainnet, // Ethereum mainnet\n sepolia, // Ethereum testnet\n // Note: Core chains removed from default list as few wallets support non-EVM chains\n // Users can manually add confluxCore via network settings if their wallet supports it\n ],\n transports: {\n [confluxESpace.id]: http(),\n [confluxLocalESpace.id]: http(),\n [confluxESpaceTestnet.id]: http(),\n [mainnet.id]: http(),\n [sepolia.id]: http(),\n },\n walletConnectProjectId:\n process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID ||\n 'conflux-devkit-local',\n appName: 'Conflux DevKit',\n appDescription: 'Development toolkit for Conflux blockchain',\n appUrl: 'https://conflux-devkit.dev',\n appIcon: 'https://conflux-devkit.dev/icon.png',\n // Note: Get a real project ID from https://cloud.walletconnect.com/ for production\n })\n);\n"],"mappings":"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","names":["useCallback","useEffect","useState","useAccount","useState","useAccount","jsx","useState","useAccount"]}