kentucky-signer-viem 0.1.3 → 0.1.5
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/README.md +262 -26
- package/dist/index.d.mts +376 -10
- package/dist/index.d.ts +376 -10
- package/dist/index.js +281 -25
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +282 -26
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.d.mts +465 -3
- package/dist/react/index.d.ts +465 -3
- package/dist/react/index.js +390 -25
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +391 -26
- package/dist/react/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/account.ts +183 -23
- package/src/client.ts +4 -5
- package/src/index.ts +32 -0
- package/src/intent.ts +167 -0
- package/src/react/index.ts +33 -0
- package/src/react/relayer-hooks.ts +318 -0
- package/src/relayer-client.ts +305 -0
- package/src/secure-client.ts +2 -2
- package/src/types.ts +4 -3
package/README.md
CHANGED
|
@@ -15,6 +15,9 @@ A custom Viem account integration for the Kentucky Signer service, enabling EVM
|
|
|
15
15
|
- **React Integration** - Hooks and context for easy React app integration
|
|
16
16
|
- **TypeScript Support** - Full type definitions included
|
|
17
17
|
- **Session Management** - Automatic refresh and persistence options
|
|
18
|
+
- **EIP-7702 Support** - Sign authorizations for EOA code delegation
|
|
19
|
+
- **Relayer Integration** - Client for gasless transactions via relayer service
|
|
20
|
+
- **Intent Signing** - Sign execution intents for smart account operations
|
|
18
21
|
|
|
19
22
|
## Installation
|
|
20
23
|
|
|
@@ -204,6 +207,205 @@ const account = createKentuckySignerAccount({
|
|
|
204
207
|
})
|
|
205
208
|
```
|
|
206
209
|
|
|
210
|
+
## EIP-7702 Authorization
|
|
211
|
+
|
|
212
|
+
Sign authorizations to delegate your EOA's code to a smart contract, enabling features like batching and gas sponsorship.
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
import { createPublicClient, http } from 'viem'
|
|
216
|
+
import { arbitrum } from 'viem/chains'
|
|
217
|
+
|
|
218
|
+
const publicClient = createPublicClient({
|
|
219
|
+
chain: arbitrum,
|
|
220
|
+
transport: http(),
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
// Get current transaction count for the account
|
|
224
|
+
const txNonce = await publicClient.getTransactionCount({
|
|
225
|
+
address: account.address,
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
// Sign EIP-7702 authorization
|
|
229
|
+
const authorization = await account.sign7702Authorization({
|
|
230
|
+
contractAddress: '0x...', // Smart account delegate contract
|
|
231
|
+
chainId: 42161, // Arbitrum
|
|
232
|
+
}, BigInt(txNonce))
|
|
233
|
+
|
|
234
|
+
// Result:
|
|
235
|
+
// {
|
|
236
|
+
// chainId: 42161,
|
|
237
|
+
// contractAddress: '0x...',
|
|
238
|
+
// nonce: 0n,
|
|
239
|
+
// yParity: 0,
|
|
240
|
+
// r: '0x...',
|
|
241
|
+
// s: '0x...'
|
|
242
|
+
// }
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
The authorization can be included in an EIP-7702 transaction's `authorizationList` to delegate the EOA.
|
|
246
|
+
|
|
247
|
+
## Intent Signing for Relayed Execution
|
|
248
|
+
|
|
249
|
+
Sign execution intents for gasless transactions via a relayer.
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
import {
|
|
253
|
+
createExecutionIntent,
|
|
254
|
+
signIntent,
|
|
255
|
+
RelayerClient,
|
|
256
|
+
} from 'kentucky-signer-viem'
|
|
257
|
+
import { encodeFunctionData, parseEther } from 'viem'
|
|
258
|
+
|
|
259
|
+
// Create relayer client
|
|
260
|
+
const relayer = new RelayerClient({
|
|
261
|
+
baseUrl: 'https://relayer.example.com',
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
// Get current nonce from the delegate contract
|
|
265
|
+
const nonce = await relayer.getNonce(42161, account.address)
|
|
266
|
+
|
|
267
|
+
// Create an execution intent
|
|
268
|
+
const intent = createExecutionIntent({
|
|
269
|
+
nonce,
|
|
270
|
+
target: '0x...', // Contract to call
|
|
271
|
+
value: parseEther('0.1'), // ETH to send (optional)
|
|
272
|
+
data: encodeFunctionData({
|
|
273
|
+
abi: [...],
|
|
274
|
+
functionName: 'transfer',
|
|
275
|
+
args: ['0x...', 1000n],
|
|
276
|
+
}),
|
|
277
|
+
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour (optional)
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
// Sign the intent
|
|
281
|
+
const signedIntent = await signIntent(account, intent)
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Relayer Client
|
|
285
|
+
|
|
286
|
+
The `RelayerClient` communicates with a relayer service to submit transactions on behalf of users.
|
|
287
|
+
|
|
288
|
+
### Basic Usage
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
import { RelayerClient, createRelayerClient } from 'kentucky-signer-viem'
|
|
292
|
+
|
|
293
|
+
// Create client
|
|
294
|
+
const relayer = new RelayerClient({
|
|
295
|
+
baseUrl: 'https://relayer.example.com',
|
|
296
|
+
timeout: 30000, // Optional, default 30s
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
// Or use the factory function
|
|
300
|
+
const relayer = createRelayerClient('https://relayer.example.com')
|
|
301
|
+
|
|
302
|
+
// Check health
|
|
303
|
+
const health = await relayer.health()
|
|
304
|
+
// { status: 'ok', relayer: '0x...', timestamp: '2024-...' }
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Get Nonce
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
const nonce = await relayer.getNonce(42161, account.address)
|
|
311
|
+
// Returns bigint nonce from the delegate contract
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Estimate Gas
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
const estimate = await relayer.estimate(42161, account.address, intent)
|
|
318
|
+
// {
|
|
319
|
+
// gasEstimate: '150000',
|
|
320
|
+
// gasCostWei: '30000000000000',
|
|
321
|
+
// sponsoredAvailable: true,
|
|
322
|
+
// tokenOptions: [
|
|
323
|
+
// { token: '0x...', symbol: 'USDC', estimatedFee: '0.05', feePercentage: 5 }
|
|
324
|
+
// ]
|
|
325
|
+
// }
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Relay Transaction
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
// Sponsored (relayer pays gas)
|
|
332
|
+
const result = await relayer.relay(
|
|
333
|
+
42161,
|
|
334
|
+
account.address,
|
|
335
|
+
signedIntent,
|
|
336
|
+
'sponsored'
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
// Pay with ERC20 token
|
|
340
|
+
const result = await relayer.relay(
|
|
341
|
+
42161,
|
|
342
|
+
account.address,
|
|
343
|
+
signedIntent,
|
|
344
|
+
{ token: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831' } // USDC on Arbitrum
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
if (result.success) {
|
|
348
|
+
console.log('TX Hash:', result.txHash)
|
|
349
|
+
} else {
|
|
350
|
+
console.error('Failed:', result.error)
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### Gasless Onboarding
|
|
355
|
+
|
|
356
|
+
For users with zero ETH, combine EIP-7702 authorization with relay to delegate and execute in a single transaction:
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
import { createPublicClient, http } from 'viem'
|
|
360
|
+
import { arbitrum } from 'viem/chains'
|
|
361
|
+
|
|
362
|
+
const publicClient = createPublicClient({
|
|
363
|
+
chain: arbitrum,
|
|
364
|
+
transport: http(),
|
|
365
|
+
})
|
|
366
|
+
|
|
367
|
+
// Get current nonce
|
|
368
|
+
const txNonce = await publicClient.getTransactionCount({
|
|
369
|
+
address: account.address,
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
// Sign EIP-7702 authorization to delegate EOA
|
|
373
|
+
const authorization = await account.sign7702Authorization({
|
|
374
|
+
contractAddress: delegateAddress,
|
|
375
|
+
chainId: 42161,
|
|
376
|
+
}, BigInt(txNonce))
|
|
377
|
+
|
|
378
|
+
// Create and sign execution intent
|
|
379
|
+
const nonce = await relayer.getNonce(42161, account.address)
|
|
380
|
+
const intent = createExecutionIntent({
|
|
381
|
+
nonce,
|
|
382
|
+
target: '0x...',
|
|
383
|
+
data: '0x...',
|
|
384
|
+
})
|
|
385
|
+
const signedIntent = await signIntent(account, intent)
|
|
386
|
+
|
|
387
|
+
// Relay with authorization - delegates EOA and executes in one tx
|
|
388
|
+
const result = await relayer.relay(
|
|
389
|
+
42161,
|
|
390
|
+
account.address,
|
|
391
|
+
signedIntent,
|
|
392
|
+
'sponsored',
|
|
393
|
+
authorization // Include EIP-7702 authorization
|
|
394
|
+
)
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Check Transaction Status
|
|
398
|
+
|
|
399
|
+
```typescript
|
|
400
|
+
const status = await relayer.getStatus(42161, txHash)
|
|
401
|
+
// {
|
|
402
|
+
// status: 'confirmed', // 'pending' | 'confirmed' | 'failed'
|
|
403
|
+
// txHash: '0x...',
|
|
404
|
+
// blockNumber: 12345678,
|
|
405
|
+
// gasUsed: '120000'
|
|
406
|
+
// }
|
|
407
|
+
```
|
|
408
|
+
|
|
207
409
|
## Two-Factor Authentication
|
|
208
410
|
|
|
209
411
|
### Setup TOTP (Authenticator App)
|
|
@@ -270,32 +472,39 @@ Set up trusted guardians for account recovery:
|
|
|
270
472
|
```typescript
|
|
271
473
|
const client = new KentuckySignerClient({ baseUrl })
|
|
272
474
|
|
|
273
|
-
// Add a guardian
|
|
274
|
-
await client.addGuardian(
|
|
275
|
-
|
|
475
|
+
// Add a guardian (requires WebAuthn attestation from guardian's device)
|
|
476
|
+
const { guardian_index, guardian_count } = await client.addGuardian({
|
|
477
|
+
attestation_object: guardianAttestationBase64url,
|
|
276
478
|
label: 'My Friend',
|
|
277
479
|
}, token)
|
|
278
480
|
|
|
279
481
|
// List guardians
|
|
280
|
-
const { guardians } = await client.getGuardians(
|
|
482
|
+
const { guardians } = await client.getGuardians(token)
|
|
483
|
+
// guardians: [{ index: 1, label: 'My Friend' }, ...]
|
|
484
|
+
|
|
485
|
+
// Initiate recovery (when locked out - register new passkey first)
|
|
486
|
+
const recovery = await client.initiateRecovery(
|
|
487
|
+
accountId,
|
|
488
|
+
newPasskeyAttestationObject,
|
|
489
|
+
'New Owner Passkey'
|
|
490
|
+
)
|
|
491
|
+
// Returns: { challenges, guardian_count, threshold, timelock_seconds }
|
|
281
492
|
|
|
282
|
-
//
|
|
283
|
-
|
|
493
|
+
// Guardian signs their challenge with their passkey
|
|
494
|
+
await client.verifyGuardian({
|
|
284
495
|
account_id: accountId,
|
|
285
|
-
|
|
496
|
+
guardian_index: 1,
|
|
497
|
+
authenticator_data: authDataBase64url,
|
|
498
|
+
client_data_json: clientDataBase64url,
|
|
499
|
+
signature: signatureBase64url,
|
|
286
500
|
})
|
|
287
501
|
|
|
288
|
-
//
|
|
289
|
-
await client.
|
|
290
|
-
|
|
291
|
-
guardian_account_id: guardianId,
|
|
292
|
-
signature: guardianSignature,
|
|
293
|
-
}, guardianToken)
|
|
502
|
+
// Check recovery status
|
|
503
|
+
const status = await client.getRecoveryStatus(accountId)
|
|
504
|
+
// { verified_count, threshold, can_complete, timelock_remaining }
|
|
294
505
|
|
|
295
|
-
// Complete recovery after threshold met
|
|
296
|
-
await client.completeRecovery(
|
|
297
|
-
recovery_id: recovery.recovery_id,
|
|
298
|
-
}, token)
|
|
506
|
+
// Complete recovery after threshold met and timelock expired
|
|
507
|
+
await client.completeRecovery(accountId)
|
|
299
508
|
```
|
|
300
509
|
|
|
301
510
|
## API Reference
|
|
@@ -311,6 +520,16 @@ await client.completeRecovery({
|
|
|
311
520
|
| `createAccountWithPassword(options)` | Create new account with password |
|
|
312
521
|
| `authenticateWithToken(...)` | Create session from JWT token |
|
|
313
522
|
|
|
523
|
+
### Intent & Relayer Functions
|
|
524
|
+
|
|
525
|
+
| Function | Description |
|
|
526
|
+
|----------|-------------|
|
|
527
|
+
| `createExecutionIntent(params)` | Create an execution intent for relayed execution |
|
|
528
|
+
| `signIntent(account, intent)` | Sign an execution intent |
|
|
529
|
+
| `signBatchIntents(account, intents)` | Sign multiple intents for batch execution |
|
|
530
|
+
| `hashIntent(intent)` | Compute the hash of an execution intent |
|
|
531
|
+
| `createRelayerClient(baseUrl)` | Create a relayer client |
|
|
532
|
+
|
|
314
533
|
### React Hooks
|
|
315
534
|
|
|
316
535
|
| Hook | Description |
|
|
@@ -328,8 +547,9 @@ await client.completeRecovery({
|
|
|
328
547
|
|
|
329
548
|
#### Authentication
|
|
330
549
|
- `getChallenge(accountId)` - Get WebAuthn challenge
|
|
331
|
-
- `authenticatePasskey(accountId, credential)` - Authenticate with passkey
|
|
332
|
-
- `authenticatePassword(
|
|
550
|
+
- `authenticatePasskey(accountId, credential, ephemeralPublicKey?)` - Authenticate with passkey
|
|
551
|
+
- `authenticatePassword(request)` - Authenticate with password (`{ account_id, password }`)
|
|
552
|
+
- `refreshToken(token)` - Refresh JWT token
|
|
333
553
|
- `logout(token)` - Invalidate session
|
|
334
554
|
|
|
335
555
|
#### Signing
|
|
@@ -338,9 +558,10 @@ await client.completeRecovery({
|
|
|
338
558
|
|
|
339
559
|
#### Account Management
|
|
340
560
|
- `getAccountInfo(accountId, token)` - Get account info
|
|
561
|
+
- `getAccountInfoExtended(accountId, token)` - Get account info with auth config
|
|
341
562
|
- `addPassword(accountId, request, token)` - Add password auth
|
|
342
563
|
- `addPasskey(accountId, request, token)` - Add passkey
|
|
343
|
-
- `removePasskey(accountId,
|
|
564
|
+
- `removePasskey(accountId, passkeyIndex, token)` - Remove passkey by index
|
|
344
565
|
|
|
345
566
|
#### Two-Factor Authentication
|
|
346
567
|
- `get2FAStatus(token)` - Get 2FA status
|
|
@@ -350,13 +571,28 @@ await client.completeRecovery({
|
|
|
350
571
|
- `setupPIN(pin, token)` - Setup PIN
|
|
351
572
|
- `disablePIN(pin, token)` - Disable PIN
|
|
352
573
|
|
|
574
|
+
### Account Methods
|
|
575
|
+
|
|
576
|
+
#### EIP-7702 Authorization
|
|
577
|
+
- `account.sign7702Authorization(params, nonce)` - Sign authorization to delegate EOA code
|
|
578
|
+
|
|
579
|
+
### Relayer Client Methods
|
|
580
|
+
|
|
581
|
+
- `health()` - Check relayer health
|
|
582
|
+
- `getNonce(chainId, address)` - Get account nonce from delegate contract
|
|
583
|
+
- `estimate(chainId, address, intent)` - Estimate gas and fees
|
|
584
|
+
- `relay(chainId, address, signedIntent, paymentMode, authorization?)` - Submit transaction
|
|
585
|
+
- `getStatus(chainId, txHash)` - Get transaction status
|
|
586
|
+
|
|
353
587
|
#### Guardian Recovery
|
|
354
|
-
- `addGuardian(
|
|
355
|
-
- `removeGuardian(
|
|
356
|
-
- `getGuardians(
|
|
357
|
-
- `initiateRecovery(
|
|
358
|
-
- `
|
|
359
|
-
- `
|
|
588
|
+
- `addGuardian(request, token)` - Add guardian passkey
|
|
589
|
+
- `removeGuardian(guardianIndex, token)` - Remove guardian
|
|
590
|
+
- `getGuardians(token)` - List guardians
|
|
591
|
+
- `initiateRecovery(accountId, attestationObject, label?)` - Start recovery
|
|
592
|
+
- `verifyGuardian(request)` - Submit guardian signature
|
|
593
|
+
- `getRecoveryStatus(accountId)` - Check recovery status
|
|
594
|
+
- `completeRecovery(accountId)` - Complete recovery
|
|
595
|
+
- `cancelRecovery(token)` - Cancel recovery (owner only)
|
|
360
596
|
|
|
361
597
|
## Error Handling
|
|
362
598
|
|