ox 0.14.44 → 0.14.46

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 (149) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/Frame/package.json +6 -0
  3. package/FrameReceipt/package.json +6 -0
  4. package/FrameSignature/package.json +6 -0
  5. package/TxEnvelopeEip8141/package.json +6 -0
  6. package/_cjs/core/Frame.js +147 -0
  7. package/_cjs/core/Frame.js.map +1 -0
  8. package/_cjs/core/FrameReceipt.js +34 -0
  9. package/_cjs/core/FrameReceipt.js.map +1 -0
  10. package/_cjs/core/FrameSignature.js +203 -0
  11. package/_cjs/core/FrameSignature.js.map +1 -0
  12. package/_cjs/core/Transaction.js +18 -2
  13. package/_cjs/core/Transaction.js.map +1 -1
  14. package/_cjs/core/TransactionReceipt.js +10 -0
  15. package/_cjs/core/TransactionReceipt.js.map +1 -1
  16. package/_cjs/core/TransactionRequest.js +11 -1
  17. package/_cjs/core/TransactionRequest.js.map +1 -1
  18. package/_cjs/core/TxEnvelopeEip8141.js +283 -0
  19. package/_cjs/core/TxEnvelopeEip8141.js.map +1 -0
  20. package/_cjs/core/internal/curves.js +6 -0
  21. package/_cjs/core/internal/curves.js.map +1 -0
  22. package/_cjs/core/internal/quantity.js +10 -0
  23. package/_cjs/core/internal/quantity.js.map +1 -0
  24. package/_cjs/erc4337/UserOperationReceipt.js.map +1 -1
  25. package/_cjs/index.js +6 -2
  26. package/_cjs/index.js.map +1 -1
  27. package/_cjs/tempo/KeyAuthorization.js +43 -10
  28. package/_cjs/tempo/KeyAuthorization.js.map +1 -1
  29. package/_cjs/tempo/MultisigConfig.js +33 -11
  30. package/_cjs/tempo/MultisigConfig.js.map +1 -1
  31. package/_cjs/tempo/MultisigOperation.js +18 -94
  32. package/_cjs/tempo/MultisigOperation.js.map +1 -1
  33. package/_cjs/tempo/MultisigSimulation.js +18 -46
  34. package/_cjs/tempo/MultisigSimulation.js.map +1 -1
  35. package/_cjs/tempo/SignatureEnvelope.js +72 -109
  36. package/_cjs/tempo/SignatureEnvelope.js.map +1 -1
  37. package/_cjs/tempo/TransactionRequest.js +5 -1
  38. package/_cjs/tempo/TransactionRequest.js.map +1 -1
  39. package/_cjs/tempo/index.js.map +1 -1
  40. package/_cjs/version.js +1 -1
  41. package/_esm/core/Frame.js +311 -0
  42. package/_esm/core/Frame.js.map +1 -0
  43. package/_esm/core/FrameReceipt.js +72 -0
  44. package/_esm/core/FrameReceipt.js.map +1 -0
  45. package/_esm/core/FrameSignature.js +422 -0
  46. package/_esm/core/FrameSignature.js.map +1 -0
  47. package/_esm/core/Transaction.js +18 -2
  48. package/_esm/core/Transaction.js.map +1 -1
  49. package/_esm/core/TransactionReceipt.js +10 -0
  50. package/_esm/core/TransactionReceipt.js.map +1 -1
  51. package/_esm/core/TransactionRequest.js +11 -1
  52. package/_esm/core/TransactionRequest.js.map +1 -1
  53. package/_esm/core/TxEnvelopeEip8141.js +490 -0
  54. package/_esm/core/TxEnvelopeEip8141.js.map +1 -0
  55. package/_esm/core/internal/curves.js +19 -0
  56. package/_esm/core/internal/curves.js.map +1 -0
  57. package/_esm/core/internal/quantity.js +14 -0
  58. package/_esm/core/internal/quantity.js.map +1 -0
  59. package/_esm/erc4337/UserOperationReceipt.js.map +1 -1
  60. package/_esm/index.js +73 -0
  61. package/_esm/index.js.map +1 -1
  62. package/_esm/tempo/KeyAuthorization.js +42 -9
  63. package/_esm/tempo/KeyAuthorization.js.map +1 -1
  64. package/_esm/tempo/MultisigConfig.js +41 -22
  65. package/_esm/tempo/MultisigConfig.js.map +1 -1
  66. package/_esm/tempo/MultisigOperation.js +20 -100
  67. package/_esm/tempo/MultisigOperation.js.map +1 -1
  68. package/_esm/tempo/MultisigSimulation.js +18 -46
  69. package/_esm/tempo/MultisigSimulation.js.map +1 -1
  70. package/_esm/tempo/SignatureEnvelope.js +107 -169
  71. package/_esm/tempo/SignatureEnvelope.js.map +1 -1
  72. package/_esm/tempo/TransactionRequest.js +5 -1
  73. package/_esm/tempo/TransactionRequest.js.map +1 -1
  74. package/_esm/tempo/index.js +3 -1
  75. package/_esm/tempo/index.js.map +1 -1
  76. package/_esm/version.js +1 -1
  77. package/_types/core/Frame.d.ts +290 -0
  78. package/_types/core/Frame.d.ts.map +1 -0
  79. package/_types/core/FrameReceipt.d.ts +92 -0
  80. package/_types/core/FrameReceipt.d.ts.map +1 -0
  81. package/_types/core/FrameSignature.d.ts +357 -0
  82. package/_types/core/FrameSignature.d.ts.map +1 -0
  83. package/_types/core/Transaction.d.ts +29 -4
  84. package/_types/core/Transaction.d.ts.map +1 -1
  85. package/_types/core/TransactionReceipt.d.ts +15 -6
  86. package/_types/core/TransactionReceipt.d.ts.map +1 -1
  87. package/_types/core/TransactionRequest.d.ts +10 -4
  88. package/_types/core/TransactionRequest.d.ts.map +1 -1
  89. package/_types/core/TxEnvelopeEip8141.d.ts +343 -0
  90. package/_types/core/TxEnvelopeEip8141.d.ts.map +1 -0
  91. package/_types/core/internal/curves.d.ts +19 -0
  92. package/_types/core/internal/curves.d.ts.map +1 -0
  93. package/_types/core/internal/quantity.d.ts +10 -0
  94. package/_types/core/internal/quantity.d.ts.map +1 -0
  95. package/_types/erc4337/UserOperationReceipt.d.ts +1 -1
  96. package/_types/erc4337/UserOperationReceipt.d.ts.map +1 -1
  97. package/_types/index.d.ts +73 -0
  98. package/_types/index.d.ts.map +1 -1
  99. package/_types/tempo/KeyAuthorization.d.ts +48 -38
  100. package/_types/tempo/KeyAuthorization.d.ts.map +1 -1
  101. package/_types/tempo/MultisigConfig.d.ts +15 -14
  102. package/_types/tempo/MultisigConfig.d.ts.map +1 -1
  103. package/_types/tempo/MultisigOperation.d.ts +3 -2
  104. package/_types/tempo/MultisigOperation.d.ts.map +1 -1
  105. package/_types/tempo/MultisigSimulation.d.ts +2 -49
  106. package/_types/tempo/MultisigSimulation.d.ts.map +1 -1
  107. package/_types/tempo/SignatureEnvelope.d.ts +69 -128
  108. package/_types/tempo/SignatureEnvelope.d.ts.map +1 -1
  109. package/_types/tempo/Transaction.d.ts +2 -0
  110. package/_types/tempo/Transaction.d.ts.map +1 -1
  111. package/_types/tempo/TransactionReceipt.d.ts +2 -0
  112. package/_types/tempo/TransactionReceipt.d.ts.map +1 -1
  113. package/_types/tempo/TransactionRequest.d.ts +5 -2
  114. package/_types/tempo/TransactionRequest.d.ts.map +1 -1
  115. package/_types/tempo/index.d.ts +3 -1
  116. package/_types/tempo/index.d.ts.map +1 -1
  117. package/_types/version.d.ts +1 -1
  118. package/core/Frame.ts +443 -0
  119. package/core/FrameReceipt.ts +117 -0
  120. package/core/FrameSignature.ts +606 -0
  121. package/core/Transaction.ts +73 -4
  122. package/core/TransactionReceipt.ts +41 -4
  123. package/core/TransactionRequest.ts +26 -2
  124. package/core/TxEnvelopeEip8141.ts +717 -0
  125. package/core/internal/curves.ts +22 -0
  126. package/core/internal/quantity.ts +16 -0
  127. package/erc4337/UserOperationReceipt.ts +1 -6
  128. package/index.ts +73 -0
  129. package/package.json +21 -1
  130. package/tempo/AuthorizationTempo.test.ts +8 -4
  131. package/tempo/KeyAuthorization.test-d.ts +68 -33
  132. package/tempo/KeyAuthorization.test.ts +89 -8
  133. package/tempo/KeyAuthorization.ts +124 -61
  134. package/tempo/MultisigConfig.test.ts +34 -26
  135. package/tempo/MultisigConfig.ts +54 -24
  136. package/tempo/MultisigOperation.test.ts +79 -550
  137. package/tempo/MultisigOperation.ts +31 -146
  138. package/tempo/MultisigSimulation.test-d.ts +2 -2
  139. package/tempo/MultisigSimulation.test.ts +21 -110
  140. package/tempo/MultisigSimulation.ts +25 -99
  141. package/tempo/SignatureEnvelope.test-d.ts +29 -67
  142. package/tempo/SignatureEnvelope.test.ts +301 -568
  143. package/tempo/SignatureEnvelope.ts +156 -274
  144. package/tempo/TransactionRequest.test-d.ts +6 -5
  145. package/tempo/TransactionRequest.test.ts +17 -80
  146. package/tempo/TransactionRequest.ts +23 -3
  147. package/tempo/index.ts +3 -1
  148. package/tempo/multisig.e2e.test.ts +322 -861
  149. package/version.ts +1 -1
