kentucky-signer-viem 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.
@@ -0,0 +1,791 @@
1
+ // src/react/context.tsx
2
+ import {
3
+ createContext,
4
+ useContext,
5
+ useState,
6
+ useCallback,
7
+ useEffect,
8
+ useMemo
9
+ } from "react";
10
+
11
+ // src/client.ts
12
+ var KentuckySignerError = class extends Error {
13
+ constructor(message, code, details) {
14
+ super(message);
15
+ this.code = code;
16
+ this.details = details;
17
+ this.name = "KentuckySignerError";
18
+ }
19
+ };
20
+ var KentuckySignerClient = class {
21
+ constructor(options) {
22
+ this.baseUrl = options.baseUrl.replace(/\/$/, "");
23
+ this.fetchImpl = options.fetch ?? globalThis.fetch;
24
+ this.timeout = options.timeout ?? 3e4;
25
+ }
26
+ /**
27
+ * Make an authenticated request to the API
28
+ */
29
+ async request(path, options = {}) {
30
+ const { token, ...fetchOptions } = options;
31
+ const headers = {
32
+ "Content-Type": "application/json",
33
+ ...options.headers
34
+ };
35
+ if (token) {
36
+ ;
37
+ headers["Authorization"] = `Bearer ${token}`;
38
+ }
39
+ const controller = new AbortController();
40
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
41
+ try {
42
+ const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
43
+ ...fetchOptions,
44
+ headers,
45
+ signal: controller.signal
46
+ });
47
+ const data = await response.json();
48
+ if (!response.ok || data.success === false) {
49
+ const error = data;
50
+ throw new KentuckySignerError(
51
+ error.error?.message ?? "Unknown error",
52
+ error.error?.code ?? "UNKNOWN_ERROR",
53
+ error.error?.details
54
+ );
55
+ }
56
+ return data;
57
+ } finally {
58
+ clearTimeout(timeoutId);
59
+ }
60
+ }
61
+ /**
62
+ * Get a challenge for passkey authentication
63
+ *
64
+ * @param accountId - Account ID to authenticate
65
+ * @returns Challenge response with 32-byte challenge
66
+ */
67
+ async getChallenge(accountId) {
68
+ return this.request("/api/auth/challenge", {
69
+ method: "POST",
70
+ body: JSON.stringify({ account_id: accountId })
71
+ });
72
+ }
73
+ /**
74
+ * Authenticate with a passkey credential
75
+ *
76
+ * @param accountId - Account ID to authenticate
77
+ * @param credential - WebAuthn credential from navigator.credentials.get()
78
+ * @returns Authentication response with JWT token
79
+ */
80
+ async authenticatePasskey(accountId, credential) {
81
+ return this.request("/api/auth/passkey", {
82
+ method: "POST",
83
+ body: JSON.stringify({
84
+ account_id: accountId,
85
+ credential_id: credential.credentialId,
86
+ client_data_json: credential.clientDataJSON,
87
+ authenticator_data: credential.authenticatorData,
88
+ signature: credential.signature,
89
+ user_handle: credential.userHandle
90
+ })
91
+ });
92
+ }
93
+ /**
94
+ * Refresh an authentication token
95
+ *
96
+ * @param token - Current JWT token
97
+ * @returns New authentication response with fresh token
98
+ */
99
+ async refreshToken(token) {
100
+ return this.request("/api/auth/refresh", {
101
+ method: "POST",
102
+ token
103
+ });
104
+ }
105
+ /**
106
+ * Logout and invalidate token
107
+ *
108
+ * @param token - JWT token to invalidate
109
+ */
110
+ async logout(token) {
111
+ await this.request("/api/auth/logout", {
112
+ method: "POST",
113
+ token
114
+ });
115
+ }
116
+ /**
117
+ * Get account information
118
+ *
119
+ * @param accountId - Account ID
120
+ * @param token - JWT token
121
+ * @returns Account info with addresses and passkeys
122
+ */
123
+ async getAccountInfo(accountId, token) {
124
+ return this.request(`/api/accounts/${accountId}`, {
125
+ method: "GET",
126
+ token
127
+ });
128
+ }
129
+ /**
130
+ * Check if an account exists
131
+ *
132
+ * @param accountId - Account ID
133
+ * @param token - JWT token
134
+ * @returns True if account exists
135
+ */
136
+ async accountExists(accountId, token) {
137
+ try {
138
+ await this.request(`/api/accounts/${accountId}`, {
139
+ method: "HEAD",
140
+ token
141
+ });
142
+ return true;
143
+ } catch {
144
+ return false;
145
+ }
146
+ }
147
+ /**
148
+ * Sign an EVM transaction hash
149
+ *
150
+ * @param request - Sign request with tx_hash and chain_id
151
+ * @param token - JWT token
152
+ * @returns Signature response with r, s, v components
153
+ */
154
+ async signEvmTransaction(request, token) {
155
+ return this.request("/api/sign/evm", {
156
+ method: "POST",
157
+ token,
158
+ body: JSON.stringify(request)
159
+ });
160
+ }
161
+ /**
162
+ * Sign a raw hash for EVM
163
+ *
164
+ * Convenience method that wraps signEvmTransaction.
165
+ *
166
+ * @param hash - 32-byte hash to sign (hex encoded with 0x prefix)
167
+ * @param chainId - Chain ID
168
+ * @param token - JWT token
169
+ * @returns Full signature (hex encoded with 0x prefix)
170
+ */
171
+ async signHash(hash, chainId, token) {
172
+ const response = await this.signEvmTransaction(
173
+ { tx_hash: hash, chain_id: chainId },
174
+ token
175
+ );
176
+ return response.signature.full;
177
+ }
178
+ /**
179
+ * Create a new account with passkey authentication
180
+ *
181
+ * @param credential - WebAuthn credential from navigator.credentials.create()
182
+ * @returns Account creation response with account ID and addresses
183
+ */
184
+ async createAccountWithPasskey(credential) {
185
+ return this.request("/api/accounts/create/passkey", {
186
+ method: "POST",
187
+ body: JSON.stringify({
188
+ credential_id: credential.credentialId,
189
+ public_key: credential.publicKey,
190
+ client_data_json: credential.clientDataJSON,
191
+ authenticator_data: credential.authenticatorData
192
+ })
193
+ });
194
+ }
195
+ /**
196
+ * Create a new account with password authentication
197
+ *
198
+ * @param request - Password and confirmation
199
+ * @returns Account creation response with account ID and addresses
200
+ */
201
+ async createAccountWithPassword(request) {
202
+ return this.request("/api/accounts/create/password", {
203
+ method: "POST",
204
+ body: JSON.stringify(request)
205
+ });
206
+ }
207
+ /**
208
+ * Authenticate with password
209
+ *
210
+ * @param request - Account ID and password
211
+ * @returns Authentication response with JWT token
212
+ */
213
+ async authenticatePassword(request) {
214
+ return this.request("/api/auth/password", {
215
+ method: "POST",
216
+ body: JSON.stringify(request)
217
+ });
218
+ }
219
+ /**
220
+ * Health check
221
+ *
222
+ * @returns True if the API is healthy
223
+ */
224
+ async healthCheck() {
225
+ try {
226
+ const response = await this.request("/api/health", {
227
+ method: "GET"
228
+ });
229
+ return response.status === "ok";
230
+ } catch {
231
+ return false;
232
+ }
233
+ }
234
+ /**
235
+ * Get API version
236
+ *
237
+ * @returns Version string
238
+ */
239
+ async getVersion() {
240
+ const response = await this.request("/api/version", {
241
+ method: "GET"
242
+ });
243
+ return response.version;
244
+ }
245
+ };
246
+
247
+ // src/utils.ts
248
+ function base64UrlEncode(data) {
249
+ let base64;
250
+ if (typeof Buffer !== "undefined") {
251
+ base64 = Buffer.from(data).toString("base64");
252
+ } else {
253
+ const binary = Array.from(data).map((byte) => String.fromCharCode(byte)).join("");
254
+ base64 = btoa(binary);
255
+ }
256
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
257
+ }
258
+ function base64UrlDecode(str) {
259
+ let base64 = str.replace(/-/g, "+").replace(/_/g, "/");
260
+ const padding = (4 - base64.length % 4) % 4;
261
+ base64 += "=".repeat(padding);
262
+ if (typeof Buffer !== "undefined") {
263
+ return new Uint8Array(Buffer.from(base64, "base64"));
264
+ } else {
265
+ const binary = atob(base64);
266
+ const bytes = new Uint8Array(binary.length);
267
+ for (let i = 0; i < binary.length; i++) {
268
+ bytes[i] = binary.charCodeAt(i);
269
+ }
270
+ return bytes;
271
+ }
272
+ }
273
+
274
+ // src/auth.ts
275
+ function isWebAuthnAvailable() {
276
+ return typeof window !== "undefined" && typeof window.PublicKeyCredential !== "undefined" && typeof navigator.credentials !== "undefined";
277
+ }
278
+ var LocalStorageTokenStorage = class {
279
+ constructor(keyPrefix = "kentucky_signer") {
280
+ this.keyPrefix = keyPrefix;
281
+ }
282
+ async getToken() {
283
+ if (typeof localStorage === "undefined") return null;
284
+ const token = localStorage.getItem(`${this.keyPrefix}_token`);
285
+ const expiresAt = localStorage.getItem(`${this.keyPrefix}_expires`);
286
+ if (token && expiresAt && Date.now() < parseInt(expiresAt, 10)) {
287
+ return token;
288
+ }
289
+ await this.clearToken();
290
+ return null;
291
+ }
292
+ async setToken(token, expiresAt) {
293
+ if (typeof localStorage === "undefined") return;
294
+ localStorage.setItem(`${this.keyPrefix}_token`, token);
295
+ localStorage.setItem(`${this.keyPrefix}_expires`, expiresAt.toString());
296
+ }
297
+ async clearToken() {
298
+ if (typeof localStorage === "undefined") return;
299
+ localStorage.removeItem(`${this.keyPrefix}_token`);
300
+ localStorage.removeItem(`${this.keyPrefix}_expires`);
301
+ }
302
+ };
303
+ function credentialToPasskey(credential) {
304
+ const response = credential.response;
305
+ return {
306
+ credentialId: base64UrlEncode(new Uint8Array(credential.rawId)),
307
+ clientDataJSON: base64UrlEncode(new Uint8Array(response.clientDataJSON)),
308
+ authenticatorData: base64UrlEncode(new Uint8Array(response.authenticatorData)),
309
+ signature: base64UrlEncode(new Uint8Array(response.signature)),
310
+ userHandle: response.userHandle ? base64UrlEncode(new Uint8Array(response.userHandle)) : void 0
311
+ };
312
+ }
313
+ async function authenticateWithPasskey(options) {
314
+ if (!isWebAuthnAvailable()) {
315
+ throw new KentuckySignerError(
316
+ "WebAuthn is not available in this environment",
317
+ "WEBAUTHN_NOT_AVAILABLE",
318
+ "Use authenticateWithToken() for server-side authentication"
319
+ );
320
+ }
321
+ const client = new KentuckySignerClient({ baseUrl: options.baseUrl });
322
+ const challengeResponse = await client.getChallenge(options.accountId);
323
+ const challengeBytes = base64UrlDecode(challengeResponse.challenge);
324
+ const credentialRequestOptions = {
325
+ publicKey: {
326
+ challenge: challengeBytes.buffer,
327
+ timeout: 6e4,
328
+ rpId: options.rpId ?? window.location.hostname,
329
+ userVerification: "preferred",
330
+ allowCredentials: options.allowCredentials?.map((id) => ({
331
+ type: "public-key",
332
+ id: base64UrlDecode(id).buffer
333
+ }))
334
+ }
335
+ };
336
+ const credential = await navigator.credentials.get(
337
+ credentialRequestOptions
338
+ );
339
+ if (!credential) {
340
+ throw new KentuckySignerError(
341
+ "User cancelled passkey authentication",
342
+ "USER_CANCELLED"
343
+ );
344
+ }
345
+ const passkeyCredential = credentialToPasskey(credential);
346
+ const authResponse = await client.authenticatePasskey(
347
+ options.accountId,
348
+ passkeyCredential
349
+ );
350
+ const accountInfo = await client.getAccountInfo(
351
+ options.accountId,
352
+ authResponse.token
353
+ );
354
+ const expiresAt = Date.now() + authResponse.expires_in * 1e3;
355
+ return {
356
+ token: authResponse.token,
357
+ accountId: options.accountId,
358
+ evmAddress: accountInfo.addresses.evm,
359
+ btcAddress: accountInfo.addresses.bitcoin,
360
+ solAddress: accountInfo.addresses.solana,
361
+ expiresAt
362
+ };
363
+ }
364
+ function isSessionValid(session, bufferMs = 6e4) {
365
+ return Date.now() + bufferMs < session.expiresAt;
366
+ }
367
+ async function refreshSessionIfNeeded(session, baseUrl, bufferMs = 6e4) {
368
+ if (isSessionValid(session, bufferMs)) {
369
+ return session;
370
+ }
371
+ const client = new KentuckySignerClient({ baseUrl });
372
+ const authResponse = await client.refreshToken(session.token);
373
+ return {
374
+ ...session,
375
+ token: authResponse.token,
376
+ expiresAt: Date.now() + authResponse.expires_in * 1e3
377
+ };
378
+ }
379
+
380
+ // src/account.ts
381
+ import {
382
+ hashMessage,
383
+ hashTypedData,
384
+ keccak256,
385
+ serializeTransaction
386
+ } from "viem";
387
+ import { toAccount } from "viem/accounts";
388
+ function createKentuckySignerAccount(options) {
389
+ const { config, defaultChainId = 1, onSessionExpired } = options;
390
+ let session = options.session;
391
+ const client = new KentuckySignerClient({ baseUrl: config.baseUrl });
392
+ async function getToken() {
393
+ if (Date.now() + 6e4 >= session.expiresAt) {
394
+ if (onSessionExpired) {
395
+ session = await onSessionExpired();
396
+ } else {
397
+ throw new KentuckySignerError(
398
+ "Session expired",
399
+ "SESSION_EXPIRED",
400
+ "Please re-authenticate with your passkey"
401
+ );
402
+ }
403
+ }
404
+ return session.token;
405
+ }
406
+ async function signHash(hash, chainId) {
407
+ const token = await getToken();
408
+ const response = await client.signEvmTransaction(
409
+ { tx_hash: hash, chain_id: chainId },
410
+ token
411
+ );
412
+ return response.signature.full;
413
+ }
414
+ function parseSignature(signature) {
415
+ const r = `0x${signature.slice(2, 66)}`;
416
+ const s = `0x${signature.slice(66, 130)}`;
417
+ const v = BigInt(`0x${signature.slice(130, 132)}`);
418
+ return { r, s, v };
419
+ }
420
+ const account = toAccount({
421
+ address: session.evmAddress,
422
+ /**
423
+ * Sign a message
424
+ *
425
+ * Supports string messages, hex messages, and raw bytes.
426
+ */
427
+ async signMessage({ message }) {
428
+ const messageHash = hashMessage(message);
429
+ return signHash(messageHash, defaultChainId);
430
+ },
431
+ /**
432
+ * Sign a transaction
433
+ *
434
+ * Serializes the transaction, hashes it, signs via Kentucky Signer,
435
+ * and returns the signed serialized transaction.
436
+ */
437
+ async signTransaction(transaction) {
438
+ const chainId = transaction.chainId ?? defaultChainId;
439
+ const serializedUnsigned = serializeTransaction(transaction);
440
+ const txHash = keccak256(serializedUnsigned);
441
+ const signature = await signHash(txHash, chainId);
442
+ const { r, s, v } = parseSignature(signature);
443
+ let yParity;
444
+ if (transaction.type === "eip1559" || transaction.type === "eip2930" || transaction.type === "eip4844" || transaction.type === "eip7702") {
445
+ yParity = Number(v) - 27;
446
+ } else {
447
+ yParity = Number(v);
448
+ }
449
+ const serializedSigned = serializeTransaction(transaction, {
450
+ r,
451
+ s,
452
+ v: BigInt(yParity),
453
+ yParity
454
+ });
455
+ return serializedSigned;
456
+ },
457
+ /**
458
+ * Sign typed data (EIP-712)
459
+ */
460
+ async signTypedData(typedData) {
461
+ const hash = hashTypedData(typedData);
462
+ return signHash(hash, defaultChainId);
463
+ }
464
+ });
465
+ account.source = "kentuckySigner";
466
+ account.accountId = config.accountId;
467
+ account.session = session;
468
+ account.updateSession = (newSession) => {
469
+ session = newSession;
470
+ if (newSession.evmAddress !== account.address) {
471
+ ;
472
+ account.address = newSession.evmAddress;
473
+ }
474
+ };
475
+ return account;
476
+ }
477
+
478
+ // src/react/context.tsx
479
+ import { jsx } from "react/jsx-runtime";
480
+ var KentuckySignerContext = createContext(null);
481
+ function KentuckySignerProvider({
482
+ baseUrl,
483
+ defaultChainId = 1,
484
+ storageKeyPrefix = "kentucky_signer",
485
+ persistSession = true,
486
+ children
487
+ }) {
488
+ const [state, setState] = useState({
489
+ isAuthenticating: false,
490
+ isAuthenticated: false,
491
+ session: null,
492
+ account: null,
493
+ error: null
494
+ });
495
+ const client = useMemo(
496
+ () => new KentuckySignerClient({ baseUrl }),
497
+ [baseUrl]
498
+ );
499
+ const storage = useMemo(
500
+ () => persistSession ? new LocalStorageTokenStorage(storageKeyPrefix) : null,
501
+ [persistSession, storageKeyPrefix]
502
+ );
503
+ const createAccount = useCallback(
504
+ (session) => {
505
+ return createKentuckySignerAccount({
506
+ config: { baseUrl, accountId: session.accountId },
507
+ session,
508
+ defaultChainId,
509
+ onSessionExpired: async () => {
510
+ const newSession = await refreshSessionIfNeeded(session, baseUrl, 0);
511
+ setState((s) => ({
512
+ ...s,
513
+ session: newSession,
514
+ account: s.account ? { ...s.account, session: newSession } : null
515
+ }));
516
+ return newSession;
517
+ }
518
+ });
519
+ },
520
+ [baseUrl, defaultChainId]
521
+ );
522
+ useEffect(() => {
523
+ async function restoreSession() {
524
+ if (!storage) return;
525
+ try {
526
+ const savedSession = localStorage.getItem(`${storageKeyPrefix}_session`);
527
+ if (!savedSession) return;
528
+ const session = JSON.parse(savedSession);
529
+ if (!isSessionValid(session)) {
530
+ try {
531
+ const refreshed = await refreshSessionIfNeeded(session, baseUrl, 0);
532
+ const account2 = createAccount(refreshed);
533
+ localStorage.setItem(
534
+ `${storageKeyPrefix}_session`,
535
+ JSON.stringify(refreshed)
536
+ );
537
+ setState({
538
+ isAuthenticating: false,
539
+ isAuthenticated: true,
540
+ session: refreshed,
541
+ account: account2,
542
+ error: null
543
+ });
544
+ } catch {
545
+ localStorage.removeItem(`${storageKeyPrefix}_session`);
546
+ }
547
+ return;
548
+ }
549
+ const account = createAccount(session);
550
+ setState({
551
+ isAuthenticating: false,
552
+ isAuthenticated: true,
553
+ session,
554
+ account,
555
+ error: null
556
+ });
557
+ } catch {
558
+ localStorage.removeItem(`${storageKeyPrefix}_session`);
559
+ }
560
+ }
561
+ restoreSession();
562
+ }, [storage, storageKeyPrefix, baseUrl, createAccount]);
563
+ const authenticate = useCallback(
564
+ async (accountId, options) => {
565
+ setState((s) => ({ ...s, isAuthenticating: true, error: null }));
566
+ try {
567
+ const session = await authenticateWithPasskey({
568
+ baseUrl,
569
+ accountId,
570
+ rpId: options?.rpId
571
+ });
572
+ const account = createAccount(session);
573
+ if (storage) {
574
+ localStorage.setItem(
575
+ `${storageKeyPrefix}_session`,
576
+ JSON.stringify(session)
577
+ );
578
+ }
579
+ setState({
580
+ isAuthenticating: false,
581
+ isAuthenticated: true,
582
+ session,
583
+ account,
584
+ error: null
585
+ });
586
+ } catch (error) {
587
+ setState((s) => ({
588
+ ...s,
589
+ isAuthenticating: false,
590
+ error
591
+ }));
592
+ throw error;
593
+ }
594
+ },
595
+ [baseUrl, createAccount, storage, storageKeyPrefix]
596
+ );
597
+ const logout = useCallback(async () => {
598
+ try {
599
+ if (state.session) {
600
+ await client.logout(state.session.token);
601
+ }
602
+ } catch {
603
+ }
604
+ if (storage) {
605
+ localStorage.removeItem(`${storageKeyPrefix}_session`);
606
+ }
607
+ setState({
608
+ isAuthenticating: false,
609
+ isAuthenticated: false,
610
+ session: null,
611
+ account: null,
612
+ error: null
613
+ });
614
+ }, [client, state.session, storage, storageKeyPrefix]);
615
+ const refreshSession = useCallback(async () => {
616
+ if (!state.session) return;
617
+ try {
618
+ const refreshed = await refreshSessionIfNeeded(state.session, baseUrl);
619
+ if (refreshed !== state.session) {
620
+ const account = createAccount(refreshed);
621
+ if (storage) {
622
+ localStorage.setItem(
623
+ `${storageKeyPrefix}_session`,
624
+ JSON.stringify(refreshed)
625
+ );
626
+ }
627
+ setState((s) => ({
628
+ ...s,
629
+ session: refreshed,
630
+ account
631
+ }));
632
+ }
633
+ } catch (error) {
634
+ setState((s) => ({ ...s, error }));
635
+ }
636
+ }, [state.session, baseUrl, createAccount, storage, storageKeyPrefix]);
637
+ const clearError = useCallback(() => {
638
+ setState((s) => ({ ...s, error: null }));
639
+ }, []);
640
+ const value = useMemo(
641
+ () => ({
642
+ ...state,
643
+ authenticate,
644
+ logout,
645
+ refreshSession,
646
+ clearError
647
+ }),
648
+ [state, authenticate, logout, refreshSession, clearError]
649
+ );
650
+ return /* @__PURE__ */ jsx(KentuckySignerContext.Provider, { value, children });
651
+ }
652
+ function useKentuckySignerContext() {
653
+ const context = useContext(KentuckySignerContext);
654
+ if (!context) {
655
+ throw new Error(
656
+ "useKentuckySignerContext must be used within a KentuckySignerProvider"
657
+ );
658
+ }
659
+ return context;
660
+ }
661
+
662
+ // src/react/hooks.ts
663
+ import { useMemo as useMemo2, useCallback as useCallback2, useState as useState2 } from "react";
664
+ import { createWalletClient, http } from "viem";
665
+ function useKentuckySigner() {
666
+ const context = useKentuckySignerContext();
667
+ return {
668
+ isAuthenticated: context.isAuthenticated,
669
+ isAuthenticating: context.isAuthenticating,
670
+ session: context.session,
671
+ account: context.account,
672
+ error: context.error,
673
+ authenticate: context.authenticate,
674
+ logout: context.logout,
675
+ refreshSession: context.refreshSession,
676
+ clearError: context.clearError
677
+ };
678
+ }
679
+ function useKentuckySignerAccount() {
680
+ const { account } = useKentuckySignerContext();
681
+ return account;
682
+ }
683
+ function useWalletClient(options) {
684
+ const { account } = useKentuckySignerContext();
685
+ const { chain, rpcUrl } = options;
686
+ return useMemo2(() => {
687
+ if (!account) return null;
688
+ return createWalletClient({
689
+ account,
690
+ chain,
691
+ transport: http(rpcUrl)
692
+ });
693
+ }, [account, chain, rpcUrl]);
694
+ }
695
+ function usePasskeyAuth() {
696
+ const { authenticate, isAuthenticating, error, clearError } = useKentuckySignerContext();
697
+ const login = useCallback2(
698
+ async (accountId, options) => {
699
+ clearError();
700
+ await authenticate(accountId, options);
701
+ },
702
+ [authenticate, clearError]
703
+ );
704
+ return {
705
+ login,
706
+ isLoading: isAuthenticating,
707
+ error,
708
+ clearError
709
+ };
710
+ }
711
+ function useSignMessage() {
712
+ const { account } = useKentuckySignerContext();
713
+ const [isLoading, setIsLoading] = useState2(false);
714
+ const [error, setError] = useState2(null);
715
+ const signMessage = useCallback2(
716
+ async (message) => {
717
+ if (!account) {
718
+ throw new Error("Not authenticated");
719
+ }
720
+ setIsLoading(true);
721
+ setError(null);
722
+ try {
723
+ const signature = await account.signMessage({ message });
724
+ return signature;
725
+ } catch (err) {
726
+ setError(err);
727
+ throw err;
728
+ } finally {
729
+ setIsLoading(false);
730
+ }
731
+ },
732
+ [account]
733
+ );
734
+ return {
735
+ signMessage,
736
+ isLoading,
737
+ error,
738
+ isAvailable: !!account
739
+ };
740
+ }
741
+ function useSignTypedData() {
742
+ const { account } = useKentuckySignerContext();
743
+ const [isLoading, setIsLoading] = useState2(false);
744
+ const [error, setError] = useState2(null);
745
+ const signTypedData = useCallback2(
746
+ async (typedData) => {
747
+ if (!account) {
748
+ throw new Error("Not authenticated");
749
+ }
750
+ setIsLoading(true);
751
+ setError(null);
752
+ try {
753
+ const signature = await account.signTypedData(typedData);
754
+ return signature;
755
+ } catch (err) {
756
+ setError(err);
757
+ throw err;
758
+ } finally {
759
+ setIsLoading(false);
760
+ }
761
+ },
762
+ [account]
763
+ );
764
+ return {
765
+ signTypedData,
766
+ isLoading,
767
+ error,
768
+ isAvailable: !!account
769
+ };
770
+ }
771
+ function useIsReady() {
772
+ const { isAuthenticated, account } = useKentuckySignerContext();
773
+ return isAuthenticated && account !== null;
774
+ }
775
+ function useAddress() {
776
+ const { account } = useKentuckySignerContext();
777
+ return account?.address;
778
+ }
779
+ export {
780
+ KentuckySignerProvider,
781
+ useAddress,
782
+ useIsReady,
783
+ useKentuckySigner,
784
+ useKentuckySignerAccount,
785
+ useKentuckySignerContext,
786
+ usePasskeyAuth,
787
+ useSignMessage,
788
+ useSignTypedData,
789
+ useWalletClient
790
+ };
791
+ //# sourceMappingURL=index.mjs.map