ox 0.12.4 → 0.13.1

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.
Files changed (128) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/_cjs/core/P256.js +1 -1
  3. package/_cjs/core/P256.js.map +1 -1
  4. package/_cjs/core/WebAuthnP256.js +15 -256
  5. package/_cjs/core/WebAuthnP256.js.map +1 -1
  6. package/_cjs/core/WebCryptoP256.js +3 -1
  7. package/_cjs/core/WebCryptoP256.js.map +1 -1
  8. package/_cjs/core/internal/webauthn.js +5 -13
  9. package/_cjs/core/internal/webauthn.js.map +1 -1
  10. package/_cjs/index.docs.js +1 -0
  11. package/_cjs/index.docs.js.map +1 -1
  12. package/_cjs/tempo/KeyAuthorization.js +18 -3
  13. package/_cjs/tempo/KeyAuthorization.js.map +1 -1
  14. package/_cjs/tempo/SignatureEnvelope.js +26 -0
  15. package/_cjs/tempo/SignatureEnvelope.js.map +1 -1
  16. package/_cjs/tempo/TxEnvelopeTempo.js +5 -10
  17. package/_cjs/tempo/TxEnvelopeTempo.js.map +1 -1
  18. package/_cjs/version.js +1 -1
  19. package/_cjs/webauthn/Authentication.js +246 -0
  20. package/_cjs/webauthn/Authentication.js.map +1 -0
  21. package/_cjs/webauthn/Authenticator.js +55 -0
  22. package/_cjs/webauthn/Authenticator.js.map +1 -0
  23. package/_cjs/webauthn/Credential.js +53 -0
  24. package/_cjs/webauthn/Credential.js.map +1 -0
  25. package/_cjs/webauthn/Registration.js +349 -0
  26. package/_cjs/webauthn/Registration.js.map +1 -0
  27. package/_cjs/webauthn/Types.js +3 -0
  28. package/_cjs/webauthn/Types.js.map +1 -0
  29. package/_cjs/webauthn/index.js +9 -0
  30. package/_cjs/webauthn/index.js.map +1 -0
  31. package/_cjs/webauthn/internal/utils.js +53 -0
  32. package/_cjs/webauthn/internal/utils.js.map +1 -0
  33. package/_esm/core/P256.js +1 -1
  34. package/_esm/core/P256.js.map +1 -1
  35. package/_esm/core/WebAuthnP256.js +13 -261
  36. package/_esm/core/WebAuthnP256.js.map +1 -1
  37. package/_esm/core/WebCryptoP256.js +4 -1
  38. package/_esm/core/WebCryptoP256.js.map +1 -1
  39. package/_esm/core/internal/webauthn.js +5 -13
  40. package/_esm/core/internal/webauthn.js.map +1 -1
  41. package/_esm/erc8021/index.js +2 -2
  42. package/_esm/index.docs.js +1 -0
  43. package/_esm/index.docs.js.map +1 -1
  44. package/_esm/tempo/KeyAuthorization.js +66 -3
  45. package/_esm/tempo/KeyAuthorization.js.map +1 -1
  46. package/_esm/tempo/SignatureEnvelope.js +74 -0
  47. package/_esm/tempo/SignatureEnvelope.js.map +1 -1
  48. package/_esm/tempo/TransactionReceipt.js +1 -1
  49. package/_esm/tempo/TransactionRequest.js +1 -1
  50. package/_esm/tempo/TxEnvelopeTempo.js +5 -10
  51. package/_esm/tempo/TxEnvelopeTempo.js.map +1 -1
  52. package/_esm/version.js +1 -1
  53. package/_esm/webauthn/Authentication.js +453 -0
  54. package/_esm/webauthn/Authentication.js.map +1 -0
  55. package/_esm/webauthn/Authenticator.js +176 -0
  56. package/_esm/webauthn/Authenticator.js.map +1 -0
  57. package/_esm/webauthn/Credential.js +95 -0
  58. package/_esm/webauthn/Credential.js.map +1 -0
  59. package/_esm/webauthn/Registration.js +512 -0
  60. package/_esm/webauthn/Registration.js.map +1 -0
  61. package/_esm/webauthn/Types.js +2 -0
  62. package/_esm/webauthn/Types.js.map +1 -0
  63. package/_esm/webauthn/index.js +31 -0
  64. package/_esm/webauthn/index.js.map +1 -0
  65. package/_esm/webauthn/internal/utils.js +52 -0
  66. package/_esm/webauthn/internal/utils.js.map +1 -0
  67. package/_types/core/WebAuthnP256.d.ts +33 -208
  68. package/_types/core/WebAuthnP256.d.ts.map +1 -1
  69. package/_types/core/WebCryptoP256.d.ts +2 -0
  70. package/_types/core/WebCryptoP256.d.ts.map +1 -1
  71. package/_types/core/internal/webauthn.d.ts +2 -110
  72. package/_types/core/internal/webauthn.d.ts.map +1 -1
  73. package/_types/erc8021/index.d.ts +2 -2
  74. package/_types/index.docs.d.ts +1 -0
  75. package/_types/index.docs.d.ts.map +1 -1
  76. package/_types/tempo/KeyAuthorization.d.ts +57 -0
  77. package/_types/tempo/KeyAuthorization.d.ts.map +1 -1
  78. package/_types/tempo/SignatureEnvelope.d.ts +75 -0
  79. package/_types/tempo/SignatureEnvelope.d.ts.map +1 -1
  80. package/_types/tempo/Transaction.d.ts +2 -2
  81. package/_types/tempo/TransactionReceipt.d.ts +2 -2
  82. package/_types/tempo/TransactionRequest.d.ts +2 -2
  83. package/_types/tempo/TxEnvelopeTempo.d.ts.map +1 -1
  84. package/_types/version.d.ts +1 -1
  85. package/_types/webauthn/Authentication.d.ts +324 -0
  86. package/_types/webauthn/Authentication.d.ts.map +1 -0
  87. package/_types/webauthn/Authenticator.d.ts +182 -0
  88. package/_types/webauthn/Authenticator.d.ts.map +1 -0
  89. package/_types/webauthn/Credential.d.ts +77 -0
  90. package/_types/webauthn/Credential.d.ts.map +1 -0
  91. package/_types/webauthn/Registration.d.ts +308 -0
  92. package/_types/webauthn/Registration.d.ts.map +1 -0
  93. package/_types/webauthn/Types.d.ts +106 -0
  94. package/_types/webauthn/Types.d.ts.map +1 -0
  95. package/_types/webauthn/index.d.ts +33 -0
  96. package/_types/webauthn/index.d.ts.map +1 -0
  97. package/_types/webauthn/internal/utils.d.ts +17 -0
  98. package/_types/webauthn/internal/utils.d.ts.map +1 -0
  99. package/core/P256.ts +1 -1
  100. package/core/WebAuthnP256.ts +37 -582
  101. package/core/WebCryptoP256.ts +6 -1
  102. package/core/internal/webauthn.ts +6 -165
  103. package/erc8021/index.ts +2 -2
  104. package/index.docs.ts +1 -0
  105. package/package.json +31 -1
  106. package/tempo/KeyAuthorization.test.ts +139 -0
  107. package/tempo/KeyAuthorization.ts +82 -3
  108. package/tempo/SignatureEnvelope.test.ts +147 -0
  109. package/tempo/SignatureEnvelope.ts +113 -0
  110. package/tempo/Transaction.ts +2 -2
  111. package/tempo/TransactionReceipt.ts +2 -2
  112. package/tempo/TransactionRequest.ts +2 -2
  113. package/tempo/TxEnvelopeTempo.ts +5 -12
  114. package/tempo/e2e.test.ts +265 -0
  115. package/version.ts +1 -1
  116. package/webauthn/Authentication/package.json +6 -0
  117. package/webauthn/Authentication.ts +673 -0
  118. package/webauthn/Authenticator/package.json +6 -0
  119. package/webauthn/Authenticator.ts +259 -0
  120. package/webauthn/Credential/package.json +6 -0
  121. package/webauthn/Credential.ts +146 -0
  122. package/webauthn/Registration/package.json +6 -0
  123. package/webauthn/Registration.ts +805 -0
  124. package/webauthn/Types/package.json +6 -0
  125. package/webauthn/Types.ts +158 -0
  126. package/webauthn/index.ts +38 -0
  127. package/webauthn/internal/utils.ts +63 -0
  128. package/webauthn/package.json +6 -0
