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,603 @@
1
+ import { Address, Hex, LocalAccount } from 'viem';
2
+
3
+ /**
4
+ * Configuration for Kentucky Signer client
5
+ */
6
+ interface KentuckySignerConfig {
7
+ /** Base URL of the Kentucky Signer API (e.g., "https://signer.example.com") */
8
+ baseUrl: string;
9
+ /** Account ID (64-character hex string) */
10
+ accountId: string;
11
+ }
12
+ /**
13
+ * Authenticated session with Kentucky Signer
14
+ */
15
+ interface AuthSession {
16
+ /** JWT access token */
17
+ token: string;
18
+ /** Account ID this session is authenticated for */
19
+ accountId: string;
20
+ /** EVM address derived from the account */
21
+ evmAddress: Address;
22
+ /** Bitcoin address derived from the account */
23
+ btcAddress?: string;
24
+ /** Solana address derived from the account */
25
+ solAddress?: string;
26
+ /** Token expiration timestamp (Unix ms) */
27
+ expiresAt: number;
28
+ }
29
+ /**
30
+ * Challenge response from Kentucky Signer
31
+ */
32
+ interface ChallengeResponse {
33
+ success: boolean;
34
+ challenge: string;
35
+ expires_at: number;
36
+ }
37
+ /**
38
+ * Authentication response from Kentucky Signer
39
+ */
40
+ interface AuthResponse {
41
+ success: boolean;
42
+ token: string;
43
+ expires_in: number;
44
+ account_id: string;
45
+ }
46
+ /**
47
+ * Account info response from Kentucky Signer
48
+ */
49
+ interface AccountInfoResponse {
50
+ success: boolean;
51
+ account_id: string;
52
+ addresses: {
53
+ evm: string;
54
+ bitcoin: string;
55
+ solana: string;
56
+ };
57
+ passkeys: Array<{
58
+ credential_id: string;
59
+ created_at: number;
60
+ }>;
61
+ }
62
+ /**
63
+ * EVM signature response from Kentucky Signer
64
+ */
65
+ interface EvmSignatureResponse {
66
+ success: boolean;
67
+ signature: {
68
+ r: Hex;
69
+ s: Hex;
70
+ v: number;
71
+ full: Hex;
72
+ };
73
+ chain_id: number;
74
+ signer_address: string;
75
+ }
76
+ /**
77
+ * Error response from Kentucky Signer API
78
+ */
79
+ interface ApiErrorResponse {
80
+ success: false;
81
+ error: {
82
+ code: string;
83
+ message: string;
84
+ details?: string;
85
+ };
86
+ }
87
+ /**
88
+ * WebAuthn credential for passkey authentication
89
+ */
90
+ interface PasskeyCredential {
91
+ /** Credential ID (base64url encoded) */
92
+ credentialId: string;
93
+ /** Client data JSON (base64url encoded) */
94
+ clientDataJSON: string;
95
+ /** Authenticator data (base64url encoded) */
96
+ authenticatorData: string;
97
+ /** Signature (base64url encoded) */
98
+ signature: string;
99
+ /** User handle (base64url encoded, optional) */
100
+ userHandle?: string;
101
+ }
102
+ /**
103
+ * Options for passkey authentication
104
+ */
105
+ interface PasskeyAuthOptions {
106
+ /** Base URL of the Kentucky Signer API */
107
+ baseUrl: string;
108
+ /** Account ID to authenticate */
109
+ accountId: string;
110
+ /** Relying Party ID for WebAuthn (defaults to current domain) */
111
+ rpId?: string;
112
+ /** Credential IDs to allow (if known) */
113
+ allowCredentials?: string[];
114
+ }
115
+ /**
116
+ * Token storage interface for custom token persistence
117
+ */
118
+ interface TokenStorage {
119
+ /** Get stored token */
120
+ getToken(): Promise<string | null>;
121
+ /** Store token */
122
+ setToken(token: string, expiresAt: number): Promise<void>;
123
+ /** Clear stored token */
124
+ clearToken(): Promise<void>;
125
+ }
126
+ /**
127
+ * Kentucky Signer client options
128
+ */
129
+ interface ClientOptions {
130
+ /** Base URL of the Kentucky Signer API */
131
+ baseUrl: string;
132
+ /** Custom fetch implementation (for Node.js or testing) */
133
+ fetch?: typeof fetch;
134
+ /** Request timeout in milliseconds (default: 30000) */
135
+ timeout?: number;
136
+ }
137
+ /**
138
+ * Sign EVM transaction request
139
+ */
140
+ interface SignEvmRequest {
141
+ /** Transaction hash to sign (32 bytes, hex encoded) */
142
+ tx_hash: Hex;
143
+ /** Chain ID */
144
+ chain_id: number;
145
+ }
146
+ /**
147
+ * Passkey registration options (for account creation)
148
+ */
149
+ interface PasskeyRegistrationOptions {
150
+ /** Base URL of the Kentucky Signer API */
151
+ baseUrl: string;
152
+ /** Username for the account */
153
+ username?: string;
154
+ /** Relying Party ID for WebAuthn (defaults to current domain) */
155
+ rpId?: string;
156
+ /** Relying Party name for WebAuthn */
157
+ rpName?: string;
158
+ }
159
+ /**
160
+ * Account creation response
161
+ */
162
+ interface AccountCreationResponse {
163
+ success: boolean;
164
+ account_id: string;
165
+ addresses: {
166
+ evm: string;
167
+ bitcoin: string;
168
+ solana: string;
169
+ };
170
+ }
171
+ /**
172
+ * Options for password authentication
173
+ */
174
+ interface PasswordAuthOptions {
175
+ /** Base URL of the Kentucky Signer API */
176
+ baseUrl: string;
177
+ /** Account ID to authenticate */
178
+ accountId: string;
179
+ /** Password for authentication */
180
+ password: string;
181
+ }
182
+ /**
183
+ * Options for creating an account with password
184
+ */
185
+ interface PasswordAccountCreationOptions {
186
+ /** Base URL of the Kentucky Signer API */
187
+ baseUrl: string;
188
+ /** Password for the account (8-128 characters) */
189
+ password: string;
190
+ /** Password confirmation (must match password) */
191
+ confirmation: string;
192
+ }
193
+ /**
194
+ * Password account creation request
195
+ */
196
+ interface CreatePasswordAccountRequest {
197
+ /** Password for the account */
198
+ password: string;
199
+ /** Password confirmation */
200
+ confirmation: string;
201
+ }
202
+ /**
203
+ * Password authentication request
204
+ */
205
+ interface PasswordAuthRequest {
206
+ /** Account ID */
207
+ account_id: string;
208
+ /** Password */
209
+ password: string;
210
+ }
211
+
212
+ /**
213
+ * Options for creating a Kentucky Signer account
214
+ */
215
+ interface KentuckySignerAccountOptions {
216
+ /** Kentucky Signer configuration */
217
+ config: KentuckySignerConfig;
218
+ /** Authenticated session */
219
+ session: AuthSession;
220
+ /** Default chain ID for signing (can be overridden per-transaction) */
221
+ defaultChainId?: number;
222
+ /** Callback when session needs refresh */
223
+ onSessionExpired?: () => Promise<AuthSession>;
224
+ }
225
+ /**
226
+ * Extended account type with Kentucky Signer specific properties
227
+ */
228
+ interface KentuckySignerAccount extends LocalAccount<'kentuckySigner'> {
229
+ /** Account ID */
230
+ accountId: string;
231
+ /** Current session */
232
+ session: AuthSession;
233
+ /** Update the session (e.g., after refresh) */
234
+ updateSession: (session: AuthSession) => void;
235
+ }
236
+ /**
237
+ * Create a custom Viem account backed by Kentucky Signer
238
+ *
239
+ * This account implementation uses the Kentucky Signer API to sign
240
+ * transactions, messages, and typed data using passkey authentication.
241
+ *
242
+ * @param options - Account options
243
+ * @returns Viem-compatible account
244
+ *
245
+ * @example
246
+ * ```typescript
247
+ * const account = createKentuckySignerAccount({
248
+ * config: {
249
+ * baseUrl: 'https://signer.example.com',
250
+ * accountId: '0x...',
251
+ * },
252
+ * session: authenticatedSession,
253
+ * defaultChainId: 1,
254
+ * })
255
+ *
256
+ * const walletClient = createWalletClient({
257
+ * account,
258
+ * chain: mainnet,
259
+ * transport: http(),
260
+ * })
261
+ *
262
+ * const hash = await walletClient.sendTransaction({
263
+ * to: '0x...',
264
+ * value: parseEther('0.1'),
265
+ * })
266
+ * ```
267
+ */
268
+ declare function createKentuckySignerAccount(options: KentuckySignerAccountOptions): KentuckySignerAccount;
269
+ /**
270
+ * Create a Kentucky Signer account for server-side use
271
+ *
272
+ * Convenience function for Node.js environments where you have
273
+ * a pre-existing JWT token.
274
+ *
275
+ * @param baseUrl - Kentucky Signer API URL
276
+ * @param accountId - Account ID
277
+ * @param token - JWT token
278
+ * @param evmAddress - EVM address for the account
279
+ * @param chainId - Default chain ID
280
+ * @returns Kentucky Signer account
281
+ */
282
+ declare function createServerAccount(baseUrl: string, accountId: string, token: string, evmAddress: Address, chainId?: number): KentuckySignerAccount;
283
+
284
+ /**
285
+ * Kentucky Signer API error
286
+ */
287
+ declare class KentuckySignerError extends Error {
288
+ code: string;
289
+ details?: string | undefined;
290
+ constructor(message: string, code: string, details?: string | undefined);
291
+ }
292
+ /**
293
+ * Kentucky Signer API client
294
+ *
295
+ * Handles communication with the Kentucky Signer API for authentication,
296
+ * account management, and transaction signing.
297
+ */
298
+ declare class KentuckySignerClient {
299
+ private baseUrl;
300
+ private fetchImpl;
301
+ private timeout;
302
+ constructor(options: ClientOptions);
303
+ /**
304
+ * Make an authenticated request to the API
305
+ */
306
+ private request;
307
+ /**
308
+ * Get a challenge for passkey authentication
309
+ *
310
+ * @param accountId - Account ID to authenticate
311
+ * @returns Challenge response with 32-byte challenge
312
+ */
313
+ getChallenge(accountId: string): Promise<ChallengeResponse>;
314
+ /**
315
+ * Authenticate with a passkey credential
316
+ *
317
+ * @param accountId - Account ID to authenticate
318
+ * @param credential - WebAuthn credential from navigator.credentials.get()
319
+ * @returns Authentication response with JWT token
320
+ */
321
+ authenticatePasskey(accountId: string, credential: PasskeyCredential): Promise<AuthResponse>;
322
+ /**
323
+ * Refresh an authentication token
324
+ *
325
+ * @param token - Current JWT token
326
+ * @returns New authentication response with fresh token
327
+ */
328
+ refreshToken(token: string): Promise<AuthResponse>;
329
+ /**
330
+ * Logout and invalidate token
331
+ *
332
+ * @param token - JWT token to invalidate
333
+ */
334
+ logout(token: string): Promise<void>;
335
+ /**
336
+ * Get account information
337
+ *
338
+ * @param accountId - Account ID
339
+ * @param token - JWT token
340
+ * @returns Account info with addresses and passkeys
341
+ */
342
+ getAccountInfo(accountId: string, token: string): Promise<AccountInfoResponse>;
343
+ /**
344
+ * Check if an account exists
345
+ *
346
+ * @param accountId - Account ID
347
+ * @param token - JWT token
348
+ * @returns True if account exists
349
+ */
350
+ accountExists(accountId: string, token: string): Promise<boolean>;
351
+ /**
352
+ * Sign an EVM transaction hash
353
+ *
354
+ * @param request - Sign request with tx_hash and chain_id
355
+ * @param token - JWT token
356
+ * @returns Signature response with r, s, v components
357
+ */
358
+ signEvmTransaction(request: SignEvmRequest, token: string): Promise<EvmSignatureResponse>;
359
+ /**
360
+ * Sign a raw hash for EVM
361
+ *
362
+ * Convenience method that wraps signEvmTransaction.
363
+ *
364
+ * @param hash - 32-byte hash to sign (hex encoded with 0x prefix)
365
+ * @param chainId - Chain ID
366
+ * @param token - JWT token
367
+ * @returns Full signature (hex encoded with 0x prefix)
368
+ */
369
+ signHash(hash: Hex, chainId: number, token: string): Promise<Hex>;
370
+ /**
371
+ * Create a new account with passkey authentication
372
+ *
373
+ * @param credential - WebAuthn credential from navigator.credentials.create()
374
+ * @returns Account creation response with account ID and addresses
375
+ */
376
+ createAccountWithPasskey(credential: PasskeyCredential & {
377
+ publicKey: string;
378
+ }): Promise<AccountCreationResponse>;
379
+ /**
380
+ * Create a new account with password authentication
381
+ *
382
+ * @param request - Password and confirmation
383
+ * @returns Account creation response with account ID and addresses
384
+ */
385
+ createAccountWithPassword(request: CreatePasswordAccountRequest): Promise<AccountCreationResponse>;
386
+ /**
387
+ * Authenticate with password
388
+ *
389
+ * @param request - Account ID and password
390
+ * @returns Authentication response with JWT token
391
+ */
392
+ authenticatePassword(request: PasswordAuthRequest): Promise<AuthResponse>;
393
+ /**
394
+ * Health check
395
+ *
396
+ * @returns True if the API is healthy
397
+ */
398
+ healthCheck(): Promise<boolean>;
399
+ /**
400
+ * Get API version
401
+ *
402
+ * @returns Version string
403
+ */
404
+ getVersion(): Promise<string>;
405
+ }
406
+ /**
407
+ * Create a new Kentucky Signer client
408
+ *
409
+ * @param options - Client options
410
+ * @returns Kentucky Signer client instance
411
+ */
412
+ declare function createClient(options: ClientOptions): KentuckySignerClient;
413
+
414
+ /**
415
+ * Check if WebAuthn is available in the current environment
416
+ */
417
+ declare function isWebAuthnAvailable(): boolean;
418
+ /**
419
+ * In-memory token storage (default implementation)
420
+ */
421
+ declare class MemoryTokenStorage implements TokenStorage {
422
+ private token;
423
+ private expiresAt;
424
+ getToken(): Promise<string | null>;
425
+ setToken(token: string, expiresAt: number): Promise<void>;
426
+ clearToken(): Promise<void>;
427
+ }
428
+ /**
429
+ * Browser localStorage token storage
430
+ */
431
+ declare class LocalStorageTokenStorage implements TokenStorage {
432
+ private keyPrefix;
433
+ constructor(keyPrefix?: string);
434
+ getToken(): Promise<string | null>;
435
+ setToken(token: string, expiresAt: number): Promise<void>;
436
+ clearToken(): Promise<void>;
437
+ }
438
+ /**
439
+ * Authenticate with a passkey (browser only)
440
+ *
441
+ * This function handles the full WebAuthn authentication flow:
442
+ * 1. Request a challenge from the Kentucky Signer
443
+ * 2. Prompt the user to authenticate with their passkey
444
+ * 3. Send the credential to the Kentucky Signer
445
+ * 4. Return an authenticated session
446
+ *
447
+ * @param options - Passkey authentication options
448
+ * @returns Authenticated session
449
+ */
450
+ declare function authenticateWithPasskey(options: PasskeyAuthOptions): Promise<AuthSession>;
451
+ /**
452
+ * Create an authenticated session from an existing JWT token (Node.js compatible)
453
+ *
454
+ * Use this for server-side applications where you already have a JWT token.
455
+ *
456
+ * @param baseUrl - Kentucky Signer API URL
457
+ * @param accountId - Account ID
458
+ * @param token - JWT token
459
+ * @param expiresAt - Token expiration timestamp (Unix ms), optional
460
+ * @returns Authenticated session
461
+ */
462
+ declare function authenticateWithToken(baseUrl: string, accountId: string, token: string, expiresAt?: number): Promise<AuthSession>;
463
+ /**
464
+ * Register a new passkey for account creation (browser only)
465
+ *
466
+ * @param options - Registration options
467
+ * @returns Registration credential with public key
468
+ */
469
+ declare function registerPasskey(options: PasskeyRegistrationOptions): Promise<PasskeyCredential & {
470
+ publicKey: string;
471
+ }>;
472
+ /**
473
+ * Check if a session is still valid
474
+ *
475
+ * @param session - Session to check
476
+ * @param bufferMs - Buffer time before expiration (default 60 seconds)
477
+ * @returns True if session is valid
478
+ */
479
+ declare function isSessionValid(session: AuthSession, bufferMs?: number): boolean;
480
+ /**
481
+ * Refresh a session if needed
482
+ *
483
+ * @param session - Current session
484
+ * @param baseUrl - Kentucky Signer API URL
485
+ * @param bufferMs - Buffer time before expiration (default 60 seconds)
486
+ * @returns Updated session (or original if still valid)
487
+ */
488
+ declare function refreshSessionIfNeeded(session: AuthSession, baseUrl: string, bufferMs?: number): Promise<AuthSession>;
489
+ /**
490
+ * Authenticate with password (works in browser and Node.js)
491
+ *
492
+ * @param options - Password authentication options
493
+ * @returns Authenticated session
494
+ *
495
+ * @example
496
+ * ```typescript
497
+ * const session = await authenticateWithPassword({
498
+ * baseUrl: 'https://signer.example.com',
499
+ * accountId: '0123456789abcdef...',
500
+ * password: 'your-secure-password',
501
+ * })
502
+ * ```
503
+ */
504
+ declare function authenticateWithPassword(options: PasswordAuthOptions): Promise<AuthSession>;
505
+ /**
506
+ * Create a new account with password authentication (works in browser and Node.js)
507
+ *
508
+ * @param options - Account creation options with password
509
+ * @returns Account creation response with account ID and addresses
510
+ *
511
+ * @example
512
+ * ```typescript
513
+ * const account = await createAccountWithPassword({
514
+ * baseUrl: 'https://signer.example.com',
515
+ * password: 'your-secure-password',
516
+ * confirmation: 'your-secure-password',
517
+ * })
518
+ * console.log('Account ID:', account.account_id)
519
+ * console.log('EVM Address:', account.addresses.evm)
520
+ * ```
521
+ */
522
+ declare function createAccountWithPassword(options: PasswordAccountCreationOptions): Promise<AccountCreationResponse>;
523
+
524
+ /**
525
+ * Utility functions for Kentucky Signer Viem integration
526
+ */
527
+ /**
528
+ * Base64URL encode a Uint8Array
529
+ *
530
+ * @param data - Data to encode
531
+ * @returns Base64URL encoded string (no padding)
532
+ */
533
+ declare function base64UrlEncode(data: Uint8Array): string;
534
+ /**
535
+ * Base64URL decode a string to Uint8Array
536
+ *
537
+ * @param str - Base64URL encoded string
538
+ * @returns Decoded bytes
539
+ */
540
+ declare function base64UrlDecode(str: string): Uint8Array;
541
+ /**
542
+ * Convert a hex string to Uint8Array
543
+ *
544
+ * @param hex - Hex string (with or without 0x prefix)
545
+ * @returns Byte array
546
+ */
547
+ declare function hexToBytes(hex: string): Uint8Array;
548
+ /**
549
+ * Convert a Uint8Array to hex string
550
+ *
551
+ * @param bytes - Byte array
552
+ * @param withPrefix - Include 0x prefix (default: true)
553
+ * @returns Hex string
554
+ */
555
+ declare function bytesToHex(bytes: Uint8Array, withPrefix?: boolean): string;
556
+ /**
557
+ * Validate an account ID format
558
+ *
559
+ * Account IDs are 64-character hex strings (32 bytes).
560
+ *
561
+ * @param accountId - Account ID to validate
562
+ * @returns True if valid
563
+ */
564
+ declare function isValidAccountId(accountId: string): boolean;
565
+ /**
566
+ * Validate an EVM address format
567
+ *
568
+ * @param address - Address to validate
569
+ * @returns True if valid
570
+ */
571
+ declare function isValidEvmAddress(address: string): boolean;
572
+ /**
573
+ * Retry a function with exponential backoff
574
+ *
575
+ * @param fn - Function to retry
576
+ * @param maxRetries - Maximum number of retries (default: 3)
577
+ * @param baseDelay - Base delay in ms (default: 1000)
578
+ * @returns Result of the function
579
+ */
580
+ declare function withRetry<T>(fn: () => Promise<T>, maxRetries?: number, baseDelay?: number): Promise<T>;
581
+ /**
582
+ * Parse a JWT token (without validation)
583
+ *
584
+ * @param token - JWT token string
585
+ * @returns Decoded payload
586
+ */
587
+ declare function parseJwt(token: string): Record<string, unknown>;
588
+ /**
589
+ * Get JWT expiration time
590
+ *
591
+ * @param token - JWT token string
592
+ * @returns Expiration timestamp in milliseconds, or null if no exp claim
593
+ */
594
+ declare function getJwtExpiration(token: string): number | null;
595
+ /**
596
+ * Format an error for display
597
+ *
598
+ * @param error - Error to format
599
+ * @returns Formatted error message
600
+ */
601
+ declare function formatError(error: unknown): string;
602
+
603
+ export { type AccountCreationResponse, type AccountInfoResponse, type ApiErrorResponse, type AuthResponse, type AuthSession, type ChallengeResponse, type ClientOptions, type CreatePasswordAccountRequest, type EvmSignatureResponse, type KentuckySignerAccount, type KentuckySignerAccountOptions, KentuckySignerClient, type KentuckySignerConfig, KentuckySignerError, LocalStorageTokenStorage, MemoryTokenStorage, type PasskeyAuthOptions, type PasskeyCredential, type PasskeyRegistrationOptions, type PasswordAccountCreationOptions, type PasswordAuthOptions, type PasswordAuthRequest, type SignEvmRequest, type TokenStorage, authenticateWithPasskey, authenticateWithPassword, authenticateWithToken, base64UrlDecode, base64UrlEncode, bytesToHex, createAccountWithPassword, createClient, createKentuckySignerAccount, createServerAccount, formatError, getJwtExpiration, hexToBytes, isSessionValid, isValidAccountId, isValidEvmAddress, isWebAuthnAvailable, parseJwt, refreshSessionIfNeeded, registerPasskey, withRetry };