ox 0.9.16 → 0.10.0

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 (216) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +4 -4
  3. package/TxEnvelope/package.json +6 -0
  4. package/TxEnvelopeEip1559/package.json +6 -0
  5. package/TxEnvelopeEip2930/package.json +6 -0
  6. package/TxEnvelopeEip4844/package.json +6 -0
  7. package/TxEnvelopeEip7702/package.json +6 -0
  8. package/TxEnvelopeLegacy/package.json +6 -0
  9. package/_cjs/core/{TransactionEnvelope.js → TxEnvelope.js} +1 -1
  10. package/_cjs/core/TxEnvelope.js.map +1 -0
  11. package/_cjs/core/{TransactionEnvelopeEip1559.js → TxEnvelopeEip1559.js} +2 -2
  12. package/_cjs/core/TxEnvelopeEip1559.js.map +1 -0
  13. package/_cjs/core/{TransactionEnvelopeEip2930.js → TxEnvelopeEip2930.js} +2 -2
  14. package/_cjs/core/TxEnvelopeEip2930.js.map +1 -0
  15. package/_cjs/core/{TransactionEnvelopeEip4844.js → TxEnvelopeEip4844.js} +4 -4
  16. package/_cjs/core/TxEnvelopeEip4844.js.map +1 -0
  17. package/_cjs/core/{TransactionEnvelopeEip7702.js → TxEnvelopeEip7702.js} +4 -4
  18. package/_cjs/core/TxEnvelopeEip7702.js.map +1 -0
  19. package/_cjs/core/{TransactionEnvelopeLegacy.js → TxEnvelopeLegacy.js} +2 -2
  20. package/_cjs/core/TxEnvelopeLegacy.js.map +1 -0
  21. package/_cjs/core/WebAuthnP256.js +1 -1
  22. package/_cjs/core/WebAuthnP256.js.map +1 -1
  23. package/_cjs/erc8021/Attribution.js +36 -6
  24. package/_cjs/erc8021/Attribution.js.map +1 -1
  25. package/_cjs/index.docs.js +1 -0
  26. package/_cjs/index.docs.js.map +1 -1
  27. package/_cjs/index.js +7 -7
  28. package/_cjs/index.js.map +1 -1
  29. package/_cjs/tempo/AuthorizationTempo.js +101 -0
  30. package/_cjs/tempo/AuthorizationTempo.js.map +1 -0
  31. package/_cjs/tempo/KeyAuthorization.js +123 -0
  32. package/_cjs/tempo/KeyAuthorization.js.map +1 -0
  33. package/_cjs/tempo/PoolId.js +10 -0
  34. package/_cjs/tempo/PoolId.js.map +1 -0
  35. package/_cjs/tempo/SignatureEnvelope.js +394 -0
  36. package/_cjs/tempo/SignatureEnvelope.js.map +1 -0
  37. package/_cjs/tempo/Tick.js +77 -0
  38. package/_cjs/tempo/Tick.js.map +1 -0
  39. package/_cjs/tempo/TokenId.js +28 -0
  40. package/_cjs/tempo/TokenId.js.map +1 -0
  41. package/_cjs/tempo/TokenRole.js +26 -0
  42. package/_cjs/tempo/TokenRole.js.map +1 -0
  43. package/_cjs/tempo/Transaction.js +80 -0
  44. package/_cjs/tempo/Transaction.js.map +1 -0
  45. package/_cjs/tempo/TransactionReceipt.js +26 -0
  46. package/_cjs/tempo/TransactionReceipt.js.map +1 -0
  47. package/_cjs/tempo/TransactionRequest.js +53 -0
  48. package/_cjs/tempo/TransactionRequest.js.map +1 -0
  49. package/_cjs/tempo/TxEnvelopeTempo.js +267 -0
  50. package/_cjs/tempo/TxEnvelopeTempo.js.map +1 -0
  51. package/_cjs/tempo/index.js +15 -0
  52. package/_cjs/tempo/index.js.map +1 -0
  53. package/_cjs/version.js +1 -1
  54. package/_esm/core/Blobs.js +8 -8
  55. package/_esm/core/{TransactionEnvelope.js → TxEnvelope.js} +11 -11
  56. package/_esm/core/TxEnvelope.js.map +1 -0
  57. package/_esm/core/{TransactionEnvelopeEip1559.js → TxEnvelopeEip1559.js} +42 -42
  58. package/_esm/core/TxEnvelopeEip1559.js.map +1 -0
  59. package/_esm/core/{TransactionEnvelopeEip2930.js → TxEnvelopeEip2930.js} +43 -43
  60. package/_esm/core/TxEnvelopeEip2930.js.map +1 -0
  61. package/_esm/core/{TransactionEnvelopeEip4844.js → TxEnvelopeEip4844.js} +42 -42
  62. package/_esm/core/TxEnvelopeEip4844.js.map +1 -0
  63. package/_esm/core/{TransactionEnvelopeEip7702.js → TxEnvelopeEip7702.js} +40 -40
  64. package/_esm/core/TxEnvelopeEip7702.js.map +1 -0
  65. package/_esm/core/{TransactionEnvelopeLegacy.js → TxEnvelopeLegacy.js} +42 -42
  66. package/_esm/core/TxEnvelopeLegacy.js.map +1 -0
  67. package/_esm/core/WebAuthnP256.js +1 -1
  68. package/_esm/core/WebAuthnP256.js.map +1 -1
  69. package/_esm/erc8021/Attribution.js +58 -13
  70. package/_esm/erc8021/Attribution.js.map +1 -1
  71. package/_esm/index.docs.js +1 -0
  72. package/_esm/index.docs.js.map +1 -1
  73. package/_esm/index.js +192 -192
  74. package/_esm/index.js.map +1 -1
  75. package/_esm/tempo/AuthorizationTempo.js +664 -0
  76. package/_esm/tempo/AuthorizationTempo.js.map +1 -0
  77. package/_esm/tempo/KeyAuthorization.js +426 -0
  78. package/_esm/tempo/KeyAuthorization.js.map +1 -0
  79. package/_esm/tempo/PoolId.js +28 -0
  80. package/_esm/tempo/PoolId.js.map +1 -0
  81. package/_esm/tempo/SignatureEnvelope.js +660 -0
  82. package/_esm/tempo/SignatureEnvelope.js.map +1 -0
  83. package/_esm/tempo/Tick.js +147 -0
  84. package/_esm/tempo/Tick.js.map +1 -0
  85. package/_esm/tempo/TokenId.js +71 -0
  86. package/_esm/tempo/TokenId.js.map +1 -0
  87. package/_esm/tempo/TokenRole.js +40 -0
  88. package/_esm/tempo/TokenRole.js.map +1 -0
  89. package/_esm/tempo/Transaction.js +167 -0
  90. package/_esm/tempo/Transaction.js.map +1 -0
  91. package/_esm/tempo/TransactionReceipt.js +138 -0
  92. package/_esm/tempo/TransactionReceipt.js.map +1 -0
  93. package/_esm/tempo/TransactionRequest.js +99 -0
  94. package/_esm/tempo/TransactionRequest.js.map +1 -0
  95. package/_esm/tempo/TxEnvelopeTempo.js +607 -0
  96. package/_esm/tempo/TxEnvelopeTempo.js.map +1 -0
  97. package/_esm/tempo/index.js +298 -0
  98. package/_esm/tempo/index.js.map +1 -0
  99. package/_esm/version.js +1 -1
  100. package/_types/core/Blobs.d.ts +8 -8
  101. package/_types/core/{TransactionEnvelope.d.ts → TxEnvelope.d.ts} +11 -11
  102. package/_types/core/TxEnvelope.d.ts.map +1 -0
  103. package/_types/core/{TransactionEnvelopeEip1559.d.ts → TxEnvelopeEip1559.d.ts} +54 -54
  104. package/_types/core/TxEnvelopeEip1559.d.ts.map +1 -0
  105. package/_types/core/{TransactionEnvelopeEip2930.d.ts → TxEnvelopeEip2930.d.ts} +55 -55
  106. package/_types/core/TxEnvelopeEip2930.d.ts.map +1 -0
  107. package/_types/core/{TransactionEnvelopeEip4844.d.ts → TxEnvelopeEip4844.d.ts} +54 -54
  108. package/_types/core/TxEnvelopeEip4844.d.ts.map +1 -0
  109. package/_types/core/{TransactionEnvelopeEip7702.d.ts → TxEnvelopeEip7702.d.ts} +49 -49
  110. package/_types/core/TxEnvelopeEip7702.d.ts.map +1 -0
  111. package/_types/core/{TransactionEnvelopeLegacy.d.ts → TxEnvelopeLegacy.d.ts} +54 -54
  112. package/_types/core/TxEnvelopeLegacy.d.ts.map +1 -0
  113. package/_types/core/WebAuthnP256.d.ts +1 -1
  114. package/_types/core/WebAuthnP256.d.ts.map +1 -1
  115. package/_types/erc8021/Attribution.d.ts +20 -6
  116. package/_types/erc8021/Attribution.d.ts.map +1 -1
  117. package/_types/index.d.ts +192 -192
  118. package/_types/index.d.ts.map +1 -1
  119. package/_types/index.docs.d.ts +1 -0
  120. package/_types/index.docs.d.ts.map +1 -1
  121. package/_types/tempo/AuthorizationTempo.d.ts +688 -0
  122. package/_types/tempo/AuthorizationTempo.d.ts.map +1 -0
  123. package/_types/tempo/KeyAuthorization.d.ts +437 -0
  124. package/_types/tempo/KeyAuthorization.d.ts.map +1 -0
  125. package/_types/tempo/PoolId.d.ts +33 -0
  126. package/_types/tempo/PoolId.d.ts.map +1 -0
  127. package/_types/tempo/SignatureEnvelope.d.ts +438 -0
  128. package/_types/tempo/SignatureEnvelope.d.ts.map +1 -0
  129. package/_types/tempo/Tick.d.ts +120 -0
  130. package/_types/tempo/Tick.d.ts.map +1 -0
  131. package/_types/tempo/TokenId.d.ts +55 -0
  132. package/_types/tempo/TokenId.d.ts.map +1 -0
  133. package/_types/tempo/TokenRole.d.ts +29 -0
  134. package/_types/tempo/TokenRole.d.ts.map +1 -0
  135. package/_types/tempo/Transaction.d.ts +208 -0
  136. package/_types/tempo/Transaction.d.ts.map +1 -0
  137. package/_types/tempo/TransactionReceipt.d.ts +165 -0
  138. package/_types/tempo/TransactionReceipt.d.ts.map +1 -0
  139. package/_types/tempo/TransactionRequest.d.ts +89 -0
  140. package/_types/tempo/TransactionRequest.d.ts.map +1 -0
  141. package/_types/tempo/TxEnvelopeTempo.d.ts +551 -0
  142. package/_types/tempo/TxEnvelopeTempo.d.ts.map +1 -0
  143. package/_types/tempo/index.d.ts +300 -0
  144. package/_types/tempo/index.d.ts.map +1 -0
  145. package/_types/version.d.ts +1 -1
  146. package/core/Blobs.ts +8 -8
  147. package/core/{TransactionEnvelope.ts → TxEnvelope.ts} +10 -10
  148. package/core/{TransactionEnvelopeEip1559.ts → TxEnvelopeEip1559.ts} +60 -69
  149. package/core/{TransactionEnvelopeEip2930.ts → TxEnvelopeEip2930.ts} +61 -72
  150. package/core/{TransactionEnvelopeEip4844.ts → TxEnvelopeEip4844.ts} +62 -71
  151. package/core/{TransactionEnvelopeEip7702.ts → TxEnvelopeEip7702.ts} +58 -67
  152. package/core/{TransactionEnvelopeLegacy.ts → TxEnvelopeLegacy.ts} +59 -68
  153. package/core/WebAuthnP256.ts +3 -1
  154. package/erc8021/Attribution.ts +77 -15
  155. package/index.docs.ts +1 -0
  156. package/index.ts +192 -195
  157. package/package.json +91 -31
  158. package/tempo/AuthorizationTempo/package.json +6 -0
  159. package/tempo/AuthorizationTempo.test.ts +1293 -0
  160. package/tempo/AuthorizationTempo.ts +884 -0
  161. package/tempo/KeyAuthorization/package.json +6 -0
  162. package/tempo/KeyAuthorization.test.ts +1373 -0
  163. package/tempo/KeyAuthorization.ts +622 -0
  164. package/tempo/PoolId/package.json +6 -0
  165. package/tempo/PoolId.test.ts +33 -0
  166. package/tempo/PoolId.ts +42 -0
  167. package/tempo/SignatureEnvelope/package.json +6 -0
  168. package/tempo/SignatureEnvelope.test.ts +1877 -0
  169. package/tempo/SignatureEnvelope.ts +973 -0
  170. package/tempo/Tick/package.json +6 -0
  171. package/tempo/Tick.test.ts +281 -0
  172. package/tempo/Tick.ts +186 -0
  173. package/tempo/TokenId/package.json +6 -0
  174. package/tempo/TokenId.test.ts +40 -0
  175. package/tempo/TokenId.ts +80 -0
  176. package/tempo/TokenRole/package.json +6 -0
  177. package/tempo/TokenRole.test.ts +16 -0
  178. package/tempo/TokenRole.ts +45 -0
  179. package/tempo/Transaction/package.json +6 -0
  180. package/tempo/Transaction.test.ts +523 -0
  181. package/tempo/Transaction.ts +339 -0
  182. package/tempo/TransactionReceipt/package.json +6 -0
  183. package/tempo/TransactionReceipt.ts +200 -0
  184. package/tempo/TransactionRequest/package.json +6 -0
  185. package/tempo/TransactionRequest.ts +160 -0
  186. package/tempo/TxEnvelopeTempo/package.json +6 -0
  187. package/tempo/TxEnvelopeTempo.test.ts +1371 -0
  188. package/tempo/TxEnvelopeTempo.ts +972 -0
  189. package/tempo/e2e.test.ts +1387 -0
  190. package/tempo/index.ts +308 -0
  191. package/tempo/package.json +6 -0
  192. package/version.ts +1 -1
  193. package/TransactionEnvelope/package.json +0 -6
  194. package/TransactionEnvelopeEip1559/package.json +0 -6
  195. package/TransactionEnvelopeEip2930/package.json +0 -6
  196. package/TransactionEnvelopeEip4844/package.json +0 -6
  197. package/TransactionEnvelopeEip7702/package.json +0 -6
  198. package/TransactionEnvelopeLegacy/package.json +0 -6
  199. package/_cjs/core/TransactionEnvelope.js.map +0 -1
  200. package/_cjs/core/TransactionEnvelopeEip1559.js.map +0 -1
  201. package/_cjs/core/TransactionEnvelopeEip2930.js.map +0 -1
  202. package/_cjs/core/TransactionEnvelopeEip4844.js.map +0 -1
  203. package/_cjs/core/TransactionEnvelopeEip7702.js.map +0 -1
  204. package/_cjs/core/TransactionEnvelopeLegacy.js.map +0 -1
  205. package/_esm/core/TransactionEnvelope.js.map +0 -1
  206. package/_esm/core/TransactionEnvelopeEip1559.js.map +0 -1
  207. package/_esm/core/TransactionEnvelopeEip2930.js.map +0 -1
  208. package/_esm/core/TransactionEnvelopeEip4844.js.map +0 -1
  209. package/_esm/core/TransactionEnvelopeEip7702.js.map +0 -1
  210. package/_esm/core/TransactionEnvelopeLegacy.js.map +0 -1
  211. package/_types/core/TransactionEnvelope.d.ts.map +0 -1
  212. package/_types/core/TransactionEnvelopeEip1559.d.ts.map +0 -1
  213. package/_types/core/TransactionEnvelopeEip2930.d.ts.map +0 -1
  214. package/_types/core/TransactionEnvelopeEip4844.d.ts.map +0 -1
  215. package/_types/core/TransactionEnvelopeEip7702.d.ts.map +0 -1
  216. package/_types/core/TransactionEnvelopeLegacy.d.ts.map +0 -1