@@ -772,6 +772,153 @@ describe('deserialize', () => {
772
772
  })
773
773
  })
774
774
 
775
+ describe('extractAddress', () => {
776
+ describe('secp256k1', () => {
777
+ test('default', () => {
778
+ const privateKey = Secp256k1.randomPrivateKey()
779
+ const address = Address.fromPublicKey(
780
+ Secp256k1.getPublicKey({ privateKey }),
781
+ )
782
+ const payload = '0xdeadbeef' as const
783
+
784
+ const signature = Secp256k1.sign({ payload, privateKey })
785
+ const envelope = SignatureEnvelope.from(signature)
786
+
787
+ expect(
788
+ SignatureEnvelope.extractAddress({ payload, signature: envelope }),
789
+ ).toBe(address)
790
+ })
791
+ })
792
+
793
+ describe('p256', () => {
794
+ test('default', () => {
795
+ const privateKey = P256.randomPrivateKey()
796
+ const pk = P256.getPublicKey({ privateKey })
797
+ const address = Address.fromPublicKey(pk)
798
+ const payload = '0xdeadbeef' as const
799
+
800
+ const signature = P256.sign({ payload, privateKey })
801
+ const envelope = SignatureEnvelope.from({
802
+ prehash: false,
803
+ publicKey: pk,
804
+ signature,
805
+ })
806
+
807
+ expect(
808
+ SignatureEnvelope.extractAddress({ payload, signature: envelope }),
809
+ ).toBe(address)
810
+ })
811
+ })
812
+
813
+ describe('webAuthn', () => {
814
+ test('default', () => {
815
+ const address = Address.fromPublicKey(publicKey)
816
+
817
+ expect(
818
+ SignatureEnvelope.extractAddress({
819
+ payload: '0xdeadbeef',
820
+ signature: signature_webauthn,
821
+ }),
822
+ ).toBe(address)
823
+ })
824
+ })
825
+
826
+ describe('keychain', () => {
827
+ test('default', () => {
828
+ const privateKey = Secp256k1.randomPrivateKey()
829
+ const address = Address.fromPublicKey(
830
+ Secp256k1.getPublicKey({ privateKey }),
831
+ )
832
+ const payload = '0xdeadbeef' as const
833
+
834
+ const signature = Secp256k1.sign({ payload, privateKey })
835
+ const envelope = SignatureEnvelope.from({
836
+ userAddress: '0x1234567890123456789012345678901234567890',
837
+ inner: SignatureEnvelope.from(signature),
838
+ })
839
+
840
+ expect(
841
+ SignatureEnvelope.extractAddress({ payload, signature: envelope }),
842
+ ).toBe(address)
843
+ })
844
+
845
+ test('behavior: root = true returns userAddress', () => {
846
+ expect(
847
+ SignatureEnvelope.extractAddress({
848
+ payload: '0xdeadbeef',
849
+ signature: signature_keychain_secp256k1,
850
+ root: true,
851
+ }),
852
+ ).toBe('0x1234567890123456789012345678901234567890')
853
+ })
854
+ })
855
+ })
856
+
857
+ describe('extractPublicKey', () => {
858
+ describe('secp256k1', () => {
859
+ test('default', () => {
860
+ const privateKey = Secp256k1.randomPrivateKey()
861
+ const pk = Secp256k1.getPublicKey({ privateKey })
862
+ const payload = '0xdeadbeef' as const
863
+
864
+ const signature = Secp256k1.sign({ payload, privateKey })
865
+ const envelope = SignatureEnvelope.from(signature)
866
+
867
+ expect(
868
+ SignatureEnvelope.extractPublicKey({ payload, signature: envelope }),
869
+ ).toEqual(pk)
870
+ })
871
+ })
872
+
873
+ describe('p256', () => {
874
+ test('default', () => {
875
+ const privateKey = P256.randomPrivateKey()
876
+ const pk = P256.getPublicKey({ privateKey })
877
+ const payload = '0xdeadbeef' as const
878
+
879
+ const signature = P256.sign({ payload, privateKey })
880
+ const envelope = SignatureEnvelope.from({
881
+ prehash: false,
882
+ publicKey: pk,
883
+ signature,
884
+ })
885
+
886
+ expect(
887
+ SignatureEnvelope.extractPublicKey({ payload, signature: envelope }),
888
+ ).toEqual(pk)
889
+ })
890
+ })
891
+
892
+ describe('webAuthn', () => {
893
+ test('default', () => {
894
+ expect(
895
+ SignatureEnvelope.extractPublicKey({
896
+ payload: '0xdeadbeef',
897
+ signature: signature_webauthn,
898
+ }),
899
+ ).toEqual(publicKey)
900
+ })
901
+ })
902
+
903
+ describe('keychain', () => {
904
+ test('default', () => {
905
+ const privateKey = Secp256k1.randomPrivateKey()
906
+ const pk = Secp256k1.getPublicKey({ privateKey })
907
+ const payload = '0xdeadbeef' as const
908
+
909
+ const signature = Secp256k1.sign({ payload, privateKey })
910
+ const envelope = SignatureEnvelope.from({
911
+ userAddress: '0x1234567890123456789012345678901234567890',
912
+ inner: SignatureEnvelope.from(signature),
913
+ })
914
+
915
+ expect(
916
+ SignatureEnvelope.extractPublicKey({ payload, signature: envelope }),
917
+ ).toEqual(pk)
918
+ })
919
+ })
920
+ })
921
+
775
922
  describe('from', () => {
776
923
  describe('secp256k1', () => {
777
924
  test('behavior: coerces from hex string', () => {
@@ -269,6 +269,119 @@ export declare namespace assert {
269
269
  | Errors.GlobalErrorType
270
270
  }
271
271
 
272
+ /**
273
+ * Extracts the address of the signer from a {@link ox#SignatureEnvelope.SignatureEnvelope}.
274
+ *
275
+ * - **secp256k1**: Recovers the address from the payload via `ecrecover`.
276
+ * - **p256** / **webAuthn**: Derives the address from the embedded public key.
277
+ * - **keychain**: Extracts from the inner signature (or returns `userAddress` if `user` is `true`).
278
+ *
279
+ * @example
280
+ * ```ts twoslash
281
+ * import { Secp256k1 } from 'ox'
282
+ * import { SignatureEnvelope } from 'ox/tempo'
283
+ *
284
+ * const payload = '0xdeadbeef'
285
+ * const signature = Secp256k1.sign({ payload, privateKey: '0x...' })
286
+ * const envelope = SignatureEnvelope.from(signature)
287
+ *
288
+ * const address = SignatureEnvelope.extractAddress({ // [!code focus]
289
+ * payload, // [!code focus]
290
+ * signature: envelope, // [!code focus]
291
+ * }) // [!code focus]
292
+ * ```
293
+ *
294
+ * @param options - The extraction options.
295
+ * @returns The signer address.
296
+ */
297
+ export function extractAddress(
298
+ options: extractAddress.Options,
299
+ ): extractAddress.ReturnType {
300
+ const { signature, root } = options
301
+ if (signature.type === 'keychain') {
302
+ if (root) return signature.userAddress
303
+ return extractAddress({ ...options, signature: signature.inner })
304
+ }
305
+ return Address.fromPublicKey(extractPublicKey(options))
306
+ }
307
+
308
+ export declare namespace extractAddress {
309
+ type Options = {
310
+ /** The sign payload that was signed (only required for secp256k1 signatures). */
311
+ payload: Hex.Hex | Bytes.Bytes
312
+ /** The signature envelope. */
313
+ signature: SignatureEnvelope
314
+ /** Whether to return the root `userAddress` for keychain signatures instead of extracting from the inner signature. */
315
+ root?: boolean | undefined
316
+ }
317
+
318
+ type ReturnType = Address.Address
319
+
320
+ type ErrorType =
321
+ | Address.fromPublicKey.ErrorType
322
+ | extractPublicKey.ErrorType
323
+ | Errors.GlobalErrorType
324
+ }
325
+
326
+ /**
327
+ * Extracts the public key of the signer from a {@link ox#SignatureEnvelope.SignatureEnvelope}.
328
+ *
329
+ * - **secp256k1**: Recovers the public key from the payload via `ecrecover`.
330
+ * - **p256** / **webAuthn**: Returns the embedded public key.
331
+ * - **keychain**: Extracts from the inner signature.
332
+ *
333
+ * @example
334
+ * ```ts twoslash
335
+ * import { Secp256k1 } from 'ox'
336
+ * import { SignatureEnvelope } from 'ox/tempo'
337
+ *
338
+ * const payload = '0xdeadbeef'
339
+ * const signature = Secp256k1.sign({ payload, privateKey: '0x...' })
340
+ * const envelope = SignatureEnvelope.from(signature)
341
+ *
342
+ * const publicKey = SignatureEnvelope.extractPublicKey({ // [!code focus]
343
+ * payload, // [!code focus]
344
+ * signature: envelope, // [!code focus]
345
+ * }) // [!code focus]
346
+ * ```
347
+ *
348
+ * @param options - The extraction options.
349
+ * @returns The signer's public key.
350
+ */
351
+ export function extractPublicKey(
352
+ options: extractPublicKey.Options,
353
+ ): extractPublicKey.ReturnType {
354
+ const { payload, signature } = options
355
+
356
+ switch (signature.type) {
357
+ case 'secp256k1':
358
+ return ox_Secp256k1.recoverPublicKey({
359
+ payload,
360
+ signature: signature.signature,
361
+ })
362
+ case 'p256':
363
+ case 'webAuthn':
364
+ return signature.publicKey
365
+ case 'keychain':
366
+ return extractPublicKey({ payload, signature: signature.inner })
367
+ }
368
+ }
369
+
370
+ export declare namespace extractPublicKey {
371
+ type Options = {
372
+ /** The sign payload that was signed (only required for secp256k1 signatures). */
373
+ payload: Hex.Hex | Bytes.Bytes
374
+ /** The signature envelope. */
375
+ signature: SignatureEnvelope
376
+ }
377
+
378
+ type ReturnType = PublicKey.PublicKey
379
+
380
+ type ErrorType =
381
+ | ox_Secp256k1.recoverPublicKey.ErrorType
382
+ | Errors.GlobalErrorType
383
+ }
384
+
272
385
  /**
273
386
  * Deserializes a hex-encoded signature envelope into a typed signature object.
274
387
  *
@@ -13,7 +13,7 @@ import type { Call } from './TxEnvelopeTempo.js'
13
13
  /**
14
14
  * A Transaction as defined in the [Execution API specification](https://github.com/ethereum/execution-apis/blob/main/src/schemas/transaction.yaml).
15
15
  *
16
- * @see {@link https://docs.tempo.xyz/protocol/transactions Tempo Transactions}
16
+ * @see {@link https://docs.tempo.xyz/protocol/transactions}
17
17
  */
18
18
  export type Transaction<
19
19
  pending extends boolean = false,
@@ -39,7 +39,7 @@ export type Rpc<pending extends boolean = false> = UnionCompute<
39
39
  * Features configurable fee tokens, call batching, fee sponsorship, access keys,
40
40
  * parallelizable nonces, and scheduled execution via `validAfter`/`validBefore`.
41
41
  *
42
- * @see {@link https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction Tempo Transaction Specification}
42
+ * @see {@link https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction}
43
43
  */
44
44
  export type Tempo<
45
45
  pending extends boolean = false,
@@ -9,7 +9,7 @@ import * as ox_TransactionReceipt from '../core/TransactionReceipt.js'
9
9
  * Extends standard receipts with `feePayer` (the address that paid fees) and
10
10
  * `feeToken` (the TIP-20 token used for fee payment).
11
11
  *
12
- * @see {@link https://docs.tempo.xyz/protocol/transactions Tempo Transactions}
12
+ * @see {@link https://docs.tempo.xyz/protocol/transactions}
13
13
  */
14
14
  export type TransactionReceipt<
15
15
  status = ox_TransactionReceipt.Status,
@@ -60,7 +60,7 @@ export const toRpcType = {
60
60
  /**
61
61
  * Converts an RPC receipt to a TransactionReceipt.
62
62
  *
63
- * @see {@link https://docs.tempo.xyz/protocol/transactions Tempo Transactions}
63
+ * @see {@link https://docs.tempo.xyz/protocol/transactions}
64
64
  *
65
65
  * @example
66
66
  * ```ts twoslash
@@ -16,7 +16,7 @@ type KeyType = 'secp256k1' | 'p256' | 'webAuthn'
16
16
  * Extends the [Execution API specification](https://github.com/ethereum/execution-apis/blob/4aca1d7a3e5aab24c8f6437131289ad386944eaa/src/schemas/transaction.yaml#L358-L423)
17
17
  * with Tempo-specific fields for batched calls, fee tokens, access keys, and scheduled execution.
18
18
  *
19
- * @see {@link https://docs.tempo.xyz/protocol/transactions Tempo Transactions}
19
+ * @see {@link https://docs.tempo.xyz/protocol/transactions}
20
20
  */
21
21
  export type TransactionRequest<
22
22
  bigintType = bigint,
@@ -54,7 +54,7 @@ export type Rpc = Omit<
54
54
  /**
55
55
  * Converts a {@link ox#TransactionRequest.TransactionRequest} to a {@link ox#TransactionRequest.Rpc}.
56
56
  *
57
- * @see {@link https://docs.tempo.xyz/protocol/transactions Tempo Transactions}
57
+ * @see {@link https://docs.tempo.xyz/protocol/transactions}
58
58
  *
59
59
  * @example
60
60
  * ```ts twoslash
@@ -378,18 +378,11 @@ export function deserialize(serialized: Serialized): Compute<TxEnvelopeTempo> {
378
378
  // Recover sender address from the signature if not already set.
379
379
  if (!transaction.from && signatureEnvelope) {
380
380
  try {
381
- if (signatureEnvelope.type === 'keychain')
382
- transaction.from = signatureEnvelope.userAddress
383
- else if (
384
- signatureEnvelope.type === 'p256' ||
385
- signatureEnvelope.type === 'webAuthn'
386
- )
387
- transaction.from = Address.fromPublicKey(signatureEnvelope.publicKey)
388
- else if (signatureEnvelope.type === 'secp256k1')
389
- transaction.from = Secp256k1.recoverAddress({
390
- payload: getSignPayload(from(transaction)),
391
- signature: signatureEnvelope.signature,
392
- })
381
+ transaction.from = SignatureEnvelope.extractAddress({
382
+ payload: getSignPayload(from(transaction)),
383
+ signature: signatureEnvelope,
384
+ root: true,
385
+ })
393
386
  } catch {}
394
387
  }
395
388
 
package/tempo/e2e.test.ts CHANGED
@@ -1391,4 +1391,269 @@ describe('behavior: keyAuthorization', () => {
1391
1391
  expect(receipt).toBeDefined()
1392
1392
  }
1393
1393
  })
1394
+
1395
+ test('behavior: access key with limits + expiry', async () => {
1396
+ const privateKey = P256.randomPrivateKey()
1397
+ const publicKey = P256.getPublicKey({ privateKey })
1398
+ const address = Address.fromPublicKey(publicKey)
1399
+ const access = {
1400
+ address,
1401
+ publicKey,
1402
+ privateKey,
1403
+ } as const
1404
+
1405
+ const keyAuth = KeyAuthorization.from({
1406
+ address: access.address,
1407
+ type: 'p256',
1408
+ expiry: Math.floor(Date.now() / 1000) + 60 * 60,
1409
+ limits: [
1410
+ {
1411
+ token: '0x20c0000000000000000000000000000000000001',
1412
+ limit: Value.from('1000', 6),
1413
+ },
1414
+ ],
1415
+ })
1416
+
1417
+ const keyAuth_signature = Secp256k1.sign({
1418
+ payload: KeyAuthorization.getSignPayload(keyAuth),
1419
+ privateKey: root.privateKey,
1420
+ })
1421
+
1422
+ const keyAuth_signed = KeyAuthorization.from(keyAuth, {
1423
+ signature: SignatureEnvelope.from(keyAuth_signature),
1424
+ })
1425
+
1426
+ const nonce = await getTransactionCount(client, {
1427
+ address: root.address,
1428
+ blockTag: 'pending',
1429
+ })
1430
+
1431
+ const transaction = TxEnvelopeTempo.from({
1432
+ calls: [
1433
+ {
1434
+ to: '0x0000000000000000000000000000000000000000',
1435
+ },
1436
+ ],
1437
+ chainId,
1438
+ feeToken: '0x20c0000000000000000000000000000000000001',
1439
+ keyAuthorization: keyAuth_signed,
1440
+ nonce: BigInt(nonce),
1441
+ gas: 1_000_000n,
1442
+ maxFeePerGas: Value.fromGwei('20'),
1443
+ maxPriorityFeePerGas: Value.fromGwei('10'),
1444
+ })
1445
+
1446
+ const signature = P256.sign({
1447
+ payload: TxEnvelopeTempo.getSignPayload(transaction),
1448
+ privateKey: access.privateKey,
1449
+ })
1450
+
1451
+ const serialized_signed = TxEnvelopeTempo.serialize(transaction, {
1452
+ signature: SignatureEnvelope.from({
1453
+ userAddress: root.address,
1454
+ inner: SignatureEnvelope.from({
1455
+ prehash: false,
1456
+ publicKey: access.publicKey,
1457
+ signature,
1458
+ type: 'p256',
1459
+ }),
1460
+ type: 'keychain',
1461
+ }),
1462
+ })
1463
+
1464
+ const receipt = (await client
1465
+ .request({
1466
+ method: 'eth_sendRawTransactionSync',
1467
+ params: [serialized_signed],
1468
+ })
1469
+ .then((tx) => TransactionReceipt.fromRpc(tx as any)))!
1470
+ expect(receipt).toBeDefined()
1471
+ expect(receipt.status).toBe('success')
1472
+
1473
+ {
1474
+ const response = await client
1475
+ .request({
1476
+ method: 'eth_getTransactionByHash',
1477
+ params: [receipt.transactionHash],
1478
+ })
1479
+ .then((tx) => Transaction.fromRpc(tx as any))
1480
+ if (!response) throw new Error()
1481
+
1482
+ expect(response.from).toBe(root.address)
1483
+ expect(response.keyAuthorization).toBeDefined()
1484
+ expect(response.keyAuthorization?.expiry).toBe(keyAuth.expiry)
1485
+ expect(response.keyAuthorization?.limits).toEqual(keyAuth.limits)
1486
+ }
1487
+ })
1488
+
1489
+ test('behavior: access key with limits (no expiry)', async () => {
1490
+ const privateKey = Secp256k1.randomPrivateKey()
1491
+ const publicKey = Secp256k1.getPublicKey({ privateKey })
1492
+ const address = Address.fromPublicKey(publicKey)
1493
+ const access = {
1494
+ address,
1495
+ publicKey,
1496
+ privateKey,
1497
+ } as const
1498
+
1499
+ const keyAuth = KeyAuthorization.from({
1500
+ address: access.address,
1501
+ type: 'secp256k1',
1502
+ limits: [
1503
+ {
1504
+ token: '0x20c0000000000000000000000000000000000001',
1505
+ limit: Value.from('1000', 6),
1506
+ },
1507
+ ],
1508
+ })
1509
+
1510
+ const keyAuth_signature = Secp256k1.sign({
1511
+ payload: KeyAuthorization.getSignPayload(keyAuth),
1512
+ privateKey: root.privateKey,
1513
+ })
1514
+
1515
+ const keyAuth_signed = KeyAuthorization.from(keyAuth, {
1516
+ signature: SignatureEnvelope.from(keyAuth_signature),
1517
+ })
1518
+
1519
+ const nonce = await getTransactionCount(client, {
1520
+ address: root.address,
1521
+ blockTag: 'pending',
1522
+ })
1523
+
1524
+ const transaction = TxEnvelopeTempo.from({
1525
+ calls: [
1526
+ {
1527
+ to: '0x0000000000000000000000000000000000000000',
1528
+ },
1529
+ ],
1530
+ chainId,
1531
+ feeToken: '0x20c0000000000000000000000000000000000001',
1532
+ keyAuthorization: keyAuth_signed,
1533
+ nonce: BigInt(nonce),
1534
+ gas: 1_000_000n,
1535
+ maxFeePerGas: Value.fromGwei('20'),
1536
+ maxPriorityFeePerGas: Value.fromGwei('10'),
1537
+ })
1538
+
1539
+ const signature = Secp256k1.sign({
1540
+ payload: TxEnvelopeTempo.getSignPayload(transaction),
1541
+ privateKey: access.privateKey,
1542
+ })
1543
+
1544
+ const serialized_signed = TxEnvelopeTempo.serialize(transaction, {
1545
+ signature: SignatureEnvelope.from({
1546
+ userAddress: root.address,
1547
+ inner: SignatureEnvelope.from(signature),
1548
+ type: 'keychain',
1549
+ }),
1550
+ })
1551
+
1552
+ const receipt = (await client
1553
+ .request({
1554
+ method: 'eth_sendRawTransactionSync',
1555
+ params: [serialized_signed],
1556
+ })
1557
+ .then((tx) => TransactionReceipt.fromRpc(tx as any)))!
1558
+ expect(receipt).toBeDefined()
1559
+ expect(receipt.status).toBe('success')
1560
+
1561
+ {
1562
+ const response = await client
1563
+ .request({
1564
+ method: 'eth_getTransactionByHash',
1565
+ params: [receipt.transactionHash],
1566
+ })
1567
+ .then((tx) => Transaction.fromRpc(tx as any))
1568
+ if (!response) throw new Error()
1569
+
1570
+ expect(response.from).toBe(root.address)
1571
+ expect(response.keyAuthorization).toBeDefined()
1572
+ expect(response.keyAuthorization?.expiry).toBe(0)
1573
+ expect(response.keyAuthorization?.limits).toEqual(keyAuth.limits)
1574
+ }
1575
+ })
1576
+
1577
+ test('behavior: access key with expiry (no limits)', async () => {
1578
+ const privateKey = Secp256k1.randomPrivateKey()
1579
+ const publicKey = Secp256k1.getPublicKey({ privateKey })
1580
+ const address = Address.fromPublicKey(publicKey)
1581
+ const access = {
1582
+ address,
1583
+ publicKey,
1584
+ privateKey,
1585
+ } as const
1586
+
1587
+ const keyAuth = KeyAuthorization.from({
1588
+ address: access.address,
1589
+ type: 'secp256k1',
1590
+ expiry: Math.floor(Date.now() / 1000) + 60 * 60,
1591
+ })
1592
+
1593
+ const keyAuth_signature = Secp256k1.sign({
1594
+ payload: KeyAuthorization.getSignPayload(keyAuth),
1595
+ privateKey: root.privateKey,
1596
+ })
1597
+
1598
+ const keyAuth_signed = KeyAuthorization.from(keyAuth, {
1599
+ signature: SignatureEnvelope.from(keyAuth_signature),
1600
+ })
1601
+
1602
+ const nonce = await getTransactionCount(client, {
1603
+ address: root.address,
1604
+ blockTag: 'pending',
1605
+ })
1606
+
1607
+ const transaction = TxEnvelopeTempo.from({
1608
+ calls: [
1609
+ {
1610
+ to: '0x0000000000000000000000000000000000000000',
1611
+ },
1612
+ ],
1613
+ chainId,
1614
+ feeToken: '0x20c0000000000000000000000000000000000001',
1615
+ keyAuthorization: keyAuth_signed,
1616
+ nonce: BigInt(nonce),
1617
+ gas: 1_000_000n,
1618
+ maxFeePerGas: Value.fromGwei('20'),
1619
+ maxPriorityFeePerGas: Value.fromGwei('10'),
1620
+ })
1621
+
1622
+ const signature = Secp256k1.sign({
1623
+ payload: TxEnvelopeTempo.getSignPayload(transaction),
1624
+ privateKey: access.privateKey,
1625
+ })
1626
+
1627
+ const serialized_signed = TxEnvelopeTempo.serialize(transaction, {
1628
+ signature: SignatureEnvelope.from({
1629
+ userAddress: root.address,
1630
+ inner: SignatureEnvelope.from(signature),
1631
+ type: 'keychain',
1632
+ }),
1633
+ })
1634
+
1635
+ const receipt = (await client
1636
+ .request({
1637
+ method: 'eth_sendRawTransactionSync',
1638
+ params: [serialized_signed],
1639
+ })
1640
+ .then((tx) => TransactionReceipt.fromRpc(tx as any)))!
1641
+ expect(receipt).toBeDefined()
1642
+ expect(receipt.status).toBe('success')
1643
+
1644
+ {
1645
+ const response = await client
1646
+ .request({
1647
+ method: 'eth_getTransactionByHash',
1648
+ params: [receipt.transactionHash],
1649
+ })
1650
+ .then((tx) => Transaction.fromRpc(tx as any))
1651
+ if (!response) throw new Error()
1652
+
1653
+ expect(response.from).toBe(root.address)
1654
+ expect(response.keyAuthorization).toBeDefined()
1655
+ expect(response.keyAuthorization?.expiry).toBe(keyAuth.expiry)
1656
+ expect(response.keyAuthorization?.limits).toBeUndefined()
1657
+ }
1658
+ })
1394
1659
  })
package/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** @internal */
2
- export const version = '0.12.4'
2
+ export const version = '0.13.1'
@@ -0,0 +1,6 @@
1
+ {
2
+ "type": "module",
3
+ "types": "../../_types/webauthn/Authentication.d.ts",
4
+ "main": "../../_cjs/webauthn/Authentication.js",
5
+ "module": "../../_esm/webauthn/Authentication.js"
6
+ }