kentucky-signer-viem 0.1.4 → 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 +225 -0
- 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/package.json
CHANGED
package/src/account.ts
CHANGED
|
@@ -8,11 +8,13 @@ import {
|
|
|
8
8
|
type TransactionSerializable,
|
|
9
9
|
type TypedData,
|
|
10
10
|
type TypedDataDefinition,
|
|
11
|
+
type SignedAuthorizationList,
|
|
11
12
|
hashMessage,
|
|
12
13
|
hashTypedData,
|
|
13
14
|
keccak256,
|
|
14
15
|
serializeTransaction,
|
|
15
16
|
toHex,
|
|
17
|
+
toRlp,
|
|
16
18
|
concat,
|
|
17
19
|
numberToHex,
|
|
18
20
|
} from 'viem'
|
|
@@ -42,6 +44,66 @@ export type TwoFactorCallback = (requirements: {
|
|
|
42
44
|
pinLength: number
|
|
43
45
|
}) => Promise<TwoFactorCodes | null | undefined>
|
|
44
46
|
|
|
47
|
+
/**
|
|
48
|
+
* EIP-7702 Magic byte used in authorization hash
|
|
49
|
+
* @see https://eips.ethereum.org/EIPS/eip-7702
|
|
50
|
+
*/
|
|
51
|
+
const EIP7702_MAGIC = '0x05' as const
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Parameters for signing an EIP-7702 authorization
|
|
55
|
+
*/
|
|
56
|
+
export interface SignAuthorizationParameters {
|
|
57
|
+
/** The contract address to delegate to */
|
|
58
|
+
contractAddress: Address
|
|
59
|
+
/** Chain ID (0 for all chains, defaults to client chain) */
|
|
60
|
+
chainId?: number
|
|
61
|
+
/** Nonce for the authorization (defaults to account's current nonce) */
|
|
62
|
+
nonce?: bigint
|
|
63
|
+
/**
|
|
64
|
+
* Whether the authorization will be executed by the signer themselves.
|
|
65
|
+
* If 'self', the nonce in the authorization will be incremented by 1
|
|
66
|
+
* over the transaction nonce.
|
|
67
|
+
*/
|
|
68
|
+
executor?: 'self'
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Signed EIP-7702 authorization
|
|
73
|
+
* Compatible with viem's SignedAuthorizationList
|
|
74
|
+
*/
|
|
75
|
+
export interface SignedAuthorization {
|
|
76
|
+
/** Chain ID (0 for all chains) */
|
|
77
|
+
chainId: number
|
|
78
|
+
/** Contract address to delegate to */
|
|
79
|
+
contractAddress: Address
|
|
80
|
+
/** Nonce for the authorization */
|
|
81
|
+
nonce: bigint
|
|
82
|
+
/** Recovery identifier (0 or 1) */
|
|
83
|
+
yParity: number
|
|
84
|
+
/** Signature r component */
|
|
85
|
+
r: Hex
|
|
86
|
+
/** Signature s component */
|
|
87
|
+
s: Hex
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Compute the hash for an EIP-7702 authorization
|
|
92
|
+
* Hash = keccak256(0x05 || rlp([chain_id, address, nonce]))
|
|
93
|
+
*/
|
|
94
|
+
function hashAuthorization(params: {
|
|
95
|
+
contractAddress: Address
|
|
96
|
+
chainId: number
|
|
97
|
+
nonce: bigint
|
|
98
|
+
}): Hex {
|
|
99
|
+
const rlpEncoded = toRlp([
|
|
100
|
+
params.chainId === 0 ? '0x' : numberToHex(params.chainId),
|
|
101
|
+
params.contractAddress,
|
|
102
|
+
params.nonce === 0n ? '0x' : numberToHex(params.nonce),
|
|
103
|
+
])
|
|
104
|
+
return keccak256(concat([EIP7702_MAGIC, rlpEncoded]))
|
|
105
|
+
}
|
|
106
|
+
|
|
45
107
|
/**
|
|
46
108
|
* Options for creating a Kentucky Signer account
|
|
47
109
|
*/
|
|
@@ -63,13 +125,44 @@ export interface KentuckySignerAccountOptions {
|
|
|
63
125
|
/**
|
|
64
126
|
* Extended account type with Kentucky Signer specific properties
|
|
65
127
|
*/
|
|
66
|
-
export interface KentuckySignerAccount extends LocalAccount<'kentuckySigner'> {
|
|
128
|
+
export interface KentuckySignerAccount extends Omit<LocalAccount<'kentuckySigner'>, 'signAuthorization'> {
|
|
67
129
|
/** Account ID */
|
|
68
130
|
accountId: string
|
|
69
131
|
/** Current session */
|
|
70
132
|
session: AuthSession
|
|
71
133
|
/** Update the session (e.g., after refresh) */
|
|
72
134
|
updateSession: (session: AuthSession) => void
|
|
135
|
+
/**
|
|
136
|
+
* Sign an EIP-7702 authorization to delegate code to this account
|
|
137
|
+
*
|
|
138
|
+
* @param params - Authorization parameters
|
|
139
|
+
* @param nonce - Current account nonce (required if params.nonce not specified)
|
|
140
|
+
* @returns Signed authorization compatible with viem's authorizationList
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```typescript
|
|
144
|
+
* // Get current nonce
|
|
145
|
+
* const nonce = await publicClient.getTransactionCount({ address: account.address })
|
|
146
|
+
*
|
|
147
|
+
* // Sign authorization
|
|
148
|
+
* const authorization = await account.sign7702Authorization({
|
|
149
|
+
* contractAddress: '0x69007702764179f14F51cdce752f4f775d74E139', // Alchemy MA v2
|
|
150
|
+
* chainId: 1,
|
|
151
|
+
* executor: 'self', // Account will execute the tx
|
|
152
|
+
* }, nonce)
|
|
153
|
+
*
|
|
154
|
+
* // Send EIP-7702 transaction
|
|
155
|
+
* const hash = await walletClient.sendTransaction({
|
|
156
|
+
* authorizationList: [authorization],
|
|
157
|
+
* to: account.address,
|
|
158
|
+
* data: initializeCalldata,
|
|
159
|
+
* })
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
sign7702Authorization: (
|
|
163
|
+
params: SignAuthorizationParameters,
|
|
164
|
+
nonce: bigint
|
|
165
|
+
) => Promise<SignedAuthorization>
|
|
73
166
|
}
|
|
74
167
|
|
|
75
168
|
/**
|
|
@@ -132,20 +225,30 @@ export function createKentuckySignerAccount(
|
|
|
132
225
|
return session.token
|
|
133
226
|
}
|
|
134
227
|
|
|
228
|
+
/**
|
|
229
|
+
* Ensure a value is properly formatted as a Hex string with 0x prefix
|
|
230
|
+
*/
|
|
231
|
+
function ensureHexPrefix(value: string): Hex {
|
|
232
|
+
return (value.startsWith('0x') ? value : `0x${value}`) as Hex
|
|
233
|
+
}
|
|
234
|
+
|
|
135
235
|
/**
|
|
136
236
|
* Sign a hash using Kentucky Signer and return full signature
|
|
137
237
|
* Handles 2FA by detecting the error and calling the callback
|
|
238
|
+
*
|
|
239
|
+
* Note: v in returned signature is always 27 or 28 (standard format)
|
|
138
240
|
*/
|
|
139
|
-
async function signHash(hash: Hex
|
|
241
|
+
async function signHash(hash: Hex): Promise<Hex> {
|
|
140
242
|
const token = await getToken()
|
|
141
243
|
|
|
142
244
|
// First attempt without 2FA codes
|
|
143
245
|
try {
|
|
144
246
|
const response = await client.signEvmTransaction(
|
|
145
|
-
{ tx_hash: hash
|
|
247
|
+
{ tx_hash: hash },
|
|
146
248
|
token
|
|
147
249
|
)
|
|
148
|
-
|
|
250
|
+
// Ensure the signature has 0x prefix
|
|
251
|
+
return ensureHexPrefix(response.signature.full)
|
|
149
252
|
} catch (err) {
|
|
150
253
|
// Check if 2FA is required
|
|
151
254
|
if (err instanceof KentuckySignerError && err.code === '2FA_REQUIRED' && on2FARequired) {
|
|
@@ -163,10 +266,11 @@ export function createKentuckySignerAccount(
|
|
|
163
266
|
|
|
164
267
|
// Retry with 2FA codes
|
|
165
268
|
const response = await client.signEvmTransactionWith2FA(
|
|
166
|
-
{ tx_hash: hash,
|
|
269
|
+
{ tx_hash: hash, totp_code: codes.totpCode, pin: codes.pin },
|
|
167
270
|
token
|
|
168
271
|
)
|
|
169
|
-
|
|
272
|
+
// Ensure the signature has 0x prefix
|
|
273
|
+
return ensureHexPrefix(response.signature.full)
|
|
170
274
|
}
|
|
171
275
|
throw err
|
|
172
276
|
}
|
|
@@ -175,19 +279,21 @@ export function createKentuckySignerAccount(
|
|
|
175
279
|
/**
|
|
176
280
|
* Sign a hash using Kentucky Signer and return signature components
|
|
177
281
|
* Handles 2FA by detecting the error and calling the callback
|
|
282
|
+
*
|
|
283
|
+
* Note: v is always 27 or 28 (standard format, recovery_id + 27)
|
|
178
284
|
*/
|
|
179
|
-
async function signHashWithComponents(hash: Hex
|
|
285
|
+
async function signHashWithComponents(hash: Hex): Promise<{ r: Hex; s: Hex; v: number }> {
|
|
180
286
|
const token = await getToken()
|
|
181
287
|
|
|
182
288
|
// First attempt without 2FA codes
|
|
183
289
|
try {
|
|
184
290
|
const response = await client.signEvmTransaction(
|
|
185
|
-
{ tx_hash: hash
|
|
291
|
+
{ tx_hash: hash },
|
|
186
292
|
token
|
|
187
293
|
)
|
|
188
294
|
return {
|
|
189
|
-
r: response.signature.r,
|
|
190
|
-
s: response.signature.s,
|
|
295
|
+
r: ensureHexPrefix(response.signature.r),
|
|
296
|
+
s: ensureHexPrefix(response.signature.s),
|
|
191
297
|
v: response.signature.v,
|
|
192
298
|
}
|
|
193
299
|
} catch (err) {
|
|
@@ -207,12 +313,12 @@ export function createKentuckySignerAccount(
|
|
|
207
313
|
|
|
208
314
|
// Retry with 2FA codes
|
|
209
315
|
const response = await client.signEvmTransactionWith2FA(
|
|
210
|
-
{ tx_hash: hash,
|
|
316
|
+
{ tx_hash: hash, totp_code: codes.totpCode, pin: codes.pin },
|
|
211
317
|
token
|
|
212
318
|
)
|
|
213
319
|
return {
|
|
214
|
-
r: response.signature.r,
|
|
215
|
-
s: response.signature.s,
|
|
320
|
+
r: ensureHexPrefix(response.signature.r),
|
|
321
|
+
s: ensureHexPrefix(response.signature.s),
|
|
216
322
|
v: response.signature.v,
|
|
217
323
|
}
|
|
218
324
|
}
|
|
@@ -230,7 +336,7 @@ export function createKentuckySignerAccount(
|
|
|
230
336
|
*/
|
|
231
337
|
async signMessage({ message }: { message: SignableMessage }): Promise<Hex> {
|
|
232
338
|
const messageHash = hashMessage(message)
|
|
233
|
-
return signHash(messageHash
|
|
339
|
+
return signHash(messageHash)
|
|
234
340
|
},
|
|
235
341
|
|
|
236
342
|
/**
|
|
@@ -238,6 +344,9 @@ export function createKentuckySignerAccount(
|
|
|
238
344
|
*
|
|
239
345
|
* Serializes the transaction, hashes it, signs via Kentucky Signer,
|
|
240
346
|
* and returns the signed serialized transaction.
|
|
347
|
+
*
|
|
348
|
+
* For legacy transactions, applies EIP-155 encoding (v = chainId * 2 + 35 + recoveryId)
|
|
349
|
+
* For modern transactions (EIP-1559, EIP-2930, etc.), uses yParity (0 or 1)
|
|
241
350
|
*/
|
|
242
351
|
async signTransaction(
|
|
243
352
|
transaction: TransactionSerializable
|
|
@@ -251,29 +360,37 @@ export function createKentuckySignerAccount(
|
|
|
251
360
|
// Hash the serialized transaction
|
|
252
361
|
const txHash = keccak256(serializedUnsigned)
|
|
253
362
|
|
|
254
|
-
// Sign the hash
|
|
255
|
-
const { r, s, v } = await signHashWithComponents(txHash
|
|
363
|
+
// Sign the hash - v is always 27 or 28 (recovery_id + 27)
|
|
364
|
+
const { r, s, v } = await signHashWithComponents(txHash)
|
|
365
|
+
|
|
366
|
+
// Convert v (27/28) to recovery ID (0/1)
|
|
367
|
+
const recoveryId = v - 27
|
|
256
368
|
|
|
257
|
-
//
|
|
258
|
-
|
|
369
|
+
// Determine signature format based on transaction type
|
|
370
|
+
let signatureV: bigint
|
|
259
371
|
let yParity: number
|
|
372
|
+
|
|
260
373
|
if (
|
|
261
374
|
transaction.type === 'eip1559' ||
|
|
262
375
|
transaction.type === 'eip2930' ||
|
|
263
376
|
transaction.type === 'eip4844' ||
|
|
264
377
|
transaction.type === 'eip7702'
|
|
265
378
|
) {
|
|
266
|
-
|
|
379
|
+
// Modern transactions use yParity directly (0 or 1)
|
|
380
|
+
yParity = recoveryId
|
|
381
|
+
signatureV = BigInt(yParity)
|
|
267
382
|
} else {
|
|
268
|
-
// Legacy transaction -
|
|
269
|
-
|
|
383
|
+
// Legacy transaction - apply EIP-155 encoding
|
|
384
|
+
// v = chainId * 2 + 35 + recoveryId
|
|
385
|
+
signatureV = BigInt(chainId * 2 + 35 + recoveryId)
|
|
386
|
+
yParity = recoveryId
|
|
270
387
|
}
|
|
271
388
|
|
|
272
389
|
// Serialize with signature
|
|
273
390
|
const serializedSigned = serializeTransaction(transaction, {
|
|
274
391
|
r,
|
|
275
392
|
s,
|
|
276
|
-
v:
|
|
393
|
+
v: signatureV,
|
|
277
394
|
yParity,
|
|
278
395
|
} as any)
|
|
279
396
|
|
|
@@ -290,7 +407,7 @@ export function createKentuckySignerAccount(
|
|
|
290
407
|
typedData: TypedDataDefinition<TTypedData, TPrimaryType>
|
|
291
408
|
): Promise<Hex> {
|
|
292
409
|
const hash = hashTypedData(typedData)
|
|
293
|
-
return signHash(hash
|
|
410
|
+
return signHash(hash)
|
|
294
411
|
},
|
|
295
412
|
}) as KentuckySignerAccount
|
|
296
413
|
|
|
@@ -306,6 +423,49 @@ export function createKentuckySignerAccount(
|
|
|
306
423
|
}
|
|
307
424
|
}
|
|
308
425
|
|
|
426
|
+
/**
|
|
427
|
+
* Sign an EIP-7702 authorization
|
|
428
|
+
*
|
|
429
|
+
* This allows the EOA to delegate its code to a smart contract,
|
|
430
|
+
* enabling smart account features like batching and gas sponsorship.
|
|
431
|
+
*/
|
|
432
|
+
account.sign7702Authorization = async (
|
|
433
|
+
params: SignAuthorizationParameters,
|
|
434
|
+
currentNonce: bigint
|
|
435
|
+
): Promise<SignedAuthorization> => {
|
|
436
|
+
// Determine the nonce for the authorization
|
|
437
|
+
// If executor is 'self', increment by 1 because the tx will use currentNonce
|
|
438
|
+
const authNonce = params.executor === 'self'
|
|
439
|
+
? currentNonce + 1n
|
|
440
|
+
: (params.nonce ?? currentNonce)
|
|
441
|
+
|
|
442
|
+
// Use provided chainId or default
|
|
443
|
+
const chainId = params.chainId ?? defaultChainId
|
|
444
|
+
|
|
445
|
+
// Compute the authorization hash
|
|
446
|
+
const authHash = hashAuthorization({
|
|
447
|
+
contractAddress: params.contractAddress,
|
|
448
|
+
chainId,
|
|
449
|
+
nonce: authNonce,
|
|
450
|
+
})
|
|
451
|
+
|
|
452
|
+
// Sign the hash using Kentucky Signer
|
|
453
|
+
// v is always 27 or 28 (recovery_id + 27)
|
|
454
|
+
const { r, s, v } = await signHashWithComponents(authHash)
|
|
455
|
+
|
|
456
|
+
// Convert v (27/28) to yParity (0/1) for EIP-7702
|
|
457
|
+
const yParity = v - 27
|
|
458
|
+
|
|
459
|
+
return {
|
|
460
|
+
chainId,
|
|
461
|
+
contractAddress: params.contractAddress,
|
|
462
|
+
nonce: authNonce,
|
|
463
|
+
yParity,
|
|
464
|
+
r,
|
|
465
|
+
s,
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
309
469
|
return account
|
|
310
470
|
}
|
|
311
471
|
|
package/src/client.ts
CHANGED
|
@@ -218,9 +218,9 @@ export class KentuckySignerClient {
|
|
|
218
218
|
/**
|
|
219
219
|
* Sign an EVM transaction hash
|
|
220
220
|
*
|
|
221
|
-
* @param request - Sign request with tx_hash
|
|
221
|
+
* @param request - Sign request with tx_hash
|
|
222
222
|
* @param token - JWT token
|
|
223
|
-
* @returns Signature response with r, s, v components
|
|
223
|
+
* @returns Signature response with r, s, v components (v is always 27 or 28)
|
|
224
224
|
*/
|
|
225
225
|
async signEvmTransaction(
|
|
226
226
|
request: SignEvmRequest,
|
|
@@ -239,13 +239,12 @@ export class KentuckySignerClient {
|
|
|
239
239
|
* Convenience method that wraps signEvmTransaction.
|
|
240
240
|
*
|
|
241
241
|
* @param hash - 32-byte hash to sign (hex encoded with 0x prefix)
|
|
242
|
-
* @param chainId - Chain ID
|
|
243
242
|
* @param token - JWT token
|
|
244
243
|
* @returns Full signature (hex encoded with 0x prefix)
|
|
245
244
|
*/
|
|
246
|
-
async signHash(hash: Hex,
|
|
245
|
+
async signHash(hash: Hex, token: string): Promise<Hex> {
|
|
247
246
|
const response = await this.signEvmTransaction(
|
|
248
|
-
{ tx_hash: hash
|
|
247
|
+
{ tx_hash: hash },
|
|
249
248
|
token
|
|
250
249
|
)
|
|
251
250
|
return response.signature.full
|
package/src/index.ts
CHANGED
|
@@ -15,8 +15,15 @@ export {
|
|
|
15
15
|
type KentuckySignerAccountOptions,
|
|
16
16
|
type TwoFactorCodes,
|
|
17
17
|
type TwoFactorCallback,
|
|
18
|
+
// EIP-7702 types
|
|
19
|
+
type SignAuthorizationParameters,
|
|
20
|
+
type SignedAuthorization,
|
|
18
21
|
} from './account'
|
|
19
22
|
|
|
23
|
+
// EIP-7702 Constants
|
|
24
|
+
/** Alchemy's SemiModularAccount7702 implementation address (same across all EVM chains) */
|
|
25
|
+
export const ALCHEMY_SEMI_MODULAR_ACCOUNT_7702 = '0x69007702764179f14F51cdce752f4f775d74E139' as const
|
|
26
|
+
|
|
20
27
|
// Client
|
|
21
28
|
export {
|
|
22
29
|
KentuckySignerClient,
|
|
@@ -127,3 +134,28 @@ export {
|
|
|
127
134
|
formatError,
|
|
128
135
|
withRetry,
|
|
129
136
|
} from './utils'
|
|
137
|
+
|
|
138
|
+
// Intent signing for relayer integration
|
|
139
|
+
export {
|
|
140
|
+
createExecutionIntent,
|
|
141
|
+
signIntent,
|
|
142
|
+
signBatchIntents,
|
|
143
|
+
hashIntent,
|
|
144
|
+
hashBatchIntents,
|
|
145
|
+
type ExecutionIntent,
|
|
146
|
+
type SignedIntent,
|
|
147
|
+
type CreateIntentParams,
|
|
148
|
+
} from './intent'
|
|
149
|
+
|
|
150
|
+
// Relayer client
|
|
151
|
+
export {
|
|
152
|
+
RelayerClient,
|
|
153
|
+
createRelayerClient,
|
|
154
|
+
type PaymentMode,
|
|
155
|
+
type TokenOption,
|
|
156
|
+
type EstimateResponse,
|
|
157
|
+
type RelayResponse,
|
|
158
|
+
type TransactionStatus,
|
|
159
|
+
type StatusResponse,
|
|
160
|
+
type RelayerClientOptions,
|
|
161
|
+
} from './relayer-client'
|
package/src/intent.ts
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Address,
|
|
3
|
+
type Hex,
|
|
4
|
+
keccak256,
|
|
5
|
+
encodeAbiParameters,
|
|
6
|
+
parseAbiParameters,
|
|
7
|
+
encodePacked,
|
|
8
|
+
} from 'viem'
|
|
9
|
+
import type { KentuckySignerAccount } from './account'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Execution intent to be signed by the EOA owner
|
|
13
|
+
*/
|
|
14
|
+
export interface ExecutionIntent {
|
|
15
|
+
/** Replay protection nonce */
|
|
16
|
+
nonce: bigint
|
|
17
|
+
/** Expiration timestamp (unix seconds) */
|
|
18
|
+
deadline: bigint
|
|
19
|
+
/** Contract to call */
|
|
20
|
+
target: Address
|
|
21
|
+
/** ETH value to send */
|
|
22
|
+
value: bigint
|
|
23
|
+
/** Calldata for the call */
|
|
24
|
+
data: Hex
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Signed execution intent
|
|
29
|
+
*/
|
|
30
|
+
export interface SignedIntent {
|
|
31
|
+
/** The execution intent */
|
|
32
|
+
intent: ExecutionIntent
|
|
33
|
+
/** Owner's signature on the intent */
|
|
34
|
+
signature: Hex
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Parameters for creating an execution intent
|
|
39
|
+
*/
|
|
40
|
+
export interface CreateIntentParams {
|
|
41
|
+
/** Account nonce (fetch from contract) */
|
|
42
|
+
nonce: bigint
|
|
43
|
+
/** Expiration timestamp (unix seconds) */
|
|
44
|
+
deadline?: bigint
|
|
45
|
+
/** Contract to call */
|
|
46
|
+
target: Address
|
|
47
|
+
/** ETH value to send */
|
|
48
|
+
value?: bigint
|
|
49
|
+
/** Calldata for the call */
|
|
50
|
+
data?: Hex
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// EIP-712 type hash for ExecutionIntent
|
|
54
|
+
const INTENT_TYPEHASH = keccak256(
|
|
55
|
+
encodePacked(
|
|
56
|
+
['string'],
|
|
57
|
+
['ExecutionIntent(uint256 nonce,uint256 deadline,address target,uint256 value,bytes data)']
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Create an execution intent
|
|
63
|
+
*
|
|
64
|
+
* @param params - Intent parameters
|
|
65
|
+
* @returns Execution intent
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```typescript
|
|
69
|
+
* const intent = createExecutionIntent({
|
|
70
|
+
* nonce: 0n,
|
|
71
|
+
* target: '0x...',
|
|
72
|
+
* value: parseEther('0.1'),
|
|
73
|
+
* data: '0x',
|
|
74
|
+
* })
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export function createExecutionIntent(params: CreateIntentParams): ExecutionIntent {
|
|
78
|
+
return {
|
|
79
|
+
nonce: params.nonce,
|
|
80
|
+
deadline: params.deadline ?? BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour default
|
|
81
|
+
target: params.target,
|
|
82
|
+
value: params.value ?? 0n,
|
|
83
|
+
data: params.data ?? '0x',
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Compute the hash of an execution intent
|
|
89
|
+
*
|
|
90
|
+
* @param intent - The execution intent
|
|
91
|
+
* @returns Intent hash
|
|
92
|
+
*/
|
|
93
|
+
export function hashIntent(intent: ExecutionIntent): Hex {
|
|
94
|
+
const dataHash = keccak256(intent.data)
|
|
95
|
+
return keccak256(
|
|
96
|
+
encodeAbiParameters(
|
|
97
|
+
parseAbiParameters('bytes32, uint256, uint256, address, uint256, bytes32'),
|
|
98
|
+
[INTENT_TYPEHASH, intent.nonce, intent.deadline, intent.target, intent.value, dataHash]
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Sign an execution intent using a Kentucky Signer account
|
|
105
|
+
*
|
|
106
|
+
* @param account - Kentucky Signer account
|
|
107
|
+
* @param intent - The execution intent to sign
|
|
108
|
+
* @returns Signed intent
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```typescript
|
|
112
|
+
* const account = useKentuckySignerAccount()
|
|
113
|
+
* const intent = createExecutionIntent({ nonce: 0n, target: '0x...' })
|
|
114
|
+
* const signedIntent = await signIntent(account, intent)
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export async function signIntent(
|
|
118
|
+
account: KentuckySignerAccount,
|
|
119
|
+
intent: ExecutionIntent
|
|
120
|
+
): Promise<SignedIntent> {
|
|
121
|
+
// Compute intent hash
|
|
122
|
+
const intentHash = hashIntent(intent)
|
|
123
|
+
|
|
124
|
+
// Sign the hash as a message (will be recovered with ECDSA)
|
|
125
|
+
// Kentucky Signer returns signatures with v = 27 or 28 (standard format)
|
|
126
|
+
// which is directly compatible with OpenZeppelin's ECDSA.recover
|
|
127
|
+
const signature = await account.signMessage({
|
|
128
|
+
message: { raw: intentHash },
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
intent,
|
|
133
|
+
signature,
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Compute combined hash for batch intents
|
|
139
|
+
*
|
|
140
|
+
* @param intents - Array of execution intents
|
|
141
|
+
* @returns Combined hash
|
|
142
|
+
*/
|
|
143
|
+
export function hashBatchIntents(intents: ExecutionIntent[]): Hex {
|
|
144
|
+
const intentHashes = intents.map(hashIntent)
|
|
145
|
+
return keccak256(encodePacked(['bytes32[]'], [intentHashes]))
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Sign multiple execution intents for batch execution
|
|
150
|
+
*
|
|
151
|
+
* @param account - Kentucky Signer account
|
|
152
|
+
* @param intents - Array of execution intents
|
|
153
|
+
* @returns Array of signed intents
|
|
154
|
+
*/
|
|
155
|
+
export async function signBatchIntents(
|
|
156
|
+
account: KentuckySignerAccount,
|
|
157
|
+
intents: ExecutionIntent[]
|
|
158
|
+
): Promise<SignedIntent[]> {
|
|
159
|
+
const signedIntents: SignedIntent[] = []
|
|
160
|
+
|
|
161
|
+
for (const intent of intents) {
|
|
162
|
+
const signed = await signIntent(account, intent)
|
|
163
|
+
signedIntents.push(signed)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return signedIntents
|
|
167
|
+
}
|
package/src/react/index.ts
CHANGED
|
@@ -30,3 +30,36 @@ export {
|
|
|
30
30
|
useAddress,
|
|
31
31
|
type UseWalletClientOptions,
|
|
32
32
|
} from './hooks'
|
|
33
|
+
|
|
34
|
+
// Relayer Hooks
|
|
35
|
+
export {
|
|
36
|
+
useRelayIntent,
|
|
37
|
+
useTransactionStatus,
|
|
38
|
+
useEstimate,
|
|
39
|
+
useNonce,
|
|
40
|
+
type UseRelayIntentResult,
|
|
41
|
+
type UseTransactionStatusResult,
|
|
42
|
+
type UseEstimateResult,
|
|
43
|
+
type UseNonceResult,
|
|
44
|
+
} from './relayer-hooks'
|
|
45
|
+
|
|
46
|
+
// Re-export relayer types needed by hooks
|
|
47
|
+
export {
|
|
48
|
+
RelayerClient,
|
|
49
|
+
createRelayerClient,
|
|
50
|
+
type PaymentMode,
|
|
51
|
+
type TokenOption,
|
|
52
|
+
type EstimateResponse,
|
|
53
|
+
type RelayResponse,
|
|
54
|
+
type TransactionStatus,
|
|
55
|
+
type StatusResponse,
|
|
56
|
+
type Authorization7702,
|
|
57
|
+
} from '../relayer-client'
|
|
58
|
+
|
|
59
|
+
// Re-export intent types needed by hooks
|
|
60
|
+
export {
|
|
61
|
+
createExecutionIntent,
|
|
62
|
+
signIntent,
|
|
63
|
+
type ExecutionIntent,
|
|
64
|
+
type SignedIntent,
|
|
65
|
+
} from '../intent'
|