@@ -0,0 +1,973 @@
1
+ import type * as Address from '../core/Address.js'
2
+ import * as Errors from '../core/Errors.js'
3
+ import * as Hex from '../core/Hex.js'
4
+ import type {
5
+ Assign,
6
+ Compute,
7
+ IsNarrowable,
8
+ OneOf,
9
+ PartialBy,
10
+ UnionPartialBy,
11
+ } from '../core/internal/types.js'
12
+ import * as Json from '../core/Json.js'
13
+ import type * as PublicKey from '../core/PublicKey.js'
14
+ import * as Signature from '../core/Signature.js'
15
+ import type * as WebAuthnP256 from '../core/WebAuthnP256.js'
16
+
17
+ /** Signature type identifiers for encoding/decoding */
18
+ const serializedP256Type = '0x01'
19
+ const serializedWebAuthnType = '0x02'
20
+ const serializedKeychainType = '0x03'
21
+
22
+ /**
23
+ * Statically determines the signature type of an envelope at compile time.
24
+ *
25
+ * @example
26
+ * ```ts twoslash
27
+ * import type { SignatureEnvelope } from 'ox/tempo'
28
+ *
29
+ * type Type = SignatureEnvelope.GetType<{ r: bigint; s: bigint; yParity: number }>
30
+ * // @log: 'secp256k1'
31
+ * ```
32
+ */
33
+ export type GetType<
34
+ envelope extends PartialBy<SignatureEnvelope, 'type'> | unknown,
35
+ > = unknown extends envelope
36
+ ? envelope extends unknown
37
+ ? Type
38
+ : never
39
+ : envelope extends { type: infer T extends Type }
40
+ ? T
41
+ : envelope extends {
42
+ signature: { r: bigint; s: bigint }
43
+ prehash: boolean
44
+ publicKey: PublicKey.PublicKey
45
+ }
46
+ ? 'p256'
47
+ : envelope extends {
48
+ signature: { r: bigint; s: bigint }
49
+ metadata: any
50
+ publicKey: PublicKey.PublicKey
51
+ }
52
+ ? 'webAuthn'
53
+ : envelope extends { r: bigint; s: bigint; yParity: number }
54
+ ? 'secp256k1'
55
+ : envelope extends {
56
+ signature: { r: bigint; s: bigint; yParity: number }
57
+ }
58
+ ? 'secp256k1'
59
+ : envelope extends {
60
+ userAddress: Address.Address
61
+ }
62
+ ? 'keychain'
63
+ : never
64
+
65
+ /**
66
+ * Represents a signature envelope that can contain different signature types.
67
+ *
68
+ * Tempo transactions support multiple signature types, each with different wire formats:
69
+ *
70
+ * - **secp256k1** (no type prefix, 65 bytes): Standard Ethereum ECDSA signature. The sender
71
+ * address is recovered via `ecrecover`. Base transaction cost: 21,000 gas.
72
+ *
73
+ * - **p256** (type `0x01`, 130 bytes): P256/secp256r1 curve signature for passkey accounts.
74
+ * Includes embedded public key (64 bytes) and prehash flag. Enables native WebCrypto
75
+ * key support. Additional gas cost: +5,000 gas over secp256k1.
76
+ *
77
+ * - **webAuthn** (type `0x02`, 129-2049 bytes): WebAuthn signature with authenticator data
78
+ * and clientDataJSON. Enables browser passkey authentication. The signature is also
79
+ * charged as calldata (16 gas/non-zero byte, 4 gas/zero byte).
80
+ *
81
+ * - **keychain** (type `0x03`): Access key signature that wraps an inner signature (secp256k1,
82
+ * p256, or webAuthn). Format: `0x03` + user_address (20 bytes) + inner signature. The
83
+ * protocol validates the access key authorization via the AccountKeychain precompile.
84
+ *
85
+ * [Signature Types Specification](https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction#signature-types)
86
+ */
87
+ export type SignatureEnvelope<bigintType = bigint, numberType = number> = OneOf<
88
+ | Secp256k1<bigintType, numberType>
89
+ | P256<bigintType, numberType>
90
+ | WebAuthn<bigintType, numberType>
91
+ | Keychain<bigintType, numberType>
92
+ >
93
+
94
+ /**
95
+ * RPC-formatted signature envelope.
96
+ */
97
+ export type SignatureEnvelopeRpc = OneOf<
98
+ Secp256k1Rpc | P256Rpc | WebAuthnRpc | KeychainRpc
99
+ >
100
+
101
+ export type Keychain<bigintType = bigint, numberType = number> = {
102
+ /** Root account address that this transaction is being executed for */
103
+ userAddress: Address.Address
104
+ /** The actual signature from the access key (can be Secp256k1, P256, or WebAuthn) */
105
+ inner: SignatureEnvelope<bigintType, numberType>
106
+ type: 'keychain'
107
+ }
108
+
109
+ export type KeychainRpc = {
110
+ type: 'keychain'
111
+ userAddress: Address.Address
112
+ signature: SignatureEnvelopeRpc
113
+ }
114
+
115
+ export type P256<bigintType = bigint, numberType = number> = {
116
+ prehash: boolean
117
+ publicKey: PublicKey.PublicKey
118
+ signature: Signature.Signature<false, bigintType, numberType>
119
+ type: 'p256'
120
+ }
121
+
122
+ export type P256Rpc = {
123
+ prehash: boolean
124
+ pubKeyX: Hex.Hex
125
+ pubKeyY: Hex.Hex
126
+ r: Hex.Hex
127
+ s: Hex.Hex
128
+ type: 'p256'
129
+ }
130
+
131
+ export type Secp256k1<bigintType = bigint, numberType = number> = {
132
+ signature: Signature.Signature<true, bigintType, numberType>
133
+ type: 'secp256k1'
134
+ }
135
+
136
+ export type Secp256k1Rpc = Compute<
137
+ Signature.Rpc<true> & {
138
+ v?: Hex.Hex | undefined
139
+ type: 'secp256k1'
140
+ }
141
+ >
142
+
143
+ export type Secp256k1Flat<
144
+ bigintType = bigint,
145
+ numberType = number,
146
+ > = Signature.Signature<true, bigintType, numberType> & {
147
+ type?: 'secp256k1' | undefined
148
+ }
149
+
150
+ export type WebAuthn<bigintType = bigint, numberType = number> = {
151
+ metadata: Pick<
152
+ WebAuthnP256.SignMetadata,
153
+ 'authenticatorData' | 'clientDataJSON'
154
+ >
155
+ signature: Signature.Signature<false, bigintType, numberType>
156
+ publicKey: PublicKey.PublicKey
157
+ type: 'webAuthn'
158
+ }
159
+
160
+ export type WebAuthnRpc = {
161
+ pubKeyX: Hex.Hex
162
+ pubKeyY: Hex.Hex
163
+ r: Hex.Hex
164
+ s: Hex.Hex
165
+ type: 'webAuthn'
166
+ webauthnData: Hex.Hex
167
+ }
168
+
169
+ /** Hex-encoded serialized signature envelope. */
170
+ export type Serialized = Hex.Hex
171
+
172
+ /** List of supported signature types. */
173
+ export const types = ['secp256k1', 'p256', 'webAuthn'] as const
174
+
175
+ /** Union type of supported signature types. */
176
+ export type Type = (typeof types)[number]
177
+
178
+ /**
179
+ * Asserts that a {@link ox#SignatureEnvelope.SignatureEnvelope} is valid.
180
+ *
181
+ * @example
182
+ * ```ts twoslash
183
+ * import { SignatureEnvelope } from 'ox/tempo'
184
+ *
185
+ * SignatureEnvelope.assert({
186
+ * type: 'secp256k1',
187
+ * signature: {
188
+ * r: 0n,
189
+ * s: 0n,
190
+ * yParity: 0,
191
+ * },
192
+ * })
193
+ * ```
194
+ *
195
+ * @param envelope - The signature envelope to assert.
196
+ * @throws `CoercionError` if the envelope type cannot be determined.
197
+ */
198
+ export function assert(envelope: PartialBy<SignatureEnvelope, 'type'>): void {
199
+ const type = getType(envelope)
200
+
201
+ if (type === 'secp256k1') {
202
+ const secp256k1 = envelope as Secp256k1
203
+ Signature.assert(secp256k1.signature)
204
+ return
205
+ }
206
+
207
+ if (type === 'p256') {
208
+ const p256 = envelope as P256
209
+ const missing: string[] = []
210
+
211
+ if (typeof p256.signature?.r !== 'bigint') missing.push('signature.r')
212
+ if (typeof p256.signature?.s !== 'bigint') missing.push('signature.s')
213
+ if (typeof p256.prehash !== 'boolean') missing.push('prehash')
214
+ if (!p256.publicKey) missing.push('publicKey')
215
+ else {
216
+ if (typeof p256.publicKey.x !== 'bigint') missing.push('publicKey.x')
217
+ if (typeof p256.publicKey.y !== 'bigint') missing.push('publicKey.y')
218
+ }
219
+
220
+ if (missing.length > 0)
221
+ throw new MissingPropertiesError({ envelope, missing, type: 'p256' })
222
+ return
223
+ }
224
+
225
+ if (type === 'webAuthn') {
226
+ const webauthn = envelope as WebAuthn
227
+ const missing: string[] = []
228
+
229
+ if (typeof webauthn.signature?.r !== 'bigint') missing.push('signature.r')
230
+ if (typeof webauthn.signature?.s !== 'bigint') missing.push('signature.s')
231
+ if (!webauthn.metadata) missing.push('metadata')
232
+ else {
233
+ if (!webauthn.metadata.authenticatorData)
234
+ missing.push('metadata.authenticatorData')
235
+ if (!webauthn.metadata.clientDataJSON)
236
+ missing.push('metadata.clientDataJSON')
237
+ }
238
+ if (!webauthn.publicKey) missing.push('publicKey')
239
+ else {
240
+ if (typeof webauthn.publicKey.x !== 'bigint') missing.push('publicKey.x')
241
+ if (typeof webauthn.publicKey.y !== 'bigint') missing.push('publicKey.y')
242
+ }
243
+
244
+ if (missing.length > 0)
245
+ throw new MissingPropertiesError({ envelope, missing, type: 'webAuthn' })
246
+ return
247
+ }
248
+
249
+ if (type === 'keychain') {
250
+ const keychain = envelope as Keychain
251
+ assert(keychain.inner)
252
+ return
253
+ }
254
+ }
255
+
256
+ export declare namespace assert {
257
+ type ErrorType =
258
+ | CoercionError
259
+ | MissingPropertiesError
260
+ | Signature.assert.ErrorType
261
+ | Errors.GlobalErrorType
262
+ }
263
+
264
+ /**
265
+ * Deserializes a hex-encoded signature envelope into a typed signature object.
266
+ *
267
+ * Wire format detection:
268
+ * - 65 bytes (no prefix): secp256k1 signature
269
+ * - Type `0x01` + 129 bytes: P256 signature (r, s, pubKeyX, pubKeyY, prehash)
270
+ * - Type `0x02` + variable: WebAuthn signature (webauthnData, r, s, pubKeyX, pubKeyY)
271
+ * - Type `0x03` + 20 bytes + inner: Keychain signature (userAddress + inner signature)
272
+ *
273
+ * [Signature Types](https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction#signature-types)
274
+ *
275
+ * @example
276
+ * ```ts twoslash
277
+ * import { SignatureEnvelope } from 'ox/tempo'
278
+ *
279
+ * const envelope = SignatureEnvelope.deserialize('0x...')
280
+ * ```
281
+ *
282
+ * @param serialized - The hex-encoded signature envelope to deserialize.
283
+ * @returns The deserialized signature envelope.
284
+ * @throws `CoercionError` if the serialized value cannot be coerced to a valid signature envelope.
285
+ */
286
+ export function deserialize(serialized: Serialized): SignatureEnvelope {
287
+ const size = Hex.size(serialized)
288
+
289
+ // Backward compatibility: 65 bytes means secp256k1 without type identifier
290
+ if (size === 65) {
291
+ const signature = Signature.fromHex(serialized)
292
+ Signature.assert(signature)
293
+ return { signature, type: 'secp256k1' } satisfies Secp256k1
294
+ }
295
+
296
+ // For all other lengths, first byte is the type identifier
297
+ const typeId = Hex.slice(serialized, 0, 1)
298
+ const data = Hex.slice(serialized, 1)
299
+ const dataSize = Hex.size(data)
300
+
301
+ if (typeId === serializedP256Type) {
302
+ // P256: 32 (r) + 32 (s) + 32 (pubKeyX) + 32 (pubKeyY) + 1 (prehash) = 129 bytes
303
+ if (dataSize !== 129)
304
+ throw new InvalidSerializedError({
305
+ reason: `Invalid P256 signature envelope size: expected 129 bytes, got ${dataSize} bytes`,
306
+ serialized,
307
+ })
308
+
309
+ return {
310
+ publicKey: {
311
+ prefix: 4,
312
+ x: Hex.toBigInt(Hex.slice(data, 64, 96)),
313
+ y: Hex.toBigInt(Hex.slice(data, 96, 128)),
314
+ },
315
+ prehash: Hex.toNumber(Hex.slice(data, 128, 129)) !== 0,
316
+ signature: {
317
+ r: Hex.toBigInt(Hex.slice(data, 0, 32)),
318
+ s: Hex.toBigInt(Hex.slice(data, 32, 64)),
319
+ },
320
+ type: 'p256',
321
+ } satisfies P256
322
+ }
323
+
324
+ if (typeId === serializedWebAuthnType) {
325
+ // WebAuthn: variable (webauthnData) + 32 (r) + 32 (s) + 32 (pubKeyX) + 32 (pubKeyY)
326
+ // Minimum: 128 bytes (at least some authenticator data + signature components)
327
+ if (dataSize < 128)
328
+ throw new InvalidSerializedError({
329
+ reason: `Invalid WebAuthn signature envelope size: expected at least 128 bytes, got ${dataSize} bytes`,
330
+ serialized,
331
+ })
332
+
333
+ const webauthnDataSize = dataSize - 128
334
+ const webauthnData = Hex.slice(data, 0, webauthnDataSize)
335
+
336
+ // Parse webauthnData into authenticatorData and clientDataJSON
337
+ // According to the Rust code, it's authenticatorData || clientDataJSON
338
+ // We need to find the split point (minimum authenticatorData is 37 bytes)
339
+ let authenticatorData: Hex.Hex | undefined
340
+ let clientDataJSON: string | undefined
341
+
342
+ // Try to find the JSON start (clientDataJSON should start with '{')
343
+ for (let split = 37; split < webauthnDataSize; split++) {
344
+ const potentialJson = Hex.toString(Hex.slice(webauthnData, split))
345
+ if (potentialJson.startsWith('{') && potentialJson.endsWith('}')) {
346
+ try {
347
+ JSON.parse(potentialJson)
348
+ authenticatorData = Hex.slice(webauthnData, 0, split)
349
+ clientDataJSON = potentialJson
350
+ break
351
+ } catch {}
352
+ }
353
+ }
354
+
355
+ if (!authenticatorData || !clientDataJSON)
356
+ throw new InvalidSerializedError({
357
+ reason:
358
+ 'Unable to parse WebAuthn metadata: could not extract valid authenticatorData and clientDataJSON',
359
+ serialized,
360
+ })
361
+
362
+ return {
363
+ publicKey: {
364
+ prefix: 4,
365
+ x: Hex.toBigInt(
366
+ Hex.slice(data, webauthnDataSize + 64, webauthnDataSize + 96),
367
+ ),
368
+ y: Hex.toBigInt(
369
+ Hex.slice(data, webauthnDataSize + 96, webauthnDataSize + 128),
370
+ ),
371
+ },
372
+ metadata: {
373
+ authenticatorData,
374
+ clientDataJSON,
375
+ },
376
+ signature: {
377
+ r: Hex.toBigInt(
378
+ Hex.slice(data, webauthnDataSize, webauthnDataSize + 32),
379
+ ),
380
+ s: Hex.toBigInt(
381
+ Hex.slice(data, webauthnDataSize + 32, webauthnDataSize + 64),
382
+ ),
383
+ },
384
+ type: 'webAuthn',
385
+ } satisfies WebAuthn
386
+ }
387
+
388
+ if (typeId === serializedKeychainType) {
389
+ const userAddress = Hex.slice(data, 0, 20)
390
+ const inner = deserialize(Hex.slice(data, 20))
391
+
392
+ return {
393
+ userAddress,
394
+ inner,
395
+ type: 'keychain',
396
+ } satisfies Keychain
397
+ }
398
+
399
+ throw new InvalidSerializedError({
400
+ reason: `Unknown signature type identifier: ${typeId}. Expected ${serializedP256Type} (P256) or ${serializedWebAuthnType} (WebAuthn)`,
401
+ serialized,
402
+ })
403
+ }
404
+
405
+ /**
406
+ * Coerces a value to a signature envelope.
407
+ *
408
+ * Accepts either a serialized hex string or an existing signature envelope object.
409
+ * Use this to wrap raw signatures from {@link ox#Secp256k1.(sign:function)}, {@link ox#P256.(sign:function)},
410
+ * {@link ox#WebCryptoP256.(sign:function)}, or {@link ox#WebAuthnP256.(sign:function)} into the envelope format
411
+ * required by Tempo transactions.
412
+ *
413
+ * [Signature Types](https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction#signature-types)
414
+ *
415
+ * @example
416
+ * ### Secp256k1
417
+ *
418
+ * Standard Ethereum ECDSA signature using the secp256k1 curve.
419
+ *
420
+ * ```ts twoslash
421
+ * import { Secp256k1 } from 'ox'
422
+ * import { SignatureEnvelope } from 'ox/tempo'
423
+ *
424
+ * const privateKey = Secp256k1.randomPrivateKey()
425
+ * const signature = Secp256k1.sign({ payload: '0xdeadbeef', privateKey })
426
+ *
427
+ * const envelope = SignatureEnvelope.from(signature)
428
+ * ```
429
+ *
430
+ * @example
431
+ * ### P256
432
+ *
433
+ * ECDSA signature using the P-256 (secp256r1) curve. Requires embedding the
434
+ * public key.
435
+ *
436
+ * ```ts twoslash
437
+ * import { P256 } from 'ox'
438
+ * import { SignatureEnvelope } from 'ox/tempo'
439
+ *
440
+ * const { privateKey, publicKey } = P256.createKeyPair()
441
+ * const signature = P256.sign({ payload: '0xdeadbeef', privateKey })
442
+ *
443
+ * const envelope = SignatureEnvelope.from({
444
+ * signature,
445
+ * publicKey,
446
+ * })
447
+ * ```
448
+ *
449
+ * @example
450
+ * ### P256 (WebCrypto)
451
+ *
452
+ * When using WebCrypto keys, `prehash` must be `true` since WebCrypto always
453
+ * SHA256 hashes the digest before signing.
454
+ *
455
+ * ```ts twoslash
456
+ * // @noErrors
457
+ * import { WebCryptoP256 } from 'ox'
458
+ * import { SignatureEnvelope } from 'ox/tempo'
459
+ *
460
+ * const { privateKey, publicKey } = await WebCryptoP256.createKeyPair()
461
+ * const signature = await WebCryptoP256.sign({ payload: '0xdeadbeef', privateKey })
462
+ *
463
+ * const envelope = SignatureEnvelope.from({
464
+ * signature,
465
+ * publicKey,
466
+ * prehash: true,
467
+ * })
468
+ * ```
469
+ *
470
+ * @example
471
+ * ### WebAuthn
472
+ *
473
+ * Passkey-based signature using WebAuthn. Includes authenticator metadata
474
+ * (authenticatorData and clientDataJSON) along with the P-256 signature and
475
+ * public key.
476
+ *
477
+ * ```ts twoslash
478
+ * // @noErrors
479
+ * import { WebAuthnP256 } from 'ox'
480
+ * import { SignatureEnvelope } from 'ox/tempo'
481
+ *
482
+ * const credential = await WebAuthnP256.createCredential({
483
+ * name: 'Example',
484
+ * })
485
+ *
486
+ * const { metadata, signature } = await WebAuthnP256.sign({
487
+ * challenge: '0xdeadbeef',
488
+ * credentialId: credential.id,
489
+ * })
490
+ *
491
+ * const envelope = SignatureEnvelope.from({
492
+ * signature,
493
+ * publicKey: credential.publicKey,
494
+ * metadata,
495
+ * })
496
+ * ```
497
+ *
498
+ * @example
499
+ * ### Keychain
500
+ *
501
+ * Wraps another signature type with a user address, used for delegated signing
502
+ * via access keys on behalf of a root account.
503
+ *
504
+ * ```ts twoslash
505
+ * import { Secp256k1 } from 'ox'
506
+ * import { SignatureEnvelope } from 'ox/tempo'
507
+ *
508
+ * const privateKey = Secp256k1.randomPrivateKey()
509
+ * const signature = Secp256k1.sign({ payload: '0xdeadbeef', privateKey })
510
+ *
511
+ * const envelope = SignatureEnvelope.from({
512
+ * userAddress: '0x1234567890123456789012345678901234567890',
513
+ * inner: SignatureEnvelope.from(signature),
514
+ * })
515
+ * ```
516
+ *
517
+ * @param value - The value to coerce (either a hex string or signature envelope).
518
+ * @returns The signature envelope.
519
+ */
520
+ export function from<const value extends from.Value>(
521
+ value: value | from.Value,
522
+ ): from.ReturnValue<value> {
523
+ if (typeof value === 'string') return deserialize(value) as never
524
+
525
+ if (
526
+ typeof value === 'object' &&
527
+ value !== null &&
528
+ 'r' in value &&
529
+ 's' in value &&
530
+ 'yParity' in value
531
+ )
532
+ return { signature: value, type: 'secp256k1' } as never
533
+
534
+ const type = getType(value)
535
+ return {
536
+ ...value,
537
+ ...(type === 'p256' ? { prehash: value.prehash } : {}),
538
+ type,
539
+ } as never
540
+ }
541
+
542
+ export declare namespace from {
543
+ type Value =
544
+ | UnionPartialBy<SignatureEnvelope, 'prehash' | 'type'>
545
+ | Secp256k1Flat
546
+ | Serialized
547
+
548
+ type ReturnValue<value extends Value> = Compute<
549
+ OneOf<
550
+ value extends Serialized
551
+ ? SignatureEnvelope
552
+ : value extends Secp256k1Flat
553
+ ? Secp256k1
554
+ : IsNarrowable<value, SignatureEnvelope> extends true
555
+ ? SignatureEnvelope
556
+ : Assign<value, { readonly type: GetType<value> }>
557
+ >
558
+ >
559
+ }
560
+
561
+ /**
562
+ * Converts an RPC-formatted signature envelope to a typed signature envelope.
563
+ *
564
+ * @example
565
+ * ```ts twoslash
566
+ * import { SignatureEnvelope } from 'ox/tempo'
567
+ *
568
+ * const envelope = SignatureEnvelope.fromRpc({
569
+ * r: '0x0',
570
+ * s: '0x0',
571
+ * yParity: '0x0',
572
+ * type: 'secp256k1',
573
+ * })
574
+ * ```
575
+ *
576
+ * @param envelope - The RPC signature envelope to convert.
577
+ * @returns The signature envelope with bigint values.
578
+ */
579
+ export function fromRpc(envelope: SignatureEnvelopeRpc): SignatureEnvelope {
580
+ if (envelope.type === 'secp256k1')
581
+ return {
582
+ signature: Signature.fromRpc(envelope),
583
+ type: 'secp256k1',
584
+ }
585
+
586
+ if (envelope.type === 'p256') {
587
+ return {
588
+ prehash: envelope.prehash,
589
+ publicKey: {
590
+ prefix: 4,
591
+ x: Hex.toBigInt(envelope.pubKeyX),
592
+ y: Hex.toBigInt(envelope.pubKeyY),
593
+ },
594
+ signature: {
595
+ r: Hex.toBigInt(envelope.r),
596
+ s: Hex.toBigInt(envelope.s),
597
+ },
598
+ type: 'p256',
599
+ }
600
+ }
601
+
602
+ if (envelope.type === 'webAuthn') {
603
+ const webauthnData = envelope.webauthnData
604
+ const webauthnDataSize = Hex.size(webauthnData)
605
+
606
+ // Parse webauthnData into authenticatorData and clientDataJSON
607
+ let authenticatorData: Hex.Hex | undefined
608
+ let clientDataJSON: string | undefined
609
+
610
+ // Try to find the JSON start (clientDataJSON should start with '{')
611
+ for (let split = 37; split < webauthnDataSize; split++) {
612
+ const potentialJson = Hex.toString(Hex.slice(webauthnData, split))
613
+ if (potentialJson.startsWith('{') && potentialJson.endsWith('}')) {
614
+ try {
615
+ JSON.parse(potentialJson)
616
+ authenticatorData = Hex.slice(webauthnData, 0, split)
617
+ clientDataJSON = potentialJson
618
+ break
619
+ } catch {}
620
+ }
621
+ }
622
+
623
+ if (!authenticatorData || !clientDataJSON)
624
+ throw new InvalidSerializedError({
625
+ reason:
626
+ 'Unable to parse WebAuthn metadata: could not extract valid authenticatorData and clientDataJSON',
627
+ serialized: webauthnData,
628
+ })
629
+
630
+ return {
631
+ metadata: {
632
+ authenticatorData,
633
+ clientDataJSON,
634
+ },
635
+ publicKey: {
636
+ prefix: 4,
637
+ x: Hex.toBigInt(envelope.pubKeyX),
638
+ y: Hex.toBigInt(envelope.pubKeyY),
639
+ },
640
+ signature: {
641
+ r: Hex.toBigInt(envelope.r),
642
+ s: Hex.toBigInt(envelope.s),
643
+ },
644
+ type: 'webAuthn',
645
+ }
646
+ }
647
+
648
+ if (
649
+ envelope.type === 'keychain' ||
650
+ ('userAddress' in envelope && 'signature' in envelope)
651
+ )
652
+ return {
653
+ type: 'keychain',
654
+ userAddress: envelope.userAddress,
655
+ inner: fromRpc(envelope.signature),
656
+ }
657
+
658
+ throw new CoercionError({ envelope })
659
+ }
660
+
661
+ export declare namespace fromRpc {
662
+ type ErrorType =
663
+ | CoercionError
664
+ | InvalidSerializedError
665
+ | Signature.fromRpc.ErrorType
666
+ | Errors.GlobalErrorType
667
+ }
668
+
669
+ /**
670
+ * Determines the signature type of an envelope.
671
+ *
672
+ * @example
673
+ * ```ts twoslash
674
+ * import { SignatureEnvelope } from 'ox/tempo'
675
+ *
676
+ * const type = SignatureEnvelope.getType({
677
+ * signature: { r: 0n, s: 0n, yParity: 0 },
678
+ * })
679
+ * // @log: 'secp256k1'
680
+ * ```
681
+ *
682
+ * @param envelope - The signature envelope to inspect.
683
+ * @returns The signature type ('secp256k1', 'p256', or 'webAuthn').
684
+ * @throws `CoercionError` if the envelope type cannot be determined.
685
+ */
686
+ export function getType<
687
+ envelope extends
688
+ | PartialBy<SignatureEnvelope, 'type'>
689
+ | Secp256k1Flat
690
+ | unknown,
691
+ >(envelope: envelope): GetType<envelope> {
692
+ if (typeof envelope !== 'object' || envelope === null)
693
+ throw new CoercionError({ envelope })
694
+
695
+ if ('type' in envelope && envelope.type) return envelope.type as never
696
+
697
+ // Detect secp256k1 signature (backwards compatibility: also support flat structure)
698
+ if (
699
+ 'signature' in envelope &&
700
+ !('publicKey' in envelope) &&
701
+ typeof envelope.signature === 'object' &&
702
+ envelope.signature !== null &&
703
+ 'r' in envelope.signature &&
704
+ 's' in envelope.signature &&
705
+ 'yParity' in envelope.signature
706
+ )
707
+ return 'secp256k1' as never
708
+
709
+ // Detect secp256k1 signature (flat structure)
710
+ if ('r' in envelope && 's' in envelope && 'yParity' in envelope)
711
+ return 'secp256k1' as never
712
+
713
+ // Detect P256 signature
714
+ if (
715
+ 'signature' in envelope &&
716
+ 'prehash' in envelope &&
717
+ 'publicKey' in envelope &&
718
+ typeof envelope.prehash === 'boolean'
719
+ )
720
+ return 'p256' as never
721
+
722
+ // Detect WebAuthn signature
723
+ if (
724
+ 'signature' in envelope &&
725
+ 'metadata' in envelope &&
726
+ 'publicKey' in envelope
727
+ )
728
+ return 'webAuthn' as never
729
+
730
+ // Detect Keychain signature
731
+ if ('userAddress' in envelope && 'inner' in envelope)
732
+ return 'keychain' as never
733
+
734
+ throw new CoercionError({
735
+ envelope,
736
+ })
737
+ }
738
+
739
+ /**
740
+ * Serializes a signature envelope to a hex-encoded string.
741
+ *
742
+ * Wire format:
743
+ * - secp256k1: 65 bytes (no type prefix, for backward compatibility)
744
+ * - P256: `0x01` + r (32) + s (32) + pubKeyX (32) + pubKeyY (32) + prehash (1) = 130 bytes
745
+ * - WebAuthn: `0x02` + webauthnData (variable) + r (32) + s (32) + pubKeyX (32) + pubKeyY (32)
746
+ * - Keychain: `0x03` + userAddress (20) + inner signature (recursive)
747
+ *
748
+ * [Signature Types](https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction#signature-types)
749
+ *
750
+ * @example
751
+ * ```ts twoslash
752
+ * import { SignatureEnvelope } from 'ox/tempo'
753
+ *
754
+ * const serialized = SignatureEnvelope.serialize({
755
+ * signature: { r: 0n, s: 0n, yParity: 0 },
756
+ * type: 'secp256k1',
757
+ * })
758
+ * ```
759
+ *
760
+ * @param envelope - The signature envelope to serialize.
761
+ * @returns The hex-encoded serialized signature.
762
+ * @throws `CoercionError` if the envelope cannot be serialized.
763
+ */
764
+ export function serialize(
765
+ envelope: UnionPartialBy<SignatureEnvelope, 'prehash'>,
766
+ ): Serialized {
767
+ const type = getType(envelope)
768
+
769
+ // Backward compatibility: no type identifier for secp256k1
770
+ if (type === 'secp256k1') {
771
+ const secp256k1 = envelope as Secp256k1
772
+ return Signature.toHex(secp256k1.signature)
773
+ }
774
+
775
+ if (type === 'p256') {
776
+ const p256 = envelope as P256
777
+ // Format: 1 byte (type) + 32 (r) + 32 (s) + 32 (pubKeyX) + 32 (pubKeyY) + 1 (prehash)
778
+ return Hex.concat(
779
+ serializedP256Type,
780
+ Hex.fromNumber(p256.signature.r, { size: 32 }),
781
+ Hex.fromNumber(p256.signature.s, { size: 32 }),
782
+ Hex.fromNumber(p256.publicKey.x, { size: 32 }),
783
+ Hex.fromNumber(p256.publicKey.y, { size: 32 }),
784
+ Hex.fromNumber(p256.prehash ? 1 : 0, { size: 1 }),
785
+ )
786
+ }
787
+
788
+ if (type === 'webAuthn') {
789
+ const webauthn = envelope as WebAuthn
790
+ // Format: 1 byte (type) + variable (authenticatorData || clientDataJSON) + 32 (r) + 32 (s) + 32 (pubKeyX) + 32 (pubKeyY)
791
+ const webauthnData = Hex.concat(
792
+ webauthn.metadata.authenticatorData,
793
+ Hex.fromString(webauthn.metadata.clientDataJSON),
794
+ )
795
+
796
+ return Hex.concat(
797
+ serializedWebAuthnType,
798
+ webauthnData,
799
+ Hex.fromNumber(webauthn.signature.r, { size: 32 }),
800
+ Hex.fromNumber(webauthn.signature.s, { size: 32 }),
801
+ Hex.fromNumber(webauthn.publicKey.x, { size: 32 }),
802
+ Hex.fromNumber(webauthn.publicKey.y, { size: 32 }),
803
+ )
804
+ }
805
+
806
+ if (type === 'keychain') {
807
+ const keychain = envelope as Keychain
808
+ return Hex.concat(
809
+ serializedKeychainType,
810
+ keychain.userAddress,
811
+ serialize(keychain.inner),
812
+ )
813
+ }
814
+
815
+ throw new CoercionError({ envelope })
816
+ }
817
+
818
+ /**
819
+ * Converts a signature envelope to RPC format.
820
+ *
821
+ * @example
822
+ * ```ts twoslash
823
+ * import { SignatureEnvelope } from 'ox/tempo'
824
+ *
825
+ * const rpc = SignatureEnvelope.toRpc({
826
+ * signature: { r: 0n, s: 0n, yParity: 0 },
827
+ * type: 'secp256k1',
828
+ * })
829
+ * ```
830
+ *
831
+ * @param envelope - The signature envelope to convert.
832
+ * @returns The RPC signature envelope with hex values.
833
+ */
834
+ export function toRpc(envelope: SignatureEnvelope): SignatureEnvelopeRpc {
835
+ const type = getType(envelope)
836
+
837
+ if (type === 'secp256k1') {
838
+ const secp256k1 = envelope as Secp256k1
839
+ return {
840
+ ...Signature.toRpc(secp256k1.signature),
841
+ type: 'secp256k1',
842
+ }
843
+ }
844
+
845
+ if (type === 'p256') {
846
+ const p256 = envelope as P256
847
+ return {
848
+ prehash: p256.prehash,
849
+ pubKeyX: Hex.fromNumber(p256.publicKey.x, { size: 32 }),
850
+ pubKeyY: Hex.fromNumber(p256.publicKey.y, { size: 32 }),
851
+ r: Hex.fromNumber(p256.signature.r, { size: 32 }),
852
+ s: Hex.fromNumber(p256.signature.s, { size: 32 }),
853
+ type: 'p256',
854
+ }
855
+ }
856
+
857
+ if (type === 'webAuthn') {
858
+ const webauthn = envelope as WebAuthn
859
+ const webauthnData = Hex.concat(
860
+ webauthn.metadata.authenticatorData,
861
+ Hex.fromString(webauthn.metadata.clientDataJSON),
862
+ )
863
+
864
+ return {
865
+ pubKeyX: Hex.fromNumber(webauthn.publicKey.x, { size: 32 }),
866
+ pubKeyY: Hex.fromNumber(webauthn.publicKey.y, { size: 32 }),
867
+ r: Hex.fromNumber(webauthn.signature.r, { size: 32 }),
868
+ s: Hex.fromNumber(webauthn.signature.s, { size: 32 }),
869
+ type: 'webAuthn',
870
+ webauthnData,
871
+ }
872
+ }
873
+
874
+ if (type === 'keychain') {
875
+ const keychain = envelope as Keychain
876
+ return {
877
+ type: 'keychain',
878
+ userAddress: keychain.userAddress,
879
+ signature: toRpc(keychain.inner),
880
+ }
881
+ }
882
+
883
+ throw new CoercionError({ envelope })
884
+ }
885
+
886
+ export declare namespace toRpc {
887
+ type ErrorType =
888
+ | CoercionError
889
+ | Signature.toRpc.ErrorType
890
+ | Errors.GlobalErrorType
891
+ }
892
+
893
+ /**
894
+ * Validates a signature envelope. Returns `true` if the envelope is valid, `false` otherwise.
895
+ *
896
+ * @example
897
+ * ```ts twoslash
898
+ * import { SignatureEnvelope } from 'ox/tempo'
899
+ *
900
+ * const valid = SignatureEnvelope.validate({
901
+ * signature: { r: 0n, s: 0n, yParity: 0 },
902
+ * type: 'secp256k1',
903
+ * })
904
+ * // @log: true
905
+ * ```
906
+ *
907
+ * @param envelope - The signature envelope to validate.
908
+ * @returns `true` if valid, `false` otherwise.
909
+ */
910
+ export function validate(
911
+ envelope: PartialBy<SignatureEnvelope, 'type'>,
912
+ ): boolean {
913
+ try {
914
+ assert(envelope)
915
+ return true
916
+ } catch {
917
+ return false
918
+ }
919
+ }
920
+
921
+ export declare namespace validate {
922
+ type ErrorType = Errors.GlobalErrorType
923
+ }
924
+
925
+ /**
926
+ * Error thrown when a signature envelope cannot be coerced to a valid type.
927
+ */
928
+ export class CoercionError extends Errors.BaseError {
929
+ override readonly name = 'SignatureEnvelope.CoercionError'
930
+ constructor({ envelope }: { envelope: unknown }) {
931
+ super(
932
+ `Unable to coerce value (\`${Json.stringify(envelope)}\`) to a valid signature envelope.`,
933
+ )
934
+ }
935
+ }
936
+
937
+ /**
938
+ * Error thrown when a signature envelope is missing required properties.
939
+ */
940
+ export class MissingPropertiesError extends Errors.BaseError {
941
+ override readonly name = 'SignatureEnvelope.MissingPropertiesError'
942
+ constructor({
943
+ envelope,
944
+ missing,
945
+ type,
946
+ }: {
947
+ envelope: unknown
948
+ missing: string[]
949
+ type: Type
950
+ }) {
951
+ super(
952
+ `Signature envelope of type "${type}" is missing required properties: ${missing.map((m) => `\`${m}\``).join(', ')}.\n\nProvided: ${Json.stringify(envelope)}`,
953
+ )
954
+ }
955
+ }
956
+
957
+ /**
958
+ * Error thrown when a serialized signature envelope cannot be deserialized.
959
+ */
960
+ export class InvalidSerializedError extends Errors.BaseError {
961
+ override readonly name = 'SignatureEnvelope.InvalidSerializedError'
962
+ constructor({
963
+ reason,
964
+ serialized,
965
+ }: {
966
+ reason: string
967
+ serialized: Hex.Hex
968
+ }) {
969
+ super(`Unable to deserialize signature envelope: ${reason}`, {
970
+ metaMessages: [`Serialized: ${serialized}`],
971
+ })
972
+ }
973
+ }