@@ -0,0 +1,606 @@
1
+ import * as Address from './Address.js'
2
+ import * as Errors from './Errors.js'
3
+ import * as Hex from './Hex.js'
4
+ import { p256N, secp256k1N } from './internal/curves.js'
5
+ import type { Compute, UnionPartialBy } from './internal/types.js'
6
+ import * as PublicKey from './PublicKey.js'
7
+ import * as Signature from './Signature.js'
8
+
9
+ /** Signature schemes supported by EIP-8141. */
10
+ export const schemes = { arbitrary: 0, p256: 2, secp256k1: 1 } as const
11
+
12
+ /** A supported numeric or named frame signature scheme. */
13
+ export type Scheme =
14
+ | keyof typeof schemes
15
+ | (typeof schemes)[keyof typeof schemes]
16
+
17
+ /** Contract-defined signature bytes. */
18
+ export type Arbitrary = {
19
+ /** Explicit nonzero digest. Omit or use empty bytes for the transaction signing hash. */
20
+ payload?: Hex.Hex | undefined
21
+ /** Contract-defined verification scheme. */
22
+ scheme: 0 | 'arbitrary'
23
+ /** Opaque witness bytes. */
24
+ signature: Hex.Hex
25
+ /** Arbitrary signatures have no signer metadata. */
26
+ signer?: undefined
27
+ }
28
+
29
+ /** A structured secp256k1 signature entry. */
30
+ export type Secp256k1 = {
31
+ /** Explicit nonzero digest. Omit or use empty bytes for the transaction signing hash. */
32
+ payload?: Hex.Hex | undefined
33
+ /** secp256k1 verification scheme. */
34
+ scheme: 1 | 'secp256k1'
35
+ /** Recovered signature. Omit for an unsigned entry. */
36
+ signature?: Signature.Signature | undefined
37
+ /** Signer address. Omit to use the transaction sender. */
38
+ signer?: Address.Address | undefined
39
+ }
40
+
41
+ /** A structured P-256 signature entry. */
42
+ export type P256 = {
43
+ /** Explicit nonzero digest. Omit or use empty bytes for the transaction signing hash. */
44
+ payload?: Hex.Hex | undefined
45
+ /** P-256 verification scheme. */
46
+ scheme: 2 | 'p256'
47
+ /** Signer address. Omit to use the transaction sender. */
48
+ signer?: Address.Address | undefined
49
+ } & (
50
+ | {
51
+ /** Uncompressed P-256 public key. */
52
+ publicKey: PublicKey.PublicKey
53
+ /** P-256 signature. */
54
+ signature: Signature.Signature<false>
55
+ }
56
+ | {
57
+ /** Public key, if already known. Empty wire signatures do not retain it. */
58
+ publicKey?: PublicKey.PublicKey | undefined
59
+ /** Omit for an unsigned entry. */
60
+ signature?: undefined
61
+ }
62
+ )
63
+
64
+ /** An EIP-8141 signature entry. Empty payload selects the transaction signing hash. */
65
+ export type FrameSignature = Arbitrary | Secp256k1 | P256
66
+
67
+ /** JSON-RPC representation of a frame signature. */
68
+ export type Rpc = {
69
+ /** Explicit digest, or empty bytes for the transaction signing hash. */
70
+ msg: Hex.Hex
71
+ /** Signature verification scheme. */
72
+ scheme: 0 | 1 | 2
73
+ /** Encoded signature bytes. */
74
+ signature: Hex.Hex
75
+ /** Signer address; absent for the transaction sender. */
76
+ signer?: Address.Address | null | undefined
77
+ }
78
+
79
+ /** RLP-ready signature entry. The payload occupies the specification's `msg` field. */
80
+ export type Tuple = readonly [
81
+ scheme: Hex.Hex,
82
+ signer: Hex.Hex,
83
+ payload: Hex.Hex,
84
+ signature: Hex.Hex,
85
+ ]
86
+
87
+ /**
88
+ * Asserts that a {@link ox#FrameSignature.FrameSignature} is structurally valid.
89
+ *
90
+ * Checks metadata, signature scalars, and public key shape without verifying
91
+ * authorization.
92
+ *
93
+ * @example
94
+ * ### Basic Usage
95
+ *
96
+ * Unsigned protocol entries are accepted by default.
97
+ *
98
+ * ```ts twoslash
99
+ * import { FrameSignature } from 'ox'
100
+ *
101
+ * FrameSignature.assert({
102
+ * payload: '0x',
103
+ * scheme: 'secp256k1'
104
+ * })
105
+ * ```
106
+ *
107
+ * @example
108
+ * ### Requiring a Signature
109
+ *
110
+ * Set `signed` to require a protocol signature.
111
+ *
112
+ * ```ts twoslash
113
+ * import { FrameSignature, Hash, Secp256k1 } from 'ox'
114
+ *
115
+ * const payload = Hash.keccak256('0xdeadbeef')
116
+ * const privateKey = Secp256k1.randomPrivateKey()
117
+ * const signature = Secp256k1.sign({ payload, privateKey })
118
+ *
119
+ * const entry = FrameSignature.from({
120
+ * payload,
121
+ * scheme: 'secp256k1',
122
+ * signature
123
+ * })
124
+ *
125
+ * FrameSignature.assert(entry, { signed: true })
126
+ * ```
127
+ *
128
+ * @param entry - The signature entry to assert.
129
+ * @param options - Validation options.
130
+ */
131
+ export function assert(
132
+ entry: FrameSignature,
133
+ options: assert.Options = {},
134
+ ): void {
135
+ const scheme =
136
+ typeof entry.scheme === 'string' ? schemes[entry.scheme] : entry.scheme
137
+ if (!Number.isInteger(scheme) || scheme < 0 || scheme > 2)
138
+ throw new InvalidError('Unsupported signature scheme.')
139
+ if (scheme === 0 && entry.signer !== undefined)
140
+ throw new InvalidError('Arbitrary signatures cannot specify a signer.')
141
+ if (entry.signer !== undefined)
142
+ Address.assert(entry.signer, { strict: false })
143
+ const payload = entry.payload ?? '0x'
144
+ Hex.assert(payload, { strict: true })
145
+ if (
146
+ payload !== '0x' &&
147
+ (payload.length !== 66 || Hex.toBigInt(payload) === 0n)
148
+ )
149
+ throw new InvalidError('payload must be empty or a nonzero 32-byte digest.')
150
+ switch (entry.scheme) {
151
+ case 0:
152
+ case 'arbitrary':
153
+ Hex.assert(entry.signature, { strict: true })
154
+ if (entry.signature.length % 2 !== 0)
155
+ throw new InvalidError('signature must contain whole bytes.')
156
+ return
157
+ }
158
+ if (entry.scheme === 2 || entry.scheme === 'p256') {
159
+ if (entry.publicKey !== undefined) {
160
+ PublicKey.assert(entry.publicKey, { compressed: false })
161
+ for (const coordinate of [entry.publicKey.x, entry.publicKey.y]) {
162
+ if (
163
+ typeof coordinate !== 'bigint' ||
164
+ coordinate < 0n ||
165
+ coordinate >= 2n ** 256n
166
+ )
167
+ throw new InvalidError('Public key coordinates must fit in 32 bytes.')
168
+ }
169
+ }
170
+ if (entry.signature !== undefined && entry.publicKey === undefined)
171
+ throw new InvalidError('P-256 signatures require a public key.')
172
+ }
173
+ if (entry.signature === undefined) {
174
+ if (options.signed) throw new InvalidError('Signature is required.')
175
+ return
176
+ }
177
+ if (
178
+ typeof entry.signature.r !== 'bigint' ||
179
+ typeof entry.signature.s !== 'bigint'
180
+ )
181
+ throw new InvalidError('Signature scalars must be bigint.')
182
+ if (
183
+ (scheme === 1 || entry.signature.yParity !== undefined) &&
184
+ entry.signature.yParity !== 0 &&
185
+ entry.signature.yParity !== 1
186
+ )
187
+ throw new InvalidError('Signature parity must be 0 or 1.')
188
+ Signature.assert(entry.signature, { recovered: scheme === 1 })
189
+ const r = entry.signature.r
190
+ const s = entry.signature.s
191
+ const order = scheme === 1 ? secp256k1N : p256N
192
+ if (
193
+ r === 0n ||
194
+ r >= order ||
195
+ s === 0n ||
196
+ s >= order ||
197
+ (scheme === 1 && s > order / 2n)
198
+ )
199
+ throw new InvalidError(
200
+ 'Signature scalars must be in range; secp256k1 requires low-s.',
201
+ )
202
+ }
203
+
204
+ export declare namespace assert {
205
+ type Options = {
206
+ /**
207
+ * Require a protocol signature. Does not perform cryptographic verification.
208
+ * @default false
209
+ */
210
+ signed?: boolean | undefined
211
+ }
212
+ type ErrorType =
213
+ | InvalidError
214
+ | Address.assert.ErrorType
215
+ | Hex.assert.ErrorType
216
+ | Hex.toBigInt.ErrorType
217
+ | PublicKey.assert.ErrorType
218
+ | Signature.assert.ErrorType
219
+ | Errors.GlobalErrorType
220
+ }
221
+
222
+ /**
223
+ * Coerces a value into a {@link ox#FrameSignature.FrameSignature}.
224
+ *
225
+ * Accepts arbitrary signature bytes or a structured signature entry. Omitted
226
+ * `scheme` and `payload` default to `'arbitrary'` and `'0x'`, respectively.
227
+ * An empty payload selects the canonical transaction signing hash; an explicit
228
+ * payload must be a nonzero 32-byte digest.
229
+ *
230
+ * @example
231
+ * ### From Hex
232
+ *
233
+ * Wrap arbitrary witness bytes for contract-defined verification.
234
+ *
235
+ * ```ts twoslash
236
+ * import { FrameSignature } from 'ox'
237
+ *
238
+ * const entry = FrameSignature.from('0xaabb')
239
+ * // @log: { payload: '0x', scheme: 'arbitrary', signature: '0xaabb' }
240
+ * ```
241
+ *
242
+ * @example
243
+ * ### Secp256k1
244
+ *
245
+ * Wrap a signature over an explicit digest, retaining the same payload in the entry.
246
+ *
247
+ * ```ts twoslash
248
+ * import { FrameSignature, Hash, Secp256k1 } from 'ox'
249
+ *
250
+ * const payload = Hash.keccak256('0xdeadbeef')
251
+ * const privateKey = Secp256k1.randomPrivateKey()
252
+ * const signature = Secp256k1.sign({ payload, privateKey })
253
+ *
254
+ * const entry = FrameSignature.from({
255
+ * payload,
256
+ * scheme: 'secp256k1',
257
+ * signature
258
+ * })
259
+ * ```
260
+ *
261
+ * @example
262
+ * ### P256
263
+ *
264
+ * Include the public key with a P-256 signature over an explicit digest.
265
+ *
266
+ * ```ts twoslash
267
+ * import { FrameSignature, Hash, P256 } from 'ox'
268
+ *
269
+ * const { privateKey, publicKey } = P256.createKeyPair()
270
+ * const payload = Hash.keccak256('0xdeadbeef')
271
+ * const signature = P256.sign({ payload, privateKey })
272
+ *
273
+ * const entry = FrameSignature.from({
274
+ * payload,
275
+ * publicKey,
276
+ * scheme: 'p256',
277
+ * signature
278
+ * })
279
+ * ```
280
+ *
281
+ * @example
282
+ * ### Unsigned Entries
283
+ *
284
+ * Omit the signature to prepare a protocol entry before signing the transaction.
285
+ *
286
+ * ```ts twoslash
287
+ * import { FrameSignature } from 'ox'
288
+ *
289
+ * const entry = FrameSignature.from({ scheme: 'secp256k1' })
290
+ * // @log: { payload: '0x', scheme: 'secp256k1' }
291
+ * ```
292
+ *
293
+ * @param entry - Arbitrary signature bytes or a structured signature entry.
294
+ * @returns The validated entry, preserving supplied signature and scheme types.
295
+ */
296
+ export function from<const entry extends from.Input>(
297
+ entry: entry | from.Input,
298
+ ): from.ReturnType<entry> {
299
+ const result = (
300
+ typeof entry === 'string'
301
+ ? { payload: '0x', scheme: 'arbitrary', signature: entry }
302
+ : {
303
+ ...entry,
304
+ payload: entry.payload ?? '0x',
305
+ scheme: entry.scheme ?? 'arbitrary',
306
+ }
307
+ ) as FrameSignature
308
+ assert(result)
309
+ return result as from.ReturnType<entry>
310
+ }
311
+
312
+ export declare namespace from {
313
+ type Input =
314
+ | Hex.Hex
315
+ | UnionPartialBy<Arbitrary, 'scheme' | 'payload'>
316
+ | UnionPartialBy<Secp256k1 | P256, 'payload'>
317
+ type ReturnType<entry extends Input = Input> = entry extends Hex.Hex
318
+ ? { payload: '0x'; scheme: 'arbitrary'; signature: entry }
319
+ : entry extends Input
320
+ ? Compute<
321
+ Omit<entry, 'scheme' | 'payload'> & {
322
+ payload: entry extends { payload: infer payload extends Hex.Hex }
323
+ ? payload
324
+ : 'payload' extends keyof entry
325
+ ? Exclude<entry['payload'], undefined> | '0x'
326
+ : '0x'
327
+ scheme: entry extends { scheme: infer scheme extends Scheme }
328
+ ? scheme
329
+ : 'scheme' extends keyof entry
330
+ ? Exclude<entry['scheme'], undefined> | 'arbitrary'
331
+ : 'arbitrary'
332
+ }
333
+ >
334
+ : never
335
+ type ErrorType = assert.ErrorType
336
+ }
337
+
338
+ /**
339
+ * Converts an RPC signature entry to a structured frame signature.
340
+ *
341
+ * @example
342
+ * ### Basic Usage
343
+ *
344
+ * ```ts twoslash
345
+ * import { FrameSignature } from 'ox'
346
+ *
347
+ * const entry = FrameSignature.fromRpc({
348
+ * msg: '0x',
349
+ * scheme: 0,
350
+ * signature: '0xdeadbeef'
351
+ * })
352
+ * ```
353
+ *
354
+ * @param entry - The value to convert.
355
+ * @returns The converted value.
356
+ */
357
+ export function fromRpc(entry: Rpc): FrameSignature {
358
+ return fromTuple([
359
+ entry.scheme === 0 ? '0x' : Hex.fromNumber(entry.scheme, { size: 1 }),
360
+ entry.signer ?? '0x',
361
+ entry.msg,
362
+ entry.signature,
363
+ ])
364
+ }
365
+
366
+ export declare namespace fromRpc {
367
+ type ErrorType = fromTuple.ErrorType | Hex.fromNumber.ErrorType
368
+ }
369
+
370
+ /**
371
+ * Converts a structured frame signature to its RPC representation.
372
+ *
373
+ * @example
374
+ * ### Basic Usage
375
+ *
376
+ * ```ts twoslash
377
+ * import { FrameSignature } from 'ox'
378
+ *
379
+ * const entry = FrameSignature.from('0xdeadbeef')
380
+ * const rpc = FrameSignature.toRpc(entry)
381
+ * ```
382
+ *
383
+ * @param entry - The value to convert.
384
+ * @returns The converted value.
385
+ */
386
+ export function toRpc(entry: FrameSignature): Rpc {
387
+ const [scheme, signer, msg, signature] = toTuple(entry)
388
+ return {
389
+ msg,
390
+ scheme: (scheme === '0x' ? 0 : Hex.toNumber(scheme)) as Rpc['scheme'],
391
+ signature,
392
+ ...(signer === '0x' ? {} : { signer }),
393
+ }
394
+ }
395
+
396
+ export declare namespace toRpc {
397
+ type ErrorType = toTuple.ErrorType | Hex.toNumber.ErrorType
398
+ }
399
+
400
+ /**
401
+ * Converts a {@link ox#FrameSignature.Tuple} to a structured
402
+ * {@link ox#FrameSignature.FrameSignature}.
403
+ *
404
+ * Returns a named scheme and unpacks protocol signatures. Empty protocol signatures
405
+ * become unsigned entries. Rejects noncanonical encodings.
406
+ *
407
+ * @example
408
+ * ### Basic Usage
409
+ *
410
+ * Decode an arbitrary signature tuple.
411
+ *
412
+ * ```ts twoslash
413
+ * import { FrameSignature } from 'ox'
414
+ *
415
+ * const entry = FrameSignature.fromTuple([
416
+ * '0x',
417
+ * '0x',
418
+ * '0x',
419
+ * '0xaabb'
420
+ * ])
421
+ * // @log: { payload: '0x', scheme: 'arbitrary', signature: '0xaabb' }
422
+ * ```
423
+ *
424
+ * @param tuple - The signature tuple to convert.
425
+ * @returns The decoded signature entry.
426
+ */
427
+ export function fromTuple(tuple: Tuple): FrameSignature {
428
+ if (!Array.isArray(tuple) || tuple.length !== 4)
429
+ throw new InvalidError('Expected [scheme, signer, payload, signature].')
430
+ const [scheme, signer, payload, signature] = tuple
431
+ Hex.assert(signer, { strict: true })
432
+ Hex.assert(signature, { strict: true })
433
+ if (signature.length % 2 !== 0)
434
+ throw new InvalidError('signature must contain whole bytes.')
435
+ if (scheme !== '0x' && scheme !== '0x01' && scheme !== '0x02')
436
+ throw new InvalidError(
437
+ 'Expected a canonical scheme encoding: empty bytes, 0x01, or 0x02.',
438
+ )
439
+ const metadata = { ...(signer === '0x' ? {} : { signer }), payload }
440
+ if (scheme === '0x') {
441
+ if (signer !== '0x')
442
+ throw new InvalidError('Arbitrary signatures cannot specify a signer.')
443
+ return from({ payload, scheme: 'arbitrary', signature })
444
+ }
445
+ if (signature === '0x')
446
+ return scheme === '0x01'
447
+ ? from({ ...metadata, scheme: 'secp256k1' })
448
+ : from({ ...metadata, scheme: 'p256' })
449
+ if (Hex.size(signature) !== (scheme === '0x01' ? 65 : 128))
450
+ throw new InvalidError('Invalid protocol signature length.')
451
+ if (scheme === '0x01') {
452
+ if (Hex.toNumber(Hex.slice(signature, 0, 1)) > 1)
453
+ throw new InvalidError('Recovery parity must be 0 or 1.')
454
+ return from({
455
+ ...metadata,
456
+ scheme: 'secp256k1',
457
+ signature: {
458
+ r: Hex.toBigInt(Hex.slice(signature, 1, 33)),
459
+ s: Hex.toBigInt(Hex.slice(signature, 33, 65)),
460
+ yParity: Hex.toNumber(Hex.slice(signature, 0, 1)),
461
+ },
462
+ })
463
+ }
464
+ const s = Hex.toBigInt(Hex.slice(signature, 32, 64))
465
+ if (s > p256N / 2n)
466
+ throw new InvalidError('P-256 wire signatures require low-s.')
467
+ return from({
468
+ ...metadata,
469
+ publicKey: {
470
+ prefix: 4,
471
+ x: Hex.toBigInt(Hex.slice(signature, 64, 96)),
472
+ y: Hex.toBigInt(Hex.slice(signature, 96, 128)),
473
+ },
474
+ scheme: 'p256',
475
+ signature: { r: Hex.toBigInt(Hex.slice(signature, 0, 32)), s },
476
+ })
477
+ }
478
+
479
+ export declare namespace fromTuple {
480
+ type ErrorType =
481
+ | assert.ErrorType
482
+ | Hex.slice.ErrorType
483
+ | Hex.toNumber.ErrorType
484
+ | Hex.toBytes.ErrorType
485
+ | Hex.toBigInt.ErrorType
486
+ }
487
+
488
+ /**
489
+ * Converts a {@link ox#FrameSignature.FrameSignature} to its RLP-ready
490
+ * {@link ox#FrameSignature.Tuple}.
491
+ *
492
+ * Encodes numeric scheme identifiers and packs protocol signatures without mutating
493
+ * the entry. Omitted protocol signatures become empty bytes.
494
+ *
495
+ * @example
496
+ * ### Basic Usage
497
+ *
498
+ * Encode arbitrary witness bytes with the default scheme and payload.
499
+ *
500
+ * ```ts twoslash
501
+ * import { FrameSignature } from 'ox'
502
+ *
503
+ * const entry = FrameSignature.from('0xaabb')
504
+ * const tuple = FrameSignature.toTuple(entry)
505
+ * // @log: ['0x', '0x', '0x', '0xaabb']
506
+ * ```
507
+ *
508
+ * @param entry - The signature entry to convert.
509
+ * @returns The encoded signature tuple.
510
+ */
511
+ export function toTuple(entry: FrameSignature): Tuple {
512
+ assert(entry)
513
+ const scheme =
514
+ typeof entry.scheme === 'string' ? schemes[entry.scheme] : entry.scheme
515
+ const signature = (() => {
516
+ switch (entry.scheme) {
517
+ case 0:
518
+ case 'arbitrary':
519
+ return entry.signature
520
+ case 1:
521
+ case 'secp256k1':
522
+ return entry.signature === undefined
523
+ ? '0x'
524
+ : Hex.concat(
525
+ Hex.fromNumber(entry.signature.yParity, { size: 1 }),
526
+ Hex.fromNumber(entry.signature.r, { size: 32 }),
527
+ Hex.fromNumber(entry.signature.s, { size: 32 }),
528
+ )
529
+ case 2:
530
+ case 'p256': {
531
+ if (entry.signature === undefined) return '0x'
532
+ const { publicKey, signature } = entry
533
+ const s = signature.s
534
+ return Hex.concat(
535
+ Hex.fromNumber(signature.r, { size: 32 }),
536
+ Hex.fromNumber(s > p256N / 2n ? p256N - s : s, { size: 32 }),
537
+ Hex.fromNumber(publicKey.x, { size: 32 }),
538
+ Hex.fromNumber(publicKey.y, { size: 32 }),
539
+ )
540
+ }
541
+ }
542
+ })()
543
+ return [
544
+ scheme ? Hex.fromNumber(scheme, { size: 1 }) : '0x',
545
+ entry.signer ?? '0x',
546
+ entry.payload ?? '0x',
547
+ signature,
548
+ ]
549
+ }
550
+
551
+ export declare namespace toTuple {
552
+ type ErrorType =
553
+ | assert.ErrorType
554
+ | Hex.fromNumber.ErrorType
555
+ | Hex.fromBytes.ErrorType
556
+ | Hex.concat.ErrorType
557
+ }
558
+
559
+ /**
560
+ * Returns whether a {@link ox#FrameSignature.FrameSignature} is structurally valid.
561
+ *
562
+ * Performs the same checks as {@link ox#FrameSignature.(assert:function)}, returning
563
+ * `false` instead of throwing. Does not verify that the signature authorizes a payload.
564
+ *
565
+ * @example
566
+ * ### Basic Usage
567
+ *
568
+ * Require a signature when checking a protocol entry.
569
+ *
570
+ * ```ts twoslash
571
+ * import { FrameSignature } from 'ox'
572
+ *
573
+ * const entry = FrameSignature.from({ scheme: 'secp256k1' })
574
+ * const valid = FrameSignature.validate(entry, {
575
+ * signed: true
576
+ * })
577
+ * // @log: false
578
+ * ```
579
+ *
580
+ * @param entry - The signature entry to validate.
581
+ * @param options - Validation options.
582
+ * @returns Whether the entry is structurally valid.
583
+ */
584
+ export function validate(
585
+ entry: FrameSignature,
586
+ options: assert.Options = {},
587
+ ): boolean {
588
+ try {
589
+ assert(entry, options)
590
+ return true
591
+ } catch {
592
+ return false
593
+ }
594
+ }
595
+
596
+ export declare namespace validate {
597
+ type ErrorType = Errors.GlobalErrorType
598
+ }
599
+
600
+ /** Thrown when frame signature metadata or encoding is invalid. */
601
+ export class InvalidError extends Errors.BaseError {
602
+ override readonly name = 'FrameSignature.InvalidError'
603
+ constructor(details: string) {
604
+ super('Invalid frame signature.', { details })
605
+ }
606
+ }