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
@@ -283,7 +283,10 @@ export declare namespace sign {
283
283
  * @returns Whether the payload was signed by the provided public key.
284
284
  */
285
285
  export async function verify(options: verify.Options): Promise<boolean> {
286
- const { payload, signature } = options
286
+ const { lowS = true, payload, signature } = options
287
+
288
+ // Reject high-S signatures if lowS is enabled.
289
+ if (lowS && signature.s > p256.CURVE.n / 2n) return false
287
290
 
288
291
  const publicKey = await globalThis.crypto.subtle.importKey(
289
292
  'raw',
@@ -306,6 +309,8 @@ export async function verify(options: verify.Options): Promise<boolean> {
306
309
 
307
310
  export declare namespace verify {
308
311
  type Options = {
312
+ /** If set to `true`, only low-S signatures will be accepted. @default true */
313
+ lowS?: boolean | undefined
309
314
  /** Public key that signed the payload. */
310
315
  publicKey: PublicKey.PublicKey<boolean>
311
316
  /** Signature of the payload. */
@@ -1,157 +1,7 @@
1
1
  import { p256 } from '@noble/curves/p256'
2
+ import * as Registration from '../../webauthn/Registration.js'
2
3
  import type * as Errors from '../Errors.js'
3
- import * as Hex from '../Hex.js'
4
4
  import * as PublicKey from '../PublicKey.js'
5
- import { CredentialCreationFailedError } from '../WebAuthnP256.js'
6
-
7
- /** @internal */
8
- export type AttestationConveyancePreference =
9
- | 'direct'
10
- | 'enterprise'
11
- | 'indirect'
12
- | 'none'
13
-
14
- /** @internal */
15
- export type AuthenticatorAttachment = 'cross-platform' | 'platform'
16
-
17
- /** @internal */
18
- export type AuthenticatorTransport =
19
- | 'ble'
20
- | 'hybrid'
21
- | 'internal'
22
- | 'nfc'
23
- | 'usb'
24
-
25
- /** @internal */
26
- export type COSEAlgorithmIdentifier = number
27
-
28
- /** @internal */
29
- export type CredentialMediationRequirement =
30
- | 'conditional'
31
- | 'optional'
32
- | 'required'
33
- | 'silent'
34
-
35
- /** @internal */
36
- export type PublicKeyCredentialType = 'public-key'
37
-
38
- /** @internal */
39
- export type ResidentKeyRequirement = 'discouraged' | 'preferred' | 'required'
40
-
41
- /** @internal */
42
- export type UserVerificationRequirement =
43
- | 'discouraged'
44
- | 'preferred'
45
- | 'required'
46
-
47
- /** @internal */
48
- export type LargeBlobSupport = {
49
- support: 'required' | 'preferred'
50
- }
51
-
52
- /** @internal */
53
- export type BufferSource = ArrayBufferView | ArrayBuffer
54
-
55
- /** @internal */
56
- export type PrfExtension = Record<'eval', Record<'first', Uint8Array>>
57
-
58
- /** @internal */
59
- export interface AuthenticationExtensionsClientInputs {
60
- appid?: string
61
- credProps?: boolean
62
- hmacCreateSecret?: boolean
63
- minPinLength?: boolean
64
- prf?: PrfExtension
65
- largeBlob?: LargeBlobSupport
66
- }
67
-
68
- /** @internal */
69
- export interface AuthenticatorSelectionCriteria {
70
- authenticatorAttachment?: AuthenticatorAttachment
71
- requireResidentKey?: boolean
72
- residentKey?: ResidentKeyRequirement
73
- userVerification?: UserVerificationRequirement
74
- }
75
-
76
- /** @internal */
77
- export interface Credential {
78
- readonly id: string
79
- readonly type: string
80
- }
81
-
82
- /** @internal */
83
- export interface CredentialCreationOptions {
84
- publicKey?: PublicKeyCredentialCreationOptions
85
- signal?: AbortSignal
86
- }
87
-
88
- /** @internal */
89
- export interface CredentialRequestOptions {
90
- mediation?: CredentialMediationRequirement
91
- publicKey?: PublicKeyCredentialRequestOptions
92
- signal?: AbortSignal
93
- }
94
-
95
- /** @internal */
96
- export interface PublicKeyCredential extends Credential {
97
- readonly authenticatorAttachment: string | null
98
- readonly rawId: ArrayBuffer
99
- readonly response: AuthenticatorResponse
100
- getClientExtensionResults(): AuthenticationExtensionsClientOutputs
101
- }
102
-
103
- /** @internal */
104
- export interface PublicKeyCredentialCreationOptions {
105
- attestation?: AttestationConveyancePreference
106
- authenticatorSelection?: AuthenticatorSelectionCriteria
107
- challenge: BufferSource
108
- excludeCredentials?: PublicKeyCredentialDescriptor[]
109
- extensions?: AuthenticationExtensionsClientInputs
110
- pubKeyCredParams: PublicKeyCredentialParameters[]
111
- rp: PublicKeyCredentialRpEntity
112
- timeout?: number
113
- user: PublicKeyCredentialUserEntity
114
- }
115
-
116
- /** @internal */
117
- export interface PublicKeyCredentialDescriptor {
118
- id: BufferSource
119
- transports?: AuthenticatorTransport[]
120
- type: PublicKeyCredentialType
121
- }
122
-
123
- /** @internal */
124
- export interface PublicKeyCredentialEntity {
125
- name: string
126
- }
127
-
128
- /** @internal */
129
- export interface PublicKeyCredentialParameters {
130
- alg: COSEAlgorithmIdentifier
131
- type: PublicKeyCredentialType
132
- }
133
-
134
- /** @internal */
135
- export interface PublicKeyCredentialRequestOptions {
136
- allowCredentials?: PublicKeyCredentialDescriptor[]
137
- challenge: BufferSource
138
- extensions?: AuthenticationExtensionsClientInputs
139
- rpId?: string
140
- timeout?: number
141
- userVerification?: UserVerificationRequirement
142
- }
143
-
144
- /** @internal */
145
- export interface PublicKeyCredentialRpEntity extends PublicKeyCredentialEntity {
146
- id?: string
147
- }
148
-
149
- /** @internal */
150
- export interface PublicKeyCredentialUserEntity
151
- extends PublicKeyCredentialEntity {
152
- displayName: string
153
- id: BufferSource
154
- }
155
5
 
156
6
  /**
157
7
  * Parses an ASN.1 signature into a r and s value.
@@ -159,17 +9,8 @@ export interface PublicKeyCredentialUserEntity
159
9
  * @internal
160
10
  */
161
11
  export function parseAsn1Signature(bytes: Uint8Array) {
162
- const r_start = bytes[4] === 0 ? 5 : 4
163
- const r_end = r_start + 32
164
- const s_start = bytes[r_end + 2] === 0 ? r_end + 3 : r_end + 2
165
-
166
- const r = BigInt(Hex.fromBytes(bytes.slice(r_start, r_end)))
167
- const s = BigInt(Hex.fromBytes(bytes.slice(s_start)))
168
-
169
- return {
170
- r,
171
- s: s > p256.CURVE.n / 2n ? p256.CURVE.n - s : s,
172
- }
12
+ const sig = p256.Signature.fromDER(bytes).normalizeS()
13
+ return { r: sig.r, s: sig.s }
173
14
  }
174
15
 
175
16
  /**
@@ -183,7 +24,7 @@ export async function parseCredentialPublicKey(
183
24
  ): Promise<PublicKey.PublicKey> {
184
25
  try {
185
26
  const publicKeyBuffer = response.getPublicKey()
186
- if (!publicKeyBuffer) throw new CredentialCreationFailedError()
27
+ if (!publicKeyBuffer) throw new Registration.CreateFailedError()
187
28
 
188
29
  // Converting `publicKeyBuffer` throws when credential is created by 1Password Firefox Add-on
189
30
  const publicKeyBytes = new Uint8Array(publicKeyBuffer)
@@ -218,7 +59,7 @@ export async function parseCredentialPublicKey(
218
59
  for (let i = 0; i < data.length - coordinate.length; i++)
219
60
  if (coordinate.every((byte, j) => data[i + j] === byte))
220
61
  return i + coordinate.length
221
- throw new CredentialCreationFailedError()
62
+ throw new Registration.CreateFailedError()
222
63
  }
223
64
 
224
65
  const xStart = findStart(0x21)
@@ -235,5 +76,5 @@ export async function parseCredentialPublicKey(
235
76
  }
236
77
 
237
78
  export declare namespace parseCredentialPublicKey {
238
- type ErrorType = CredentialCreationFailedError | Errors.GlobalErrorType
79
+ type ErrorType = Registration.CreateFailedError | Errors.GlobalErrorType
239
80
  }
package/erc8021/index.ts CHANGED
@@ -17,7 +17,7 @@ export type {}
17
17
  *
18
18
  * const dataSuffix2 = Attribution.toDataSuffix({
19
19
  * codes: ['baseapp', 'morpho'],
20
- * codeRegistryAddress: '0x...'
20
+ * codeRegistry: { address: '0x0000000000000000000000000000000000000000', chainId: 1 },
21
21
  * })
22
22
  * ```
23
23
  *
@@ -30,7 +30,7 @@ export type {}
30
30
  * const attribution = Attribution.fromData('0x...')
31
31
  *
32
32
  * console.log(attribution)
33
- * // @log: { codes: ['baseapp', 'morpho'], codeRegistryAddress: '0x...' }
33
+ * // @log: { codes: ['baseapp', 'morpho'], codeRegistry: { address: '0x...', chainId: 1 } }
34
34
  * ```
35
35
  *
36
36
  * @category ERC-8021
package/index.docs.ts CHANGED
@@ -7,4 +7,5 @@ export * from './erc6492/index.js'
7
7
  export * from './erc7821/index.js'
8
8
  export * from './erc8010/index.js'
9
9
  export * from './erc8021/index.js'
10
+ export * from './webauthn/index.js'
10
11
  export * from './tempo/index.js'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ox",
3
3
  "description": "Ethereum Standard Library",
4
- "version": "0.12.4",
4
+ "version": "0.13.1",
5
5
  "main": "./_cjs/index.js",
6
6
  "module": "./_esm/index.js",
7
7
  "types": "./_types/index.d.ts",
@@ -554,6 +554,36 @@
554
554
  "import": "./_esm/trusted-setups/index.js",
555
555
  "default": "./_cjs/trusted-setups/index.js"
556
556
  },
557
+ "./webauthn/Authentication": {
558
+ "types": "./_types/webauthn/Authentication.d.ts",
559
+ "import": "./_esm/webauthn/Authentication.js",
560
+ "default": "./_cjs/webauthn/Authentication.js"
561
+ },
562
+ "./webauthn/Authenticator": {
563
+ "types": "./_types/webauthn/Authenticator.d.ts",
564
+ "import": "./_esm/webauthn/Authenticator.js",
565
+ "default": "./_cjs/webauthn/Authenticator.js"
566
+ },
567
+ "./webauthn/Credential": {
568
+ "types": "./_types/webauthn/Credential.d.ts",
569
+ "import": "./_esm/webauthn/Credential.js",
570
+ "default": "./_cjs/webauthn/Credential.js"
571
+ },
572
+ "./webauthn/Registration": {
573
+ "types": "./_types/webauthn/Registration.d.ts",
574
+ "import": "./_esm/webauthn/Registration.js",
575
+ "default": "./_cjs/webauthn/Registration.js"
576
+ },
577
+ "./webauthn/Types": {
578
+ "types": "./_types/webauthn/Types.d.ts",
579
+ "import": "./_esm/webauthn/Types.js",
580
+ "default": "./_cjs/webauthn/Types.js"
581
+ },
582
+ "./webauthn": {
583
+ "types": "./_types/webauthn/index.d.ts",
584
+ "import": "./_esm/webauthn/index.js",
585
+ "default": "./_cjs/webauthn/index.js"
586
+ },
557
587
  "./window": {
558
588
  "types": "./_types/window/index.d.ts",
559
589
  "import": "./_esm/window/index.js",
@@ -927,6 +927,74 @@ describe('getSignPayload', () => {
927
927
  })
928
928
  })
929
929
 
930
+ describe('deserialize', () => {
931
+ test('default', () => {
932
+ const authorization = KeyAuthorization.from({
933
+ address,
934
+ expiry,
935
+ type: 'secp256k1',
936
+ limits: [
937
+ {
938
+ token,
939
+ limit: Value.from('10', 6),
940
+ },
941
+ ],
942
+ })
943
+
944
+ const serialized = KeyAuthorization.serialize(authorization)
945
+ const deserialized = KeyAuthorization.deserialize(serialized)
946
+
947
+ expect(deserialized).toEqual(authorization)
948
+ })
949
+
950
+ test('signed (secp256k1)', () => {
951
+ const authorization = KeyAuthorization.from(
952
+ {
953
+ address,
954
+ expiry,
955
+ type: 'secp256k1',
956
+ limits: [
957
+ {
958
+ token,
959
+ limit: Value.from('10', 6),
960
+ },
961
+ ],
962
+ },
963
+ { signature: signature_secp256k1 },
964
+ )
965
+
966
+ const serialized = KeyAuthorization.serialize(authorization)
967
+ const deserialized = KeyAuthorization.deserialize(serialized)
968
+
969
+ expect(deserialized).toEqual(authorization)
970
+ })
971
+
972
+ test('no limits', () => {
973
+ const authorization = KeyAuthorization.from({
974
+ address,
975
+ expiry,
976
+ type: 'secp256k1',
977
+ })
978
+
979
+ const serialized = KeyAuthorization.serialize(authorization)
980
+ const deserialized = KeyAuthorization.deserialize(serialized)
981
+
982
+ expect(deserialized).toEqual(authorization)
983
+ })
984
+
985
+ test('no expiry', () => {
986
+ const authorization = KeyAuthorization.from({
987
+ address,
988
+ type: 'secp256k1',
989
+ })
990
+
991
+ const serialized = KeyAuthorization.serialize(authorization)
992
+ const deserialized = KeyAuthorization.deserialize(serialized)
993
+
994
+ expect(deserialized).toEqual(authorization)
995
+ })
996
+ })
997
+
930
998
  describe('hash', () => {
931
999
  test('default', () => {
932
1000
  const authorization = KeyAuthorization.from({
@@ -949,6 +1017,77 @@ describe('hash', () => {
949
1017
  })
950
1018
  })
951
1019
 
1020
+ describe('serialize', () => {
1021
+ test('default', () => {
1022
+ const authorization = KeyAuthorization.from({
1023
+ address,
1024
+ expiry,
1025
+ type: 'secp256k1',
1026
+ limits: [
1027
+ {
1028
+ token,
1029
+ limit: Value.from('10', 6),
1030
+ },
1031
+ ],
1032
+ })
1033
+
1034
+ const serialized = KeyAuthorization.serialize(authorization)
1035
+
1036
+ expect(serialized).toMatchInlineSnapshot(
1037
+ `"0xf838f7808094be95c3f554e9fc85ec51be69a3d807a0d55bcf2c84499602d2dad99420c000000000000000000000000000000000000183989680"`,
1038
+ )
1039
+ })
1040
+
1041
+ test('signed (secp256k1)', () => {
1042
+ const authorization = KeyAuthorization.from(
1043
+ {
1044
+ address,
1045
+ expiry,
1046
+ type: 'secp256k1',
1047
+ limits: [
1048
+ {
1049
+ token,
1050
+ limit: Value.from('10', 6),
1051
+ },
1052
+ ],
1053
+ },
1054
+ { signature: signature_secp256k1 },
1055
+ )
1056
+
1057
+ const serialized = KeyAuthorization.serialize(authorization)
1058
+ const deserialized = KeyAuthorization.deserialize(serialized)
1059
+
1060
+ expect(deserialized).toEqual(authorization)
1061
+ })
1062
+
1063
+ test('no limits', () => {
1064
+ const authorization = KeyAuthorization.from({
1065
+ address,
1066
+ expiry,
1067
+ type: 'secp256k1',
1068
+ })
1069
+
1070
+ const serialized = KeyAuthorization.serialize(authorization)
1071
+
1072
+ expect(serialized).toMatchInlineSnapshot(
1073
+ `"0xdddc808094be95c3f554e9fc85ec51be69a3d807a0d55bcf2c84499602d2"`,
1074
+ )
1075
+ })
1076
+
1077
+ test('no expiry', () => {
1078
+ const authorization = KeyAuthorization.from({
1079
+ address,
1080
+ type: 'secp256k1',
1081
+ })
1082
+
1083
+ const serialized = KeyAuthorization.serialize(authorization)
1084
+
1085
+ expect(serialized).toMatchInlineSnapshot(
1086
+ `"0xd8d7808094be95c3f554e9fc85ec51be69a3d807a0d55bcf2c"`,
1087
+ )
1088
+ })
1089
+ })
1090
+
952
1091
  describe('toRpc', () => {
953
1092
  test('secp256k1', () => {
954
1093
  const authorization = KeyAuthorization.from({
@@ -458,6 +458,43 @@ export declare namespace getSignPayload {
458
458
  type ErrorType = hash.ErrorType | Errors.GlobalErrorType
459
459
  }
460
460
 
461
+ /**
462
+ * Deserializes an RLP-encoded {@link ox#KeyAuthorization.KeyAuthorization}.
463
+ *
464
+ * @example
465
+ * ```ts twoslash
466
+ * import { KeyAuthorization } from 'ox/tempo'
467
+ * import { Value } from 'ox'
468
+ *
469
+ * const authorization = KeyAuthorization.from({
470
+ * expiry: 1234567890,
471
+ * address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
472
+ * type: 'secp256k1',
473
+ * limits: [{
474
+ * token: '0x20c0000000000000000000000000000000000001',
475
+ * limit: Value.from('10', 6)
476
+ * }],
477
+ * })
478
+ *
479
+ * const serialized = KeyAuthorization.serialize(authorization)
480
+ * const deserialized = KeyAuthorization.deserialize(serialized) // [!code focus]
481
+ * ```
482
+ *
483
+ * @param serialized - The RLP-encoded Key Authorization.
484
+ * @returns The {@link ox#KeyAuthorization.KeyAuthorization}.
485
+ */
486
+ export function deserialize(serialized: Hex.Hex): KeyAuthorization {
487
+ const tuple = Rlp.toHex(serialized) as unknown as Tuple
488
+ return fromTuple(tuple)
489
+ }
490
+
491
+ export declare namespace deserialize {
492
+ type ErrorType =
493
+ | Rlp.toHex.ErrorType
494
+ | fromTuple.ErrorType
495
+ | Errors.GlobalErrorType
496
+ }
497
+
461
498
  /**
462
499
  * Computes the hash for an {@link ox#KeyAuthorization.KeyAuthorization}.
463
500
  *
@@ -497,6 +534,42 @@ export declare namespace hash {
497
534
  | Errors.GlobalErrorType
498
535
  }
499
536
 
537
+ /**
538
+ * Serializes a {@link ox#KeyAuthorization.KeyAuthorization} to RLP-encoded hex.
539
+ *
540
+ * @example
541
+ * ```ts twoslash
542
+ * import { KeyAuthorization } from 'ox/tempo'
543
+ * import { Value } from 'ox'
544
+ *
545
+ * const authorization = KeyAuthorization.from({
546
+ * expiry: 1234567890,
547
+ * address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
548
+ * type: 'secp256k1',
549
+ * limits: [{
550
+ * token: '0x20c0000000000000000000000000000000000001',
551
+ * limit: Value.from('10', 6)
552
+ * }],
553
+ * })
554
+ *
555
+ * const serialized = KeyAuthorization.serialize(authorization) // [!code focus]
556
+ * ```
557
+ *
558
+ * @param authorization - The {@link ox#KeyAuthorization.KeyAuthorization}.
559
+ * @returns The RLP-encoded Key Authorization.
560
+ */
561
+ export function serialize(authorization: KeyAuthorization): Hex.Hex {
562
+ const tuple = toTuple(authorization)
563
+ return Rlp.fromHex(tuple as any)
564
+ }
565
+
566
+ export declare namespace serialize {
567
+ type ErrorType =
568
+ | toTuple.ErrorType
569
+ | Rlp.fromHex.ErrorType
570
+ | Errors.GlobalErrorType
571
+ }
572
+
500
573
  /**
501
574
  * Converts an {@link ox#KeyAuthorization.KeyAuthorization} to an {@link ox#KeyAuthorization.Rpc}.
502
575
  *
@@ -603,13 +676,19 @@ export function toTuple<const authorization extends KeyAuthorization>(
603
676
  throw new Error(`Invalid key type: ${authorization.type}`)
604
677
  }
605
678
  })()
679
+ const limitsValue = limits?.map((limit) => [
680
+ limit.token,
681
+ bigintToHex(limit.limit),
682
+ ])
606
683
  const authorizationTuple = [
607
684
  bigintToHex(chainId),
608
685
  type,
609
686
  address,
610
- typeof expiry === 'number' ? numberToHex(expiry) : undefined,
611
- limits?.map((limit) => [limit.token, bigintToHex(limit.limit)]) ??
612
- undefined,
687
+ // expiry is required in the tuple when limits are present
688
+ typeof expiry === 'number' || limitsValue
689
+ ? numberToHex(expiry ?? 0)
690
+ : undefined,
691
+ limitsValue,
613
692
  ].filter(Boolean)
614
693
  return [authorizationTuple, ...(signature ? [signature] : [])] as never
615
694
  }