tempo.ts 0.10.5 → 0.11.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 (224) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +0 -41
  3. package/dist/viem/Account.d.ts +8 -3
  4. package/dist/viem/Account.d.ts.map +1 -1
  5. package/dist/viem/Account.js +9 -2
  6. package/dist/viem/Account.js.map +1 -1
  7. package/dist/viem/Actions/amm.d.ts +1 -1
  8. package/dist/viem/Actions/amm.d.ts.map +1 -1
  9. package/dist/viem/Actions/amm.js +1 -2
  10. package/dist/viem/Actions/amm.js.map +1 -1
  11. package/dist/viem/Actions/dex.d.ts +0 -35
  12. package/dist/viem/Actions/dex.d.ts.map +1 -1
  13. package/dist/viem/Actions/dex.js +0 -43
  14. package/dist/viem/Actions/dex.js.map +1 -1
  15. package/dist/viem/Actions/fee.d.ts +1 -1
  16. package/dist/viem/Actions/fee.d.ts.map +1 -1
  17. package/dist/viem/Actions/fee.js +1 -1
  18. package/dist/viem/Actions/fee.js.map +1 -1
  19. package/dist/viem/Actions/nonce.d.ts.map +1 -1
  20. package/dist/viem/Actions/nonce.js +6 -4
  21. package/dist/viem/Actions/nonce.js.map +1 -1
  22. package/dist/viem/Actions/token.d.ts +1 -2
  23. package/dist/viem/Actions/token.d.ts.map +1 -1
  24. package/dist/viem/Actions/token.js +1 -2
  25. package/dist/viem/Actions/token.js.map +1 -1
  26. package/dist/viem/Chain.d.ts +34 -34
  27. package/dist/viem/Chain.d.ts.map +1 -1
  28. package/dist/viem/Chain.js +2 -0
  29. package/dist/viem/Chain.js.map +1 -1
  30. package/dist/viem/Formatters.d.ts.map +1 -1
  31. package/dist/viem/Formatters.js +5 -3
  32. package/dist/viem/Formatters.js.map +1 -1
  33. package/dist/viem/Transaction.d.ts +8 -5
  34. package/dist/viem/Transaction.d.ts.map +1 -1
  35. package/dist/viem/Transaction.js +13 -3
  36. package/dist/viem/Transaction.js.map +1 -1
  37. package/dist/viem/internal/types.d.ts +1 -1
  38. package/dist/viem/internal/types.d.ts.map +1 -1
  39. package/dist/wagmi/Actions/dex.d.ts +1 -45
  40. package/dist/wagmi/Actions/dex.d.ts.map +1 -1
  41. package/dist/wagmi/Actions/dex.js +0 -55
  42. package/dist/wagmi/Actions/dex.js.map +1 -1
  43. package/dist/wagmi/Connector.d.ts +1 -1
  44. package/dist/wagmi/Connector.d.ts.map +1 -1
  45. package/dist/wagmi/Connector.js +1 -2
  46. package/dist/wagmi/Connector.js.map +1 -1
  47. package/dist/wagmi/Hooks/dex.d.ts +3 -46
  48. package/dist/wagmi/Hooks/dex.d.ts.map +1 -1
  49. package/dist/wagmi/Hooks/dex.js +2 -54
  50. package/dist/wagmi/Hooks/dex.js.map +1 -1
  51. package/dist/wagmi/index.d.ts +1 -1
  52. package/dist/wagmi/index.d.ts.map +1 -1
  53. package/dist/wagmi/index.js +1 -1
  54. package/dist/wagmi/index.js.map +1 -1
  55. package/package.json +5 -23
  56. package/src/viem/Account.test.ts +1 -1
  57. package/src/viem/Account.ts +11 -2
  58. package/src/viem/Actions/account.ts +1 -1
  59. package/src/viem/Actions/amm.ts +1 -2
  60. package/src/viem/Actions/dex.test.ts +1 -195
  61. package/src/viem/Actions/dex.ts +0 -53
  62. package/src/viem/Actions/fee.test.ts +2 -2
  63. package/src/viem/Actions/fee.ts +1 -1
  64. package/src/viem/Actions/nonce.test.ts +27 -14
  65. package/src/viem/Actions/nonce.ts +6 -4
  66. package/src/viem/Actions/token.test.ts +18 -52
  67. package/src/viem/Actions/token.ts +1 -2
  68. package/src/viem/Chain.ts +3 -1
  69. package/src/viem/Decorator.ts +0 -30
  70. package/src/viem/Formatters.ts +9 -3
  71. package/src/viem/Transaction.ts +23 -7
  72. package/src/viem/e2e.test.ts +19 -0
  73. package/src/viem/index.ts +12 -1
  74. package/src/viem/internal/types.ts +1 -1
  75. package/src/wagmi/Actions/dex.test.ts +0 -26
  76. package/src/wagmi/Actions/dex.ts +1 -111
  77. package/src/wagmi/Actions/nonce.test.ts +8 -4
  78. package/src/wagmi/Actions/token.test.ts +2 -2
  79. package/src/wagmi/Connector.ts +1 -2
  80. package/src/wagmi/Hooks/dex.test.ts +0 -26
  81. package/src/wagmi/Hooks/dex.ts +1 -88
  82. package/src/wagmi/Hooks/nonce.test.ts +3 -3
  83. package/src/wagmi/index.ts +4 -1
  84. package/dist/chains.d.ts +0 -73
  85. package/dist/chains.d.ts.map +0 -1
  86. package/dist/chains.js +0 -51
  87. package/dist/chains.js.map +0 -1
  88. package/dist/ox/AuthorizationTempo.d.ts +0 -450
  89. package/dist/ox/AuthorizationTempo.d.ts.map +0 -1
  90. package/dist/ox/AuthorizationTempo.js +0 -433
  91. package/dist/ox/AuthorizationTempo.js.map +0 -1
  92. package/dist/ox/KeyAuthorization.d.ts +0 -356
  93. package/dist/ox/KeyAuthorization.d.ts.map +0 -1
  94. package/dist/ox/KeyAuthorization.js +0 -359
  95. package/dist/ox/KeyAuthorization.js.map +0 -1
  96. package/dist/ox/Order.d.ts +0 -92
  97. package/dist/ox/Order.d.ts.map +0 -1
  98. package/dist/ox/Order.js +0 -88
  99. package/dist/ox/Order.js.map +0 -1
  100. package/dist/ox/OrdersFilters.d.ts +0 -72
  101. package/dist/ox/OrdersFilters.d.ts.map +0 -1
  102. package/dist/ox/OrdersFilters.js +0 -100
  103. package/dist/ox/OrdersFilters.js.map +0 -1
  104. package/dist/ox/Pagination.d.ts +0 -128
  105. package/dist/ox/Pagination.d.ts.map +0 -1
  106. package/dist/ox/Pagination.js +0 -78
  107. package/dist/ox/Pagination.js.map +0 -1
  108. package/dist/ox/PoolId.d.ts +0 -18
  109. package/dist/ox/PoolId.d.ts.map +0 -1
  110. package/dist/ox/PoolId.js +0 -13
  111. package/dist/ox/PoolId.js.map +0 -1
  112. package/dist/ox/RpcSchema.d.ts +0 -32
  113. package/dist/ox/RpcSchema.d.ts.map +0 -1
  114. package/dist/ox/RpcSchema.js +0 -2
  115. package/dist/ox/RpcSchema.js.map +0 -1
  116. package/dist/ox/SignatureEnvelope.d.ts +0 -260
  117. package/dist/ox/SignatureEnvelope.d.ts.map +0 -1
  118. package/dist/ox/SignatureEnvelope.js +0 -477
  119. package/dist/ox/SignatureEnvelope.js.map +0 -1
  120. package/dist/ox/Tick.d.ts +0 -115
  121. package/dist/ox/Tick.d.ts.map +0 -1
  122. package/dist/ox/Tick.js +0 -127
  123. package/dist/ox/Tick.js.map +0 -1
  124. package/dist/ox/TokenId.d.ts +0 -25
  125. package/dist/ox/TokenId.d.ts.map +0 -1
  126. package/dist/ox/TokenId.js +0 -41
  127. package/dist/ox/TokenId.js.map +0 -1
  128. package/dist/ox/TokenRole.d.ts +0 -11
  129. package/dist/ox/TokenRole.d.ts.map +0 -1
  130. package/dist/ox/TokenRole.js +0 -22
  131. package/dist/ox/TokenRole.js.map +0 -1
  132. package/dist/ox/Transaction.d.ts +0 -201
  133. package/dist/ox/Transaction.d.ts.map +0 -1
  134. package/dist/ox/Transaction.js +0 -167
  135. package/dist/ox/Transaction.js.map +0 -1
  136. package/dist/ox/TransactionEnvelopeTempo.d.ts +0 -470
  137. package/dist/ox/TransactionEnvelopeTempo.d.ts.map +0 -1
  138. package/dist/ox/TransactionEnvelopeTempo.js +0 -547
  139. package/dist/ox/TransactionEnvelopeTempo.js.map +0 -1
  140. package/dist/ox/TransactionReceipt.d.ts +0 -155
  141. package/dist/ox/TransactionReceipt.d.ts.map +0 -1
  142. package/dist/ox/TransactionReceipt.js +0 -136
  143. package/dist/ox/TransactionReceipt.js.map +0 -1
  144. package/dist/ox/TransactionRequest.d.ts +0 -76
  145. package/dist/ox/TransactionRequest.d.ts.map +0 -1
  146. package/dist/ox/TransactionRequest.js +0 -93
  147. package/dist/ox/TransactionRequest.js.map +0 -1
  148. package/dist/ox/index.d.ts +0 -14
  149. package/dist/ox/index.d.ts.map +0 -1
  150. package/dist/ox/index.js +0 -14
  151. package/dist/ox/index.js.map +0 -1
  152. package/dist/prool/Instance.d.ts +0 -101
  153. package/dist/prool/Instance.d.ts.map +0 -1
  154. package/dist/prool/Instance.js +0 -136
  155. package/dist/prool/Instance.js.map +0 -1
  156. package/dist/prool/chain.json +0 -238
  157. package/dist/prool/index.d.ts +0 -2
  158. package/dist/prool/index.d.ts.map +0 -1
  159. package/dist/prool/index.js +0 -2
  160. package/dist/prool/index.js.map +0 -1
  161. package/dist/viem/Actions/account.d.ts +0 -40
  162. package/dist/viem/Actions/account.d.ts.map +0 -1
  163. package/dist/viem/Actions/account.js +0 -86
  164. package/dist/viem/Actions/account.js.map +0 -1
  165. package/dist/viem/Actions/index.d.ts +0 -10
  166. package/dist/viem/Actions/index.d.ts.map +0 -1
  167. package/dist/viem/Actions/index.js +0 -10
  168. package/dist/viem/Actions/index.js.map +0 -1
  169. package/dist/viem/Decorator.d.ts +0 -2810
  170. package/dist/viem/Decorator.d.ts.map +0 -1
  171. package/dist/viem/Decorator.js +0 -138
  172. package/dist/viem/Decorator.js.map +0 -1
  173. package/dist/viem/P256.d.ts +0 -2
  174. package/dist/viem/P256.d.ts.map +0 -1
  175. package/dist/viem/P256.js +0 -2
  176. package/dist/viem/P256.js.map +0 -1
  177. package/dist/viem/Secp256k1.d.ts +0 -2
  178. package/dist/viem/Secp256k1.d.ts.map +0 -1
  179. package/dist/viem/Secp256k1.js +0 -2
  180. package/dist/viem/Secp256k1.js.map +0 -1
  181. package/dist/viem/TokenIds.d.ts +0 -2
  182. package/dist/viem/TokenIds.d.ts.map +0 -1
  183. package/dist/viem/TokenIds.js +0 -2
  184. package/dist/viem/TokenIds.js.map +0 -1
  185. package/dist/viem/index.d.ts +0 -17
  186. package/dist/viem/index.d.ts.map +0 -1
  187. package/dist/viem/index.js +0 -17
  188. package/dist/viem/index.js.map +0 -1
  189. package/src/ox/AuthorizationTempo.test.ts +0 -1256
  190. package/src/ox/AuthorizationTempo.ts +0 -648
  191. package/src/ox/KeyAuthorization.test.ts +0 -1332
  192. package/src/ox/KeyAuthorization.ts +0 -540
  193. package/src/ox/Order.test.ts +0 -78
  194. package/src/ox/Order.ts +0 -125
  195. package/src/ox/OrdersFilters.test.ts +0 -182
  196. package/src/ox/OrdersFilters.ts +0 -125
  197. package/src/ox/Pagination.test.ts +0 -162
  198. package/src/ox/Pagination.ts +0 -164
  199. package/src/ox/PoolId.test.ts +0 -33
  200. package/src/ox/PoolId.ts +0 -27
  201. package/src/ox/RpcSchema.ts +0 -35
  202. package/src/ox/SignatureEnvelope.test.ts +0 -1876
  203. package/src/ox/SignatureEnvelope.ts +0 -791
  204. package/src/ox/Tick.test.ts +0 -281
  205. package/src/ox/Tick.ts +0 -181
  206. package/src/ox/TokenId.test.ts +0 -40
  207. package/src/ox/TokenId.ts +0 -50
  208. package/src/ox/TokenRole.test.ts +0 -16
  209. package/src/ox/TokenRole.ts +0 -27
  210. package/src/ox/Transaction.test.ts +0 -523
  211. package/src/ox/Transaction.ts +0 -332
  212. package/src/ox/TransactionEnvelopeTempo.test.ts +0 -1352
  213. package/src/ox/TransactionEnvelopeTempo.ts +0 -905
  214. package/src/ox/TransactionReceipt.ts +0 -190
  215. package/src/ox/TransactionRequest.ts +0 -147
  216. package/src/ox/e2e.test.ts +0 -1393
  217. package/src/ox/index.ts +0 -13
  218. package/src/prool/Instance.test.ts +0 -43
  219. package/src/prool/Instance.ts +0 -247
  220. package/src/prool/chain.json +0 -238
  221. package/src/prool/index.ts +0 -1
  222. package/src/viem/Actions/__snapshots__/dex.test.ts.snap +0 -850
  223. package/src/wagmi/Actions/__snapshots__/dex.test.ts.snap +0 -310
  224. package/src/wagmi/Hooks/__snapshots__/dex.test.ts.snap +0 -457
@@ -1,1876 +0,0 @@
1
- import { Hex, PublicKey, Secp256k1, Signature, WebAuthnP256 } from 'ox'
2
- import { describe, expect, test } from 'vitest'
3
- import * as SignatureEnvelope from './SignatureEnvelope.js'
4
-
5
- const publicKey = PublicKey.from({
6
- prefix: 4,
7
- x: 78495282704852028275327922540131762143565388050940484317945369745559774511861n,
8
- y: 8109764566587999957624872393871720746996669263962991155166704261108473113504n,
9
- })
10
-
11
- const p256Signature = Signature.from({
12
- r: 92602584010956101470289867944347135737570451066466093224269890121909314569518n,
13
- s: 54171125190222965779385658110416711469231271457324878825831748147306957269813n,
14
- yParity: 0,
15
- })
16
-
17
- const signature_secp256k1 = Secp256k1.sign({
18
- payload: '0xdeadbeef',
19
- privateKey: Secp256k1.randomPrivateKey(),
20
- })
21
-
22
- const signature_p256 = SignatureEnvelope.from({
23
- signature: p256Signature,
24
- publicKey,
25
- prehash: true,
26
- })
27
-
28
- const signature_webauthn = SignatureEnvelope.from({
29
- signature: p256Signature,
30
- publicKey,
31
- metadata: {
32
- authenticatorData: WebAuthnP256.getAuthenticatorData({ rpId: 'localhost' }),
33
- clientDataJSON: WebAuthnP256.getClientDataJSON({
34
- challenge: '0xdeadbeef',
35
- origin: 'http://localhost',
36
- }),
37
- },
38
- })
39
-
40
- // Keychain signatures with different inner types
41
- const signature_keychain_secp256k1 = SignatureEnvelope.from({
42
- userAddress: '0x1234567890123456789012345678901234567890',
43
- inner: SignatureEnvelope.from(signature_secp256k1),
44
- })
45
-
46
- const signature_keychain_p256 = SignatureEnvelope.from({
47
- userAddress: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd',
48
- inner: signature_p256,
49
- })
50
-
51
- const signature_keychain_webauthn = SignatureEnvelope.from({
52
- userAddress: '0xfedcbafedcbafedcbafedcbafedcbafedcbafedc',
53
- inner: signature_webauthn,
54
- })
55
-
56
- describe('assert', () => {
57
- describe('secp256k1', () => {
58
- test('behavior: validates valid signature', () => {
59
- expect(() =>
60
- SignatureEnvelope.assert({
61
- signature: signature_secp256k1,
62
- type: 'secp256k1',
63
- }),
64
- ).not.toThrow()
65
- })
66
-
67
- test('behavior: validates signature without explicit type', () => {
68
- expect(() =>
69
- SignatureEnvelope.assert({ signature: signature_secp256k1 }),
70
- ).not.toThrow()
71
- })
72
-
73
- test('error: throws on invalid signature values', () => {
74
- expect(() =>
75
- SignatureEnvelope.assert({
76
- signature: {
77
- r: 0n,
78
- s: 0n,
79
- yParity: 2,
80
- },
81
- type: 'secp256k1',
82
- }),
83
- ).toThrowErrorMatchingInlineSnapshot(
84
- `[Signature.InvalidYParityError: Value \`2\` is an invalid y-parity value. Y-parity must be 0 or 1.]`,
85
- )
86
- })
87
- })
88
-
89
- describe('p256', () => {
90
- test('behavior: validates valid P256 signature', () => {
91
- expect(() => SignatureEnvelope.assert(signature_p256)).not.toThrow()
92
- })
93
-
94
- test('behavior: validates P256 signature without explicit type', () => {
95
- const { type: _, ...signatureWithoutType } = signature_p256
96
- expect(() => SignatureEnvelope.assert(signatureWithoutType)).not.toThrow()
97
- })
98
-
99
- test('error: throws on invalid prehash type', () => {
100
- expect(() =>
101
- SignatureEnvelope.assert({
102
- ...signature_p256,
103
- prehash: 'true' as any,
104
- }),
105
- ).toThrowErrorMatchingInlineSnapshot(
106
- `
107
- [SignatureEnvelope.MissingPropertiesError: Signature envelope of type "p256" is missing required properties: \`prehash\`.
108
-
109
- Provided: {"signature":{"r":"92602584010956101470289867944347135737570451066466093224269890121909314569518#__bigint","s":"54171125190222965779385658110416711469231271457324878825831748147306957269813#__bigint","yParity":0},"publicKey":{"prefix":4,"x":"78495282704852028275327922540131762143565388050940484317945369745559774511861#__bigint","y":"8109764566587999957624872393871720746996669263962991155166704261108473113504#__bigint"},"prehash":"true","type":"p256"}]
110
- `,
111
- )
112
- })
113
-
114
- test('error: throws on missing publicKey', () => {
115
- const { publicKey: _, ...withoutPublicKey } = signature_p256
116
- expect(() =>
117
- SignatureEnvelope.assert(withoutPublicKey as any),
118
- ).toThrowErrorMatchingInlineSnapshot(
119
- `
120
- [SignatureEnvelope.MissingPropertiesError: Signature envelope of type "p256" is missing required properties: \`publicKey\`.
121
-
122
- Provided: {"signature":{"r":"92602584010956101470289867944347135737570451066466093224269890121909314569518#__bigint","s":"54171125190222965779385658110416711469231271457324878825831748147306957269813#__bigint","yParity":0},"prehash":true,"type":"p256"}]
123
- `,
124
- )
125
- })
126
-
127
- test('error: throws on missing signature.r', () => {
128
- const invalid = {
129
- signature: { s: 1n } as any,
130
- publicKey,
131
- prehash: true,
132
- type: 'p256' as const,
133
- }
134
- expect(() =>
135
- SignatureEnvelope.assert(invalid),
136
- ).toThrowErrorMatchingInlineSnapshot(
137
- `
138
- [SignatureEnvelope.MissingPropertiesError: Signature envelope of type "p256" is missing required properties: \`signature.r\`.
139
-
140
- Provided: {"signature":{"s":"1#__bigint"},"publicKey":{"prefix":4,"x":"78495282704852028275327922540131762143565388050940484317945369745559774511861#__bigint","y":"8109764566587999957624872393871720746996669263962991155166704261108473113504#__bigint"},"prehash":true,"type":"p256"}]
141
- `,
142
- )
143
- })
144
-
145
- test('error: throws on missing signature.s', () => {
146
- const invalid = {
147
- signature: { r: 1n } as any,
148
- publicKey,
149
- prehash: true,
150
- type: 'p256' as const,
151
- }
152
- expect(() =>
153
- SignatureEnvelope.assert(invalid),
154
- ).toThrowErrorMatchingInlineSnapshot(
155
- `
156
- [SignatureEnvelope.MissingPropertiesError: Signature envelope of type "p256" is missing required properties: \`signature.s\`.
157
-
158
- Provided: {"signature":{"r":"1#__bigint"},"publicKey":{"prefix":4,"x":"78495282704852028275327922540131762143565388050940484317945369745559774511861#__bigint","y":"8109764566587999957624872393871720746996669263962991155166704261108473113504#__bigint"},"prehash":true,"type":"p256"}]
159
- `,
160
- )
161
- })
162
-
163
- test('error: throws on missing publicKey.x', () => {
164
- const invalid = {
165
- signature: p256Signature,
166
- publicKey: { y: 1n } as any,
167
- prehash: true,
168
- type: 'p256' as const,
169
- }
170
- expect(() =>
171
- SignatureEnvelope.assert(invalid),
172
- ).toThrowErrorMatchingInlineSnapshot(
173
- `
174
- [SignatureEnvelope.MissingPropertiesError: Signature envelope of type "p256" is missing required properties: \`publicKey.x\`.
175
-
176
- Provided: {"signature":{"r":"92602584010956101470289867944347135737570451066466093224269890121909314569518#__bigint","s":"54171125190222965779385658110416711469231271457324878825831748147306957269813#__bigint","yParity":0},"publicKey":{"y":"1#__bigint"},"prehash":true,"type":"p256"}]
177
- `,
178
- )
179
- })
180
-
181
- test('error: throws on missing publicKey.y', () => {
182
- const invalid = {
183
- signature: p256Signature,
184
- publicKey: { x: 1n } as any,
185
- prehash: true,
186
- type: 'p256' as const,
187
- }
188
- expect(() =>
189
- SignatureEnvelope.assert(invalid),
190
- ).toThrowErrorMatchingInlineSnapshot(
191
- `
192
- [SignatureEnvelope.MissingPropertiesError: Signature envelope of type "p256" is missing required properties: \`publicKey.y\`.
193
-
194
- Provided: {"signature":{"r":"92602584010956101470289867944347135737570451066466093224269890121909314569518#__bigint","s":"54171125190222965779385658110416711469231271457324878825831748147306957269813#__bigint","yParity":0},"publicKey":{"x":"1#__bigint"},"prehash":true,"type":"p256"}]
195
- `,
196
- )
197
- })
198
-
199
- test('error: throws with all missing properties listed', () => {
200
- const invalid = {
201
- signature: {} as any,
202
- type: 'p256' as const,
203
- }
204
- expect(() =>
205
- SignatureEnvelope.assert(invalid),
206
- ).toThrowErrorMatchingInlineSnapshot(
207
- `
208
- [SignatureEnvelope.MissingPropertiesError: Signature envelope of type "p256" is missing required properties: \`signature.r\`, \`signature.s\`, \`prehash\`, \`publicKey\`.
209
-
210
- Provided: {"signature":{},"type":"p256"}]
211
- `,
212
- )
213
- })
214
- })
215
-
216
- describe('webAuthn', () => {
217
- test('behavior: validates valid WebAuthn signature', () => {
218
- expect(() => SignatureEnvelope.assert(signature_webauthn)).not.toThrow()
219
- })
220
-
221
- test('behavior: validates WebAuthn signature without explicit type', () => {
222
- const { type: _, ...signatureWithoutType } = signature_webauthn
223
- expect(() => SignatureEnvelope.assert(signatureWithoutType)).not.toThrow()
224
- })
225
-
226
- test('error: throws on missing metadata', () => {
227
- const { metadata: _, ...withoutMetadata } = signature_webauthn
228
- expect(() =>
229
- SignatureEnvelope.assert(withoutMetadata as any),
230
- ).toThrowErrorMatchingInlineSnapshot(
231
- `
232
- [SignatureEnvelope.MissingPropertiesError: Signature envelope of type "webAuthn" is missing required properties: \`metadata\`.
233
-
234
- Provided: {"signature":{"r":"92602584010956101470289867944347135737570451066466093224269890121909314569518#__bigint","s":"54171125190222965779385658110416711469231271457324878825831748147306957269813#__bigint","yParity":0},"publicKey":{"prefix":4,"x":"78495282704852028275327922540131762143565388050940484317945369745559774511861#__bigint","y":"8109764566587999957624872393871720746996669263962991155166704261108473113504#__bigint"},"type":"webAuthn"}]
235
- `,
236
- )
237
- })
238
-
239
- test('error: throws on missing publicKey', () => {
240
- const { publicKey: _, ...withoutPublicKey } = signature_webauthn
241
- expect(() =>
242
- SignatureEnvelope.assert(withoutPublicKey as any),
243
- ).toThrowErrorMatchingInlineSnapshot(
244
- `
245
- [SignatureEnvelope.MissingPropertiesError: Signature envelope of type "webAuthn" is missing required properties: \`publicKey\`.
246
-
247
- Provided: {"signature":{"r":"92602584010956101470289867944347135737570451066466093224269890121909314569518#__bigint","s":"54171125190222965779385658110416711469231271457324878825831748147306957269813#__bigint","yParity":0},"metadata":{"authenticatorData":"0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000","clientDataJSON":"{\\"type\\":\\"webauthn.get\\",\\"challenge\\":\\"3q2-7w\\",\\"origin\\":\\"http://localhost\\",\\"crossOrigin\\":false}"},"type":"webAuthn"}]
248
- `,
249
- )
250
- })
251
-
252
- test('error: throws on missing signature.r', () => {
253
- const invalid = {
254
- signature: { s: 1n } as any,
255
- publicKey,
256
- metadata: {
257
- authenticatorData: WebAuthnP256.getAuthenticatorData({
258
- rpId: 'localhost',
259
- }),
260
- clientDataJSON: WebAuthnP256.getClientDataJSON({
261
- challenge: '0xdeadbeef',
262
- origin: 'http://localhost',
263
- }),
264
- },
265
- type: 'webAuthn' as const,
266
- }
267
- expect(() =>
268
- SignatureEnvelope.assert(invalid),
269
- ).toThrowErrorMatchingInlineSnapshot(
270
- `
271
- [SignatureEnvelope.MissingPropertiesError: Signature envelope of type "webAuthn" is missing required properties: \`signature.r\`.
272
-
273
- Provided: {"signature":{"s":"1#__bigint"},"publicKey":{"prefix":4,"x":"78495282704852028275327922540131762143565388050940484317945369745559774511861#__bigint","y":"8109764566587999957624872393871720746996669263962991155166704261108473113504#__bigint"},"metadata":{"authenticatorData":"0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000","clientDataJSON":"{\\"type\\":\\"webauthn.get\\",\\"challenge\\":\\"3q2-7w\\",\\"origin\\":\\"http://localhost\\",\\"crossOrigin\\":false}"},"type":"webAuthn"}]
274
- `,
275
- )
276
- })
277
-
278
- test('error: throws on missing signature.s', () => {
279
- const invalid = {
280
- signature: { r: 1n } as any,
281
- publicKey,
282
- metadata: {
283
- authenticatorData: WebAuthnP256.getAuthenticatorData({
284
- rpId: 'localhost',
285
- }),
286
- clientDataJSON: WebAuthnP256.getClientDataJSON({
287
- challenge: '0xdeadbeef',
288
- origin: 'http://localhost',
289
- }),
290
- },
291
- type: 'webAuthn' as const,
292
- }
293
- expect(() =>
294
- SignatureEnvelope.assert(invalid),
295
- ).toThrowErrorMatchingInlineSnapshot(
296
- `
297
- [SignatureEnvelope.MissingPropertiesError: Signature envelope of type "webAuthn" is missing required properties: \`signature.s\`.
298
-
299
- Provided: {"signature":{"r":"1#__bigint"},"publicKey":{"prefix":4,"x":"78495282704852028275327922540131762143565388050940484317945369745559774511861#__bigint","y":"8109764566587999957624872393871720746996669263962991155166704261108473113504#__bigint"},"metadata":{"authenticatorData":"0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000","clientDataJSON":"{\\"type\\":\\"webauthn.get\\",\\"challenge\\":\\"3q2-7w\\",\\"origin\\":\\"http://localhost\\",\\"crossOrigin\\":false}"},"type":"webAuthn"}]
300
- `,
301
- )
302
- })
303
-
304
- test('error: throws on missing metadata.authenticatorData', () => {
305
- const invalid = {
306
- signature: p256Signature,
307
- publicKey,
308
- metadata: {
309
- clientDataJSON: WebAuthnP256.getClientDataJSON({
310
- challenge: '0xdeadbeef',
311
- origin: 'http://localhost',
312
- }),
313
- } as any,
314
- type: 'webAuthn' as const,
315
- }
316
- expect(() =>
317
- SignatureEnvelope.assert(invalid),
318
- ).toThrowErrorMatchingInlineSnapshot(
319
- `
320
- [SignatureEnvelope.MissingPropertiesError: Signature envelope of type "webAuthn" is missing required properties: \`metadata.authenticatorData\`.
321
-
322
- Provided: {"signature":{"r":"92602584010956101470289867944347135737570451066466093224269890121909314569518#__bigint","s":"54171125190222965779385658110416711469231271457324878825831748147306957269813#__bigint","yParity":0},"publicKey":{"prefix":4,"x":"78495282704852028275327922540131762143565388050940484317945369745559774511861#__bigint","y":"8109764566587999957624872393871720746996669263962991155166704261108473113504#__bigint"},"metadata":{"clientDataJSON":"{\\"type\\":\\"webauthn.get\\",\\"challenge\\":\\"3q2-7w\\",\\"origin\\":\\"http://localhost\\",\\"crossOrigin\\":false}"},"type":"webAuthn"}]
323
- `,
324
- )
325
- })
326
-
327
- test('error: throws on missing metadata.clientDataJSON', () => {
328
- const invalid = {
329
- signature: p256Signature,
330
- publicKey,
331
- metadata: {
332
- authenticatorData: WebAuthnP256.getAuthenticatorData({
333
- rpId: 'localhost',
334
- }),
335
- } as any,
336
- type: 'webAuthn' as const,
337
- }
338
- expect(() =>
339
- SignatureEnvelope.assert(invalid),
340
- ).toThrowErrorMatchingInlineSnapshot(
341
- `
342
- [SignatureEnvelope.MissingPropertiesError: Signature envelope of type "webAuthn" is missing required properties: \`metadata.clientDataJSON\`.
343
-
344
- Provided: {"signature":{"r":"92602584010956101470289867944347135737570451066466093224269890121909314569518#__bigint","s":"54171125190222965779385658110416711469231271457324878825831748147306957269813#__bigint","yParity":0},"publicKey":{"prefix":4,"x":"78495282704852028275327922540131762143565388050940484317945369745559774511861#__bigint","y":"8109764566587999957624872393871720746996669263962991155166704261108473113504#__bigint"},"metadata":{"authenticatorData":"0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000"},"type":"webAuthn"}]
345
- `,
346
- )
347
- })
348
-
349
- test('error: throws on missing publicKey.x', () => {
350
- const invalid = {
351
- signature: p256Signature,
352
- publicKey: { y: 1n } as any,
353
- metadata: {
354
- authenticatorData: WebAuthnP256.getAuthenticatorData({
355
- rpId: 'localhost',
356
- }),
357
- clientDataJSON: WebAuthnP256.getClientDataJSON({
358
- challenge: '0xdeadbeef',
359
- origin: 'http://localhost',
360
- }),
361
- },
362
- type: 'webAuthn' as const,
363
- }
364
- expect(() =>
365
- SignatureEnvelope.assert(invalid),
366
- ).toThrowErrorMatchingInlineSnapshot(
367
- `
368
- [SignatureEnvelope.MissingPropertiesError: Signature envelope of type "webAuthn" is missing required properties: \`publicKey.x\`.
369
-
370
- Provided: {"signature":{"r":"92602584010956101470289867944347135737570451066466093224269890121909314569518#__bigint","s":"54171125190222965779385658110416711469231271457324878825831748147306957269813#__bigint","yParity":0},"publicKey":{"y":"1#__bigint"},"metadata":{"authenticatorData":"0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000","clientDataJSON":"{\\"type\\":\\"webauthn.get\\",\\"challenge\\":\\"3q2-7w\\",\\"origin\\":\\"http://localhost\\",\\"crossOrigin\\":false}"},"type":"webAuthn"}]
371
- `,
372
- )
373
- })
374
-
375
- test('error: throws on missing publicKey.y', () => {
376
- const invalid = {
377
- signature: p256Signature,
378
- publicKey: { x: 1n } as any,
379
- metadata: {
380
- authenticatorData: WebAuthnP256.getAuthenticatorData({
381
- rpId: 'localhost',
382
- }),
383
- clientDataJSON: WebAuthnP256.getClientDataJSON({
384
- challenge: '0xdeadbeef',
385
- origin: 'http://localhost',
386
- }),
387
- },
388
- type: 'webAuthn' as const,
389
- }
390
- expect(() =>
391
- SignatureEnvelope.assert(invalid),
392
- ).toThrowErrorMatchingInlineSnapshot(
393
- `
394
- [SignatureEnvelope.MissingPropertiesError: Signature envelope of type "webAuthn" is missing required properties: \`publicKey.y\`.
395
-
396
- Provided: {"signature":{"r":"92602584010956101470289867944347135737570451066466093224269890121909314569518#__bigint","s":"54171125190222965779385658110416711469231271457324878825831748147306957269813#__bigint","yParity":0},"publicKey":{"x":"1#__bigint"},"metadata":{"authenticatorData":"0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000","clientDataJSON":"{\\"type\\":\\"webauthn.get\\",\\"challenge\\":\\"3q2-7w\\",\\"origin\\":\\"http://localhost\\",\\"crossOrigin\\":false}"},"type":"webAuthn"}]
397
- `,
398
- )
399
- })
400
- })
401
-
402
- describe('keychain', () => {
403
- test('behavior: validates valid keychain with secp256k1 inner', () => {
404
- expect(() =>
405
- SignatureEnvelope.assert(signature_keychain_secp256k1),
406
- ).not.toThrow()
407
- })
408
-
409
- test('behavior: validates valid keychain with p256 inner', () => {
410
- expect(() =>
411
- SignatureEnvelope.assert(signature_keychain_p256),
412
- ).not.toThrow()
413
- })
414
-
415
- test('behavior: validates valid keychain with webAuthn inner', () => {
416
- expect(() =>
417
- SignatureEnvelope.assert(signature_keychain_webauthn),
418
- ).not.toThrow()
419
- })
420
-
421
- test('behavior: validates keychain without explicit type', () => {
422
- const { type: _, ...signatureWithoutType } = signature_keychain_secp256k1
423
- expect(() => SignatureEnvelope.assert(signatureWithoutType)).not.toThrow()
424
- })
425
-
426
- test('error: throws on invalid inner signature', () => {
427
- expect(() =>
428
- SignatureEnvelope.assert({
429
- userAddress: '0x1234567890123456789012345678901234567890',
430
- inner: SignatureEnvelope.from({
431
- r: 0n,
432
- s: 0n,
433
- yParity: 2,
434
- }),
435
- type: 'keychain',
436
- } as any),
437
- ).toThrowErrorMatchingInlineSnapshot(
438
- `[Signature.InvalidYParityError: Value \`2\` is an invalid y-parity value. Y-parity must be 0 or 1.]`,
439
- )
440
- })
441
- })
442
-
443
- test('error: throws on invalid envelope', () => {
444
- expect(() =>
445
- SignatureEnvelope.assert({} as any),
446
- ).toThrowErrorMatchingInlineSnapshot(
447
- `[SignatureEnvelope.CoercionError: Unable to coerce value (\`{}\`) to a valid signature envelope.]`,
448
- )
449
- })
450
-
451
- test('error: throws on incomplete signature', () => {
452
- expect(() =>
453
- SignatureEnvelope.assert({
454
- r: 0n,
455
- s: 0n,
456
- } as any),
457
- ).toThrowErrorMatchingInlineSnapshot(
458
- `[SignatureEnvelope.CoercionError: Unable to coerce value (\`{"r":"0#__bigint","s":"0#__bigint"}\`) to a valid signature envelope.]`,
459
- )
460
- })
461
- })
462
-
463
- describe('deserialize', () => {
464
- describe('secp256k1', () => {
465
- test('behavior: deserializes valid signature', () => {
466
- const serialized = Signature.toHex(signature_secp256k1)
467
-
468
- const envelope = SignatureEnvelope.deserialize(serialized)
469
-
470
- expect(envelope).toMatchObject({
471
- signature: {
472
- r: signature_secp256k1.r,
473
- s: signature_secp256k1.s,
474
- yParity: signature_secp256k1.yParity,
475
- },
476
- type: 'secp256k1',
477
- })
478
- })
479
-
480
- test('error: throws on invalid size', () => {
481
- expect(() =>
482
- SignatureEnvelope.deserialize('0xdeadbeef'),
483
- ).toThrowErrorMatchingInlineSnapshot(
484
- `
485
- [SignatureEnvelope.InvalidSerializedError: Unable to deserialize signature envelope: Unknown signature type identifier: 0xde. Expected 0x01 (P256) or 0x02 (WebAuthn)
486
-
487
- Serialized: 0xdeadbeef]
488
- `,
489
- )
490
- })
491
-
492
- test('error: throws on invalid yParity', () => {
493
- // Signature with invalid yParity (must be 0 or 1)
494
- const invalidSig =
495
- '0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000102'
496
- expect(() =>
497
- SignatureEnvelope.deserialize(invalidSig),
498
- ).toThrowErrorMatchingInlineSnapshot(
499
- `[Signature.InvalidYParityError: Value \`2\` is an invalid y-parity value. Y-parity must be 0 or 1.]`,
500
- )
501
- })
502
- })
503
-
504
- describe('p256', () => {
505
- test('behavior: deserializes P256 signature', () => {
506
- const serialized = SignatureEnvelope.serialize(signature_p256)
507
- const deserialized = SignatureEnvelope.deserialize(serialized)
508
-
509
- expect(deserialized).toMatchObject({
510
- signature: {
511
- r: signature_p256.signature.r,
512
- s: signature_p256.signature.s,
513
- },
514
- publicKey: {
515
- x: signature_p256.publicKey.x,
516
- y: signature_p256.publicKey.y,
517
- },
518
- prehash: signature_p256.prehash,
519
- type: 'p256',
520
- })
521
- })
522
-
523
- test('error: throws on invalid P256 signature length', () => {
524
- // P256 signature with wrong length (should be 130 bytes total, but only 100)
525
- const invalidSig = `0x01${'00'.repeat(100)}` as `0x${string}`
526
- expect(() =>
527
- SignatureEnvelope.deserialize(invalidSig),
528
- ).toThrowErrorMatchingInlineSnapshot(
529
- `
530
- [SignatureEnvelope.InvalidSerializedError: Unable to deserialize signature envelope: Invalid P256 signature envelope size: expected 129 bytes, got 100 bytes
531
-
532
- Serialized: 0x0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000]
533
- `,
534
- )
535
- })
536
- })
537
-
538
- describe('webAuthn', () => {
539
- test('behavior: deserializes WebAuthn signature', () => {
540
- const serialized = SignatureEnvelope.serialize(signature_webauthn)
541
- const deserialized = SignatureEnvelope.deserialize(serialized)
542
-
543
- expect(deserialized).toMatchObject({
544
- signature: {
545
- r: signature_webauthn.signature.r,
546
- s: signature_webauthn.signature.s,
547
- },
548
- publicKey: {
549
- x: signature_webauthn.publicKey.x,
550
- y: signature_webauthn.publicKey.y,
551
- },
552
- metadata: {
553
- authenticatorData: signature_webauthn.metadata.authenticatorData,
554
- clientDataJSON: signature_webauthn.metadata.clientDataJSON,
555
- },
556
- type: 'webAuthn',
557
- })
558
- })
559
-
560
- test('error: throws on invalid WebAuthn signature length', () => {
561
- // WebAuthn signature too short (must be at least 129 bytes: 1 type + 128 signature data)
562
- const invalidSig = `0x02${'00'.repeat(100)}` as const
563
- expect(() =>
564
- SignatureEnvelope.deserialize(invalidSig),
565
- ).toThrowErrorMatchingInlineSnapshot(
566
- `
567
- [SignatureEnvelope.InvalidSerializedError: Unable to deserialize signature envelope: Invalid WebAuthn signature envelope size: expected at least 128 bytes, got 100 bytes
568
-
569
- Serialized: 0x0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000]
570
- `,
571
- )
572
- })
573
-
574
- test('error: throws on invalid clientDataJSON', () => {
575
- // Create a signature with invalid JSON (not properly formatted)
576
- const invalidMetadata = {
577
- authenticatorData: `0x${'00'.repeat(37)}` as const,
578
- clientDataJSON: 'not-valid-json',
579
- }
580
- const serialized = SignatureEnvelope.serialize({
581
- ...signature_webauthn,
582
- metadata: invalidMetadata,
583
- })
584
-
585
- expect(() =>
586
- SignatureEnvelope.deserialize(serialized),
587
- ).toThrowErrorMatchingInlineSnapshot(
588
- `
589
- [SignatureEnvelope.InvalidSerializedError: Unable to deserialize signature envelope: Unable to parse WebAuthn metadata: could not extract valid authenticatorData and clientDataJSON
590
-
591
- Serialized: 0x02000000000000000000000000000000000000000000000000000000000000000000000000006e6f742d76616c69642d6a736f6eccbb3485d4726235f13cb15ef394fb7158179fb7b1925eccec0147671090c52e77c3c53373cc1e3b05e7c23f609deb17cea8fe097300c45411237e9fe4166b35ad8ac16e167d6992c3e120d7f17d2376bc1cbcf30c46ba6dd00ce07303e742f511edf6ce1c32de66846f56afa7be1cbd729bc35750b6d0cdcf3ec9d75461aba0]
592
- `,
593
- )
594
- })
595
-
596
- test('error: throws on unknown type identifier', () => {
597
- const unknownType = `0xff${'00'.repeat(129)}` as const
598
- expect(() =>
599
- SignatureEnvelope.deserialize(unknownType),
600
- ).toThrowErrorMatchingInlineSnapshot(
601
- `
602
- [SignatureEnvelope.InvalidSerializedError: Unable to deserialize signature envelope: Unknown signature type identifier: 0xff. Expected 0x01 (P256) or 0x02 (WebAuthn)
603
-
604
- Serialized: 0xff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000]
605
- `,
606
- )
607
- })
608
- })
609
-
610
- describe('keychain', () => {
611
- test('behavior: deserializes keychain signature with secp256k1 inner', () => {
612
- const serialized = SignatureEnvelope.serialize(
613
- signature_keychain_secp256k1,
614
- )
615
- const deserialized = SignatureEnvelope.deserialize(serialized)
616
-
617
- expect(deserialized).toMatchObject({
618
- userAddress: signature_keychain_secp256k1.userAddress,
619
- inner: SignatureEnvelope.from(signature_secp256k1),
620
- type: 'keychain',
621
- })
622
- })
623
-
624
- test('behavior: deserializes keychain signature with p256 inner', () => {
625
- const serialized = SignatureEnvelope.serialize(signature_keychain_p256)
626
- const deserialized = SignatureEnvelope.deserialize(serialized)
627
-
628
- expect(deserialized).toMatchInlineSnapshot(`
629
- {
630
- "inner": {
631
- "prehash": true,
632
- "publicKey": {
633
- "prefix": 4,
634
- "x": 78495282704852028275327922540131762143565388050940484317945369745559774511861n,
635
- "y": 8109764566587999957624872393871720746996669263962991155166704261108473113504n,
636
- },
637
- "signature": {
638
- "r": 92602584010956101470289867944347135737570451066466093224269890121909314569518n,
639
- "s": 54171125190222965779385658110416711469231271457324878825831748147306957269813n,
640
- },
641
- "type": "p256",
642
- },
643
- "type": "keychain",
644
- "userAddress": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
645
- }
646
- `)
647
- })
648
-
649
- test('behavior: deserializes keychain signature with webAuthn inner', () => {
650
- const serialized = SignatureEnvelope.serialize(
651
- signature_keychain_webauthn,
652
- )
653
- const deserialized = SignatureEnvelope.deserialize(serialized)
654
-
655
- expect(deserialized).toMatchInlineSnapshot(`
656
- {
657
- "inner": {
658
- "metadata": {
659
- "authenticatorData": "0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000",
660
- "clientDataJSON": "{"type":"webauthn.get","challenge":"3q2-7w","origin":"http://localhost","crossOrigin":false}",
661
- },
662
- "publicKey": {
663
- "prefix": 4,
664
- "x": 78495282704852028275327922540131762143565388050940484317945369745559774511861n,
665
- "y": 8109764566587999957624872393871720746996669263962991155166704261108473113504n,
666
- },
667
- "signature": {
668
- "r": 92602584010956101470289867944347135737570451066466093224269890121909314569518n,
669
- "s": 54171125190222965779385658110416711469231271457324878825831748147306957269813n,
670
- },
671
- "type": "webAuthn",
672
- },
673
- "type": "keychain",
674
- "userAddress": "0xfedcbafedcbafedcbafedcbafedcbafedcbafedc",
675
- }
676
- `)
677
- })
678
-
679
- test('error: throws on invalid keychain signature length', () => {
680
- // Keychain signature too short (must be at least 21 bytes: 1 type + 20 address)
681
- const invalidSig = `0x03${'00'.repeat(10)}` as const
682
- expect(() =>
683
- SignatureEnvelope.deserialize(invalidSig),
684
- ).toThrowErrorMatchingInlineSnapshot(
685
- `[Hex.SliceOffsetOutOfBoundsError: Slice starting at offset \`20\` is out-of-bounds (size: \`10\`).]`,
686
- )
687
- })
688
- })
689
- })
690
-
691
- describe('from', () => {
692
- describe('secp256k1', () => {
693
- test('behavior: coerces from hex string', () => {
694
- const serialized = Signature.toHex(signature_secp256k1)
695
-
696
- const envelope = SignatureEnvelope.from(serialized)
697
-
698
- expect(envelope).toMatchObject({
699
- signature: {
700
- r: signature_secp256k1.r,
701
- s: signature_secp256k1.s,
702
- yParity: signature_secp256k1.yParity,
703
- },
704
- type: 'secp256k1',
705
- })
706
- })
707
-
708
- test('behavior: returns object as-is', () => {
709
- const envelope: SignatureEnvelope.SignatureEnvelope = {
710
- signature: signature_secp256k1,
711
- type: 'secp256k1',
712
- }
713
-
714
- const result = SignatureEnvelope.from(envelope)
715
-
716
- expect(result).toEqual(envelope)
717
- })
718
-
719
- test('behavior: coerces from flat signature', () => {
720
- const result = SignatureEnvelope.from(signature_secp256k1)
721
-
722
- expect(result).toMatchObject({
723
- signature: {
724
- r: signature_secp256k1.r,
725
- s: signature_secp256k1.s,
726
- yParity: signature_secp256k1.yParity,
727
- },
728
- type: 'secp256k1',
729
- })
730
- })
731
- })
732
-
733
- describe('p256', () => {
734
- test('behavior: coerces from hex string', () => {
735
- const serialized = SignatureEnvelope.serialize(signature_p256)
736
- const envelope = SignatureEnvelope.from(serialized)
737
-
738
- expect(envelope).toMatchObject({
739
- signature: {
740
- r: signature_p256.signature.r,
741
- s: signature_p256.signature.s,
742
- },
743
- type: 'p256',
744
- })
745
- })
746
-
747
- test('behavior: adds type to object', () => {
748
- const { type: _, ...withoutType } = signature_p256
749
- const envelope = SignatureEnvelope.from(withoutType)
750
-
751
- expect(envelope.type).toBe('p256')
752
- })
753
- })
754
-
755
- describe('webAuthn', () => {
756
- test('behavior: coerces from hex string', () => {
757
- const serialized = SignatureEnvelope.serialize(signature_webauthn)
758
- const envelope = SignatureEnvelope.from(serialized)
759
-
760
- expect(envelope).toMatchObject({
761
- signature: {
762
- r: signature_webauthn.signature.r,
763
- s: signature_webauthn.signature.s,
764
- },
765
- type: 'webAuthn',
766
- })
767
- })
768
-
769
- test('behavior: adds type to object', () => {
770
- const { type: _, ...withoutType } = signature_webauthn
771
- const envelope = SignatureEnvelope.from(withoutType)
772
-
773
- expect(envelope.type).toBe('webAuthn')
774
- })
775
- })
776
-
777
- describe('keychain', () => {
778
- test('behavior: coerces from hex string with secp256k1 inner', () => {
779
- const serialized = SignatureEnvelope.serialize(
780
- signature_keychain_secp256k1,
781
- )
782
- const envelope = SignatureEnvelope.from(serialized)
783
-
784
- expect(envelope).toMatchObject({
785
- userAddress: signature_keychain_secp256k1.userAddress,
786
- inner: SignatureEnvelope.from(signature_secp256k1),
787
- type: 'keychain',
788
- })
789
- })
790
-
791
- test('behavior: coerces from hex string with p256 inner', () => {
792
- const serialized = SignatureEnvelope.serialize(signature_keychain_p256)
793
- const envelope = SignatureEnvelope.from(serialized)
794
-
795
- expect(envelope).toMatchObject({
796
- userAddress: signature_keychain_p256.userAddress,
797
- type: 'keychain',
798
- })
799
- })
800
-
801
- test('behavior: adds type to object', () => {
802
- const { type: _, ...withoutType } = signature_keychain_secp256k1
803
- const envelope = SignatureEnvelope.from(withoutType)
804
-
805
- expect(envelope.type).toBe('keychain')
806
- })
807
- })
808
- })
809
-
810
- describe('getType', () => {
811
- describe('secp256k1', () => {
812
- test('behavior: returns explicit type', () => {
813
- const envelope: SignatureEnvelope.SignatureEnvelope = {
814
- signature: { r: 0n, s: 0n, yParity: 0 },
815
- type: 'secp256k1',
816
- }
817
-
818
- expect(SignatureEnvelope.getType(envelope)).toBe('secp256k1')
819
- })
820
-
821
- test('behavior: infers type from properties', () => {
822
- expect(
823
- SignatureEnvelope.getType({ signature: signature_secp256k1 }),
824
- ).toBe('secp256k1')
825
- })
826
-
827
- test('behavior: infers type from flat signature', () => {
828
- const signature = { r: 0n, s: 0n, yParity: 0 }
829
- expect(SignatureEnvelope.getType(signature)).toBe('secp256k1')
830
- })
831
- })
832
-
833
- describe('p256', () => {
834
- test('behavior: returns explicit type', () => {
835
- expect(SignatureEnvelope.getType(signature_p256)).toBe('p256')
836
- })
837
-
838
- test('behavior: infers type from properties', () => {
839
- const { type: _, ...signatureWithoutType } = signature_p256
840
- expect(SignatureEnvelope.getType(signatureWithoutType)).toBe('p256')
841
- })
842
- })
843
-
844
- describe('webAuthn', () => {
845
- test('behavior: returns explicit type', () => {
846
- expect(SignatureEnvelope.getType(signature_webauthn)).toBe('webAuthn')
847
- })
848
-
849
- test('behavior: infers type from properties', () => {
850
- const { type: _, ...signatureWithoutType } = signature_webauthn
851
- expect(SignatureEnvelope.getType(signatureWithoutType)).toBe('webAuthn')
852
- })
853
- })
854
-
855
- describe('keychain', () => {
856
- test('behavior: returns explicit type', () => {
857
- expect(SignatureEnvelope.getType(signature_keychain_secp256k1)).toBe(
858
- 'keychain',
859
- )
860
- })
861
-
862
- test('behavior: infers type from properties', () => {
863
- const { type: _, ...signatureWithoutType } = signature_keychain_secp256k1
864
- expect(SignatureEnvelope.getType(signatureWithoutType)).toBe('keychain')
865
- })
866
-
867
- test('behavior: infers type for keychain with p256 inner', () => {
868
- const { type: _, ...signatureWithoutType } = signature_keychain_p256
869
- expect(SignatureEnvelope.getType(signatureWithoutType)).toBe('keychain')
870
- })
871
-
872
- test('behavior: infers type for keychain with webAuthn inner', () => {
873
- const { type: _, ...signatureWithoutType } = signature_keychain_webauthn
874
- expect(SignatureEnvelope.getType(signatureWithoutType)).toBe('keychain')
875
- })
876
- })
877
-
878
- test('error: throws on invalid envelope', () => {
879
- expect(() =>
880
- SignatureEnvelope.getType({} as any),
881
- ).toThrowErrorMatchingInlineSnapshot(
882
- `[SignatureEnvelope.CoercionError: Unable to coerce value (\`{}\`) to a valid signature envelope.]`,
883
- )
884
- })
885
-
886
- test('error: throws on incomplete signature', () => {
887
- expect(() =>
888
- SignatureEnvelope.getType({
889
- r: 0n,
890
- s: 0n,
891
- } as any),
892
- ).toThrowErrorMatchingInlineSnapshot(
893
- `[SignatureEnvelope.CoercionError: Unable to coerce value (\`{"r":"0#__bigint","s":"0#__bigint"}\`) to a valid signature envelope.]`,
894
- )
895
- })
896
- })
897
-
898
- describe('serialize', () => {
899
- describe('secp256k1', () => {
900
- test('behavior: serializes with explicit type', () => {
901
- const envelope: SignatureEnvelope.SignatureEnvelope = {
902
- signature: signature_secp256k1,
903
- type: 'secp256k1',
904
- }
905
-
906
- const serialized = SignatureEnvelope.serialize(envelope)
907
-
908
- expect(serialized).toBe(Signature.toHex(signature_secp256k1))
909
- })
910
-
911
- test('behavior: serializes without explicit type', () => {
912
- const serialized = SignatureEnvelope.serialize({
913
- signature: signature_secp256k1,
914
- type: 'secp256k1',
915
- })
916
-
917
- expect(serialized).toBe(Signature.toHex(signature_secp256k1))
918
- })
919
- })
920
-
921
- describe('p256', () => {
922
- test('behavior: serializes P256 signature with type identifier', () => {
923
- const serialized = SignatureEnvelope.serialize(signature_p256)
924
-
925
- // Should be 130 bytes: 1 (type) + 32 (r) + 32 (s) + 32 (pubKeyX) + 32 (pubKeyY) + 1 (prehash)
926
- expect(serialized.length).toBe(2 + 130 * 2) // 2 for '0x' prefix + 130 bytes * 2 hex chars
927
-
928
- // First byte should be P256 type identifier (0x01)
929
- expect(serialized.slice(0, 4)).toBe('0x01')
930
- })
931
-
932
- test('behavior: serializes prehash flag correctly', () => {
933
- const withPreHashFalse = { ...signature_p256, prehash: false }
934
- const serialized = SignatureEnvelope.serialize(withPreHashFalse)
935
-
936
- // Last byte should be 0x00 for false
937
- expect(serialized.slice(-2)).toBe('00')
938
- })
939
- })
940
-
941
- describe('webAuthn', () => {
942
- test('behavior: serializes WebAuthn signature with type identifier', () => {
943
- const serialized = SignatureEnvelope.serialize(signature_webauthn)
944
-
945
- // Should be: 1 (type) + authenticatorData.length + clientDataJSON.length + 128 (signature components)
946
- const authDataLength =
947
- (signature_webauthn.metadata.authenticatorData.length - 2) / 2
948
- const clientDataLength = signature_webauthn.metadata.clientDataJSON.length
949
- const expectedLength =
950
- 2 + (1 + authDataLength + clientDataLength + 128) * 2
951
-
952
- expect(serialized.length).toBe(expectedLength)
953
-
954
- // First byte should be WebAuthn type identifier (0x02)
955
- expect(serialized.slice(0, 4)).toBe('0x02')
956
- })
957
-
958
- test('behavior: preserves metadata', () => {
959
- const serialized = SignatureEnvelope.serialize(signature_webauthn)
960
- const deserialized = SignatureEnvelope.deserialize(serialized)
961
-
962
- expect(deserialized.metadata?.authenticatorData).toBe(
963
- signature_webauthn.metadata.authenticatorData,
964
- )
965
- expect(deserialized.metadata?.clientDataJSON).toBe(
966
- signature_webauthn.metadata.clientDataJSON,
967
- )
968
- })
969
- })
970
-
971
- describe('keychain', () => {
972
- test('behavior: serializes keychain signature with secp256k1 inner and type identifier', () => {
973
- const serialized = SignatureEnvelope.serialize(
974
- signature_keychain_secp256k1,
975
- )
976
-
977
- // Should be: 1 (type) + 20 (address) + 65 (secp256k1 signature)
978
- expect(Hex.size(serialized)).toBe(1 + 20 + 65)
979
-
980
- // First byte should be Keychain type identifier (0x03)
981
- expect(Hex.slice(serialized, 0, 1)).toBe('0x03')
982
-
983
- // Next 20 bytes should be the user address (without '0x')
984
- expect(Hex.slice(serialized, 1, 21)).toBe(
985
- signature_keychain_secp256k1.userAddress,
986
- )
987
- })
988
-
989
- test('behavior: serializes keychain signature with p256 inner', () => {
990
- const serialized = SignatureEnvelope.serialize(signature_keychain_p256)
991
-
992
- // Should be: 1 (type) + 20 (address) + 130 (p256 signature with type)
993
- expect(Hex.size(serialized)).toBe(1 + 20 + 130)
994
-
995
- // First byte should be Keychain type identifier (0x03)
996
- expect(Hex.slice(serialized, 0, 1)).toBe('0x03')
997
-
998
- // Next 20 bytes should be the user address (without '0x')
999
- expect(Hex.slice(serialized, 1, 21)).toBe(
1000
- signature_keychain_p256.userAddress,
1001
- )
1002
-
1003
- // Next 130 bytes should be the p256 signature
1004
- expect(Hex.slice(serialized, 21, 151)).toBe(
1005
- SignatureEnvelope.serialize(signature_p256),
1006
- )
1007
- })
1008
-
1009
- test('behavior: serializes keychain signature with webAuthn inner', () => {
1010
- const serialized = SignatureEnvelope.serialize(
1011
- signature_keychain_webauthn,
1012
- )
1013
-
1014
- // First byte should be Keychain type identifier (0x03)
1015
- expect(Hex.slice(serialized, 0, 1)).toBe('0x03')
1016
-
1017
- // Should contain the user address
1018
- expect(Hex.slice(serialized, 1, 21)).toBe(
1019
- signature_keychain_webauthn.userAddress,
1020
- )
1021
-
1022
- // Next N bytes should be the webAuthn signature
1023
- expect(Hex.slice(serialized, 21)).toBe(
1024
- SignatureEnvelope.serialize(signature_webauthn),
1025
- )
1026
- })
1027
-
1028
- test('behavior: preserves userAddress and inner signature', () => {
1029
- const serialized = SignatureEnvelope.serialize(
1030
- signature_keychain_secp256k1,
1031
- )
1032
- const deserialized = SignatureEnvelope.deserialize(serialized)
1033
-
1034
- expect(deserialized.userAddress).toBe(
1035
- signature_keychain_secp256k1.userAddress,
1036
- )
1037
- expect(deserialized.inner).toMatchObject({
1038
- type: 'secp256k1',
1039
- signature: {
1040
- r: signature_secp256k1.r,
1041
- s: signature_secp256k1.s,
1042
- yParity: signature_secp256k1.yParity,
1043
- },
1044
- })
1045
- })
1046
- })
1047
-
1048
- describe('roundtrip', () => {
1049
- describe('secp256k1', () => {
1050
- test('behavior: roundtrips serialize -> deserialize', () => {
1051
- const envelope: SignatureEnvelope.Secp256k1 = {
1052
- signature: signature_secp256k1,
1053
- type: 'secp256k1',
1054
- }
1055
-
1056
- const serialized = SignatureEnvelope.serialize(envelope)
1057
- const deserialized = SignatureEnvelope.deserialize(serialized)
1058
-
1059
- expect(deserialized).toMatchObject({
1060
- signature: {
1061
- r: signature_secp256k1.r,
1062
- s: signature_secp256k1.s,
1063
- yParity: signature_secp256k1.yParity,
1064
- },
1065
- type: 'secp256k1',
1066
- })
1067
- })
1068
- })
1069
-
1070
- describe('p256', () => {
1071
- test('behavior: roundtrips serialize -> deserialize', () => {
1072
- const serialized = SignatureEnvelope.serialize(signature_p256)
1073
- const deserialized = SignatureEnvelope.deserialize(serialized)
1074
-
1075
- expect(deserialized).toMatchObject({
1076
- signature: {
1077
- r: signature_p256.signature.r,
1078
- s: signature_p256.signature.s,
1079
- },
1080
- publicKey: {
1081
- x: signature_p256.publicKey.x,
1082
- y: signature_p256.publicKey.y,
1083
- },
1084
- prehash: signature_p256.prehash,
1085
- type: 'p256',
1086
- })
1087
- })
1088
-
1089
- test('behavior: handles prehash=false', () => {
1090
- const signature = { ...signature_p256, prehash: false }
1091
- const serialized = SignatureEnvelope.serialize(signature)
1092
- const deserialized = SignatureEnvelope.deserialize(serialized)
1093
-
1094
- expect(deserialized.prehash).toBe(false)
1095
- })
1096
- })
1097
-
1098
- describe('webAuthn', () => {
1099
- test('behavior: roundtrips serialize -> deserialize', () => {
1100
- const serialized = SignatureEnvelope.serialize(signature_webauthn)
1101
- const deserialized = SignatureEnvelope.deserialize(serialized)
1102
-
1103
- expect(deserialized).toMatchObject({
1104
- signature: {
1105
- r: signature_webauthn.signature.r,
1106
- s: signature_webauthn.signature.s,
1107
- },
1108
- publicKey: {
1109
- x: signature_webauthn.publicKey.x,
1110
- y: signature_webauthn.publicKey.y,
1111
- },
1112
- metadata: {
1113
- authenticatorData: signature_webauthn.metadata.authenticatorData,
1114
- clientDataJSON: signature_webauthn.metadata.clientDataJSON,
1115
- },
1116
- type: 'webAuthn',
1117
- })
1118
- })
1119
-
1120
- test('behavior: handles variable-length clientDataJSON', () => {
1121
- const longClientData = JSON.stringify({
1122
- type: 'webAuthn.get',
1123
- challenge: 'a'.repeat(100),
1124
- origin: 'https://example.com',
1125
- })
1126
-
1127
- const signatureWithLongData = {
1128
- ...signature_webauthn,
1129
- metadata: {
1130
- ...signature_webauthn.metadata,
1131
- clientDataJSON: longClientData,
1132
- },
1133
- }
1134
-
1135
- const serialized = SignatureEnvelope.serialize(signatureWithLongData)
1136
- const deserialized = SignatureEnvelope.deserialize(serialized)
1137
-
1138
- expect(deserialized.metadata?.clientDataJSON).toBe(longClientData)
1139
- })
1140
- })
1141
-
1142
- describe('keychain', () => {
1143
- test('behavior: roundtrips serialize -> deserialize with secp256k1 inner', () => {
1144
- const serialized = SignatureEnvelope.serialize(
1145
- signature_keychain_secp256k1,
1146
- )
1147
- const deserialized = SignatureEnvelope.deserialize(serialized)
1148
-
1149
- expect(deserialized).toMatchObject(signature_keychain_secp256k1)
1150
- })
1151
-
1152
- test('behavior: roundtrips serialize -> deserialize with p256 inner', () => {
1153
- const serialized = SignatureEnvelope.serialize(signature_keychain_p256)
1154
- const deserialized = SignatureEnvelope.deserialize(serialized)
1155
-
1156
- expect(deserialized).toMatchInlineSnapshot(`
1157
- {
1158
- "inner": {
1159
- "prehash": true,
1160
- "publicKey": {
1161
- "prefix": 4,
1162
- "x": 78495282704852028275327922540131762143565388050940484317945369745559774511861n,
1163
- "y": 8109764566587999957624872393871720746996669263962991155166704261108473113504n,
1164
- },
1165
- "signature": {
1166
- "r": 92602584010956101470289867944347135737570451066466093224269890121909314569518n,
1167
- "s": 54171125190222965779385658110416711469231271457324878825831748147306957269813n,
1168
- },
1169
- "type": "p256",
1170
- },
1171
- "type": "keychain",
1172
- "userAddress": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
1173
- }
1174
- `)
1175
- })
1176
-
1177
- test('behavior: roundtrips serialize -> deserialize with webAuthn inner', () => {
1178
- const serialized = SignatureEnvelope.serialize(
1179
- signature_keychain_webauthn,
1180
- )
1181
- const deserialized = SignatureEnvelope.deserialize(serialized)
1182
-
1183
- expect(deserialized).toMatchInlineSnapshot(`
1184
- {
1185
- "inner": {
1186
- "metadata": {
1187
- "authenticatorData": "0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000",
1188
- "clientDataJSON": "{"type":"webauthn.get","challenge":"3q2-7w","origin":"http://localhost","crossOrigin":false}",
1189
- },
1190
- "publicKey": {
1191
- "prefix": 4,
1192
- "x": 78495282704852028275327922540131762143565388050940484317945369745559774511861n,
1193
- "y": 8109764566587999957624872393871720746996669263962991155166704261108473113504n,
1194
- },
1195
- "signature": {
1196
- "r": 92602584010956101470289867944347135737570451066466093224269890121909314569518n,
1197
- "s": 54171125190222965779385658110416711469231271457324878825831748147306957269813n,
1198
- },
1199
- "type": "webAuthn",
1200
- },
1201
- "type": "keychain",
1202
- "userAddress": "0xfedcbafedcbafedcbafedcbafedcbafedcbafedc",
1203
- }
1204
- `)
1205
- })
1206
- })
1207
- })
1208
-
1209
- test('error: throws on invalid envelope', () => {
1210
- const error = (() => {
1211
- try {
1212
- SignatureEnvelope.serialize({} as any)
1213
- } catch (e) {
1214
- return e
1215
- }
1216
- })() as SignatureEnvelope.CoercionError
1217
- expect(error).toBeInstanceOf(SignatureEnvelope.CoercionError)
1218
- expect(error.message).toMatchInlineSnapshot(
1219
- `"Unable to coerce value (\`{}\`) to a valid signature envelope."`,
1220
- )
1221
- })
1222
- })
1223
-
1224
- describe('validate', () => {
1225
- describe('secp256k1', () => {
1226
- test('behavior: returns true for valid signature', () => {
1227
- expect(
1228
- SignatureEnvelope.validate({
1229
- signature: signature_secp256k1,
1230
- type: 'secp256k1',
1231
- }),
1232
- ).toBe(true)
1233
- })
1234
-
1235
- test('behavior: returns true for signature without explicit type', () => {
1236
- expect(
1237
- SignatureEnvelope.validate({ signature: signature_secp256k1 }),
1238
- ).toBe(true)
1239
- })
1240
-
1241
- test('behavior: returns false for invalid signature values', () => {
1242
- expect(
1243
- SignatureEnvelope.validate({
1244
- signature: {
1245
- r: 0n,
1246
- s: 0n,
1247
- yParity: 2,
1248
- },
1249
- type: 'secp256k1',
1250
- }),
1251
- ).toBe(false)
1252
- })
1253
- })
1254
-
1255
- describe('p256', () => {
1256
- test('behavior: returns true for valid P256 signature', () => {
1257
- expect(SignatureEnvelope.validate(signature_p256)).toBe(true)
1258
- })
1259
-
1260
- test('behavior: returns false for invalid P256 signature', () => {
1261
- expect(
1262
- SignatureEnvelope.validate({
1263
- ...signature_p256,
1264
- prehash: 'invalid' as any,
1265
- }),
1266
- ).toBe(false)
1267
- })
1268
- })
1269
-
1270
- describe('webAuthn', () => {
1271
- test('behavior: returns true for valid WebAuthn signature', () => {
1272
- expect(SignatureEnvelope.validate(signature_webauthn)).toBe(true)
1273
- })
1274
-
1275
- test('behavior: returns false for invalid WebAuthn signature', () => {
1276
- const { metadata: _, ...withoutMetadata } = signature_webauthn
1277
- expect(SignatureEnvelope.validate(withoutMetadata as any)).toBe(false)
1278
- })
1279
- })
1280
-
1281
- describe('keychain', () => {
1282
- test('behavior: returns true for valid keychain with secp256k1 inner', () => {
1283
- expect(SignatureEnvelope.validate(signature_keychain_secp256k1)).toBe(
1284
- true,
1285
- )
1286
- })
1287
-
1288
- test('behavior: returns true for valid keychain with p256 inner', () => {
1289
- expect(SignatureEnvelope.validate(signature_keychain_p256)).toBe(true)
1290
- })
1291
-
1292
- test('behavior: returns true for valid keychain with webAuthn inner', () => {
1293
- expect(SignatureEnvelope.validate(signature_keychain_webauthn)).toBe(true)
1294
- })
1295
-
1296
- test('behavior: returns false for invalid keychain signature', () => {
1297
- expect(
1298
- SignatureEnvelope.validate({
1299
- userAddress: '0x1234567890123456789012345678901234567890',
1300
- inner: {
1301
- signature: {
1302
- r: 0n,
1303
- s: 0n,
1304
- yParity: 2,
1305
- },
1306
- type: 'secp256k1',
1307
- },
1308
- type: 'keychain',
1309
- } as any),
1310
- ).toBe(false)
1311
- })
1312
- })
1313
-
1314
- test('behavior: returns false for invalid envelope', () => {
1315
- expect(SignatureEnvelope.validate({} as any)).toBe(false)
1316
- })
1317
-
1318
- test('behavior: returns false for incomplete signature', () => {
1319
- expect(
1320
- SignatureEnvelope.validate({
1321
- r: 0n,
1322
- s: 0n,
1323
- } as any),
1324
- ).toBe(false)
1325
- })
1326
- })
1327
-
1328
- describe('fromRpc', () => {
1329
- describe('secp256k1', () => {
1330
- test('behavior: converts RPC secp256k1 signature', () => {
1331
- const rpc: SignatureEnvelope.Secp256k1Rpc = {
1332
- r: Signature.toRpc(signature_secp256k1).r,
1333
- s: Signature.toRpc(signature_secp256k1).s,
1334
- yParity: Signature.toRpc(signature_secp256k1).yParity,
1335
- type: 'secp256k1',
1336
- }
1337
-
1338
- const envelope = SignatureEnvelope.fromRpc(rpc)
1339
-
1340
- expect(envelope).toMatchObject({
1341
- signature: {
1342
- r: signature_secp256k1.r,
1343
- s: signature_secp256k1.s,
1344
- yParity: signature_secp256k1.yParity,
1345
- },
1346
- type: 'secp256k1',
1347
- })
1348
- })
1349
- })
1350
-
1351
- describe('p256', () => {
1352
- test('behavior: converts RPC P256 signature', () => {
1353
- const rpc: SignatureEnvelope.P256Rpc = {
1354
- prehash: true,
1355
- pubKeyX: Hex.fromNumber(publicKey.x, { size: 32 }),
1356
- pubKeyY: Hex.fromNumber(publicKey.y, { size: 32 }),
1357
- r: Hex.fromNumber(p256Signature.r, { size: 32 }),
1358
- s: Hex.fromNumber(p256Signature.s, { size: 32 }),
1359
- type: 'p256',
1360
- }
1361
-
1362
- const envelope = SignatureEnvelope.fromRpc(rpc)
1363
-
1364
- expect(envelope).toMatchObject({
1365
- prehash: true,
1366
- publicKey: {
1367
- x: publicKey.x,
1368
- y: publicKey.y,
1369
- },
1370
- signature: {
1371
- r: p256Signature.r,
1372
- s: p256Signature.s,
1373
- },
1374
- type: 'p256',
1375
- })
1376
- })
1377
- })
1378
-
1379
- describe('webAuthn', () => {
1380
- test('behavior: converts RPC WebAuthn signature', () => {
1381
- const webauthnData = WebAuthnP256.getAuthenticatorData({
1382
- rpId: 'localhost',
1383
- })
1384
- const clientDataJSON = WebAuthnP256.getClientDataJSON({
1385
- challenge: '0xdeadbeef',
1386
- origin: 'http://localhost',
1387
- })
1388
-
1389
- const rpc: SignatureEnvelope.WebAuthnRpc = {
1390
- pubKeyX: Hex.fromNumber(publicKey.x, { size: 32 }),
1391
- pubKeyY: Hex.fromNumber(publicKey.y, { size: 32 }),
1392
- r: Hex.fromNumber(p256Signature.r, { size: 32 }),
1393
- s: Hex.fromNumber(p256Signature.s, { size: 32 }),
1394
- type: 'webAuthn',
1395
- webauthnData: Hex.concat(webauthnData, Hex.fromString(clientDataJSON)),
1396
- }
1397
-
1398
- const envelope = SignatureEnvelope.fromRpc(rpc)
1399
-
1400
- expect(envelope).toMatchObject({
1401
- metadata: {
1402
- authenticatorData: webauthnData,
1403
- clientDataJSON,
1404
- },
1405
- publicKey: {
1406
- x: publicKey.x,
1407
- y: publicKey.y,
1408
- },
1409
- signature: {
1410
- r: p256Signature.r,
1411
- s: p256Signature.s,
1412
- },
1413
- type: 'webAuthn',
1414
- })
1415
- })
1416
- })
1417
-
1418
- describe('keychain', () => {
1419
- test('behavior: converts RPC keychain signature with secp256k1 inner', () => {
1420
- const rpc: SignatureEnvelope.KeychainRpc = {
1421
- type: 'keychain',
1422
- userAddress: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
1423
- signature: {
1424
- type: 'secp256k1',
1425
- r: '0xa2bb71146c20ce932456c043ebb2973ed205e07cd32c35a60bdefca1285fd132',
1426
- s: '0x7cba10692bccdbfba9a215418443c2903dbee6fe5cb55c91172e47efc607840e',
1427
- yParity: '0x1',
1428
- },
1429
- }
1430
-
1431
- const envelope = SignatureEnvelope.fromRpc(rpc)
1432
-
1433
- expect(envelope).toMatchObject({
1434
- type: 'keychain',
1435
- userAddress: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
1436
- inner: {
1437
- type: 'secp256k1',
1438
- signature: {
1439
- r: 0xa2bb71146c20ce932456c043ebb2973ed205e07cd32c35a60bdefca1285fd132n,
1440
- s: 0x7cba10692bccdbfba9a215418443c2903dbee6fe5cb55c91172e47efc607840en,
1441
- yParity: 1,
1442
- },
1443
- },
1444
- })
1445
- })
1446
-
1447
- test('behavior: converts RPC keychain signature with p256 inner', () => {
1448
- const rpc: SignatureEnvelope.KeychainRpc = {
1449
- type: 'keychain',
1450
- userAddress: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd',
1451
- signature: {
1452
- prehash: true,
1453
- pubKeyX: Hex.fromNumber(publicKey.x, { size: 32 }),
1454
- pubKeyY: Hex.fromNumber(publicKey.y, { size: 32 }),
1455
- r: Hex.fromNumber(p256Signature.r, { size: 32 }),
1456
- s: Hex.fromNumber(p256Signature.s, { size: 32 }),
1457
- type: 'p256',
1458
- },
1459
- }
1460
-
1461
- const envelope = SignatureEnvelope.fromRpc(rpc)
1462
-
1463
- expect(envelope).toMatchObject({
1464
- type: 'keychain',
1465
- userAddress: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd',
1466
- inner: {
1467
- type: 'p256',
1468
- prehash: true,
1469
- publicKey: {
1470
- x: publicKey.x,
1471
- y: publicKey.y,
1472
- },
1473
- signature: {
1474
- r: p256Signature.r,
1475
- s: p256Signature.s,
1476
- },
1477
- },
1478
- })
1479
- })
1480
-
1481
- test('behavior: converts RPC keychain signature with webAuthn inner', () => {
1482
- const webauthnData = WebAuthnP256.getAuthenticatorData({
1483
- rpId: 'localhost',
1484
- })
1485
- const clientDataJSON = WebAuthnP256.getClientDataJSON({
1486
- challenge: '0xdeadbeef',
1487
- origin: 'http://localhost',
1488
- })
1489
-
1490
- const rpc: SignatureEnvelope.KeychainRpc = {
1491
- type: 'keychain',
1492
- userAddress: '0xfedcbafedcbafedcbafedcbafedcbafedcbafedc',
1493
- signature: {
1494
- pubKeyX: Hex.fromNumber(publicKey.x, { size: 32 }),
1495
- pubKeyY: Hex.fromNumber(publicKey.y, { size: 32 }),
1496
- r: Hex.fromNumber(p256Signature.r, { size: 32 }),
1497
- s: Hex.fromNumber(p256Signature.s, { size: 32 }),
1498
- type: 'webAuthn',
1499
- webauthnData: Hex.concat(
1500
- webauthnData,
1501
- Hex.fromString(clientDataJSON),
1502
- ),
1503
- },
1504
- }
1505
-
1506
- const envelope = SignatureEnvelope.fromRpc(rpc)
1507
-
1508
- expect(envelope).toMatchObject({
1509
- type: 'keychain',
1510
- userAddress: '0xfedcbafedcbafedcbafedcbafedcbafedcbafedc',
1511
- inner: {
1512
- type: 'webAuthn',
1513
- metadata: {
1514
- authenticatorData: webauthnData,
1515
- clientDataJSON,
1516
- },
1517
- publicKey: {
1518
- x: publicKey.x,
1519
- y: publicKey.y,
1520
- },
1521
- signature: {
1522
- r: p256Signature.r,
1523
- s: p256Signature.s,
1524
- },
1525
- },
1526
- })
1527
- })
1528
- })
1529
- })
1530
-
1531
- describe('toRpc', () => {
1532
- describe('secp256k1', () => {
1533
- test('behavior: converts secp256k1 signature to RPC', () => {
1534
- const envelope: SignatureEnvelope.Secp256k1 = {
1535
- signature: signature_secp256k1,
1536
- type: 'secp256k1',
1537
- }
1538
-
1539
- const rpc = SignatureEnvelope.toRpc(envelope)
1540
-
1541
- expect(rpc).toMatchObject({
1542
- r: Signature.toRpc(signature_secp256k1).r,
1543
- s: Signature.toRpc(signature_secp256k1).s,
1544
- yParity: Signature.toRpc(signature_secp256k1).yParity,
1545
- type: 'secp256k1',
1546
- })
1547
- })
1548
- })
1549
-
1550
- describe('p256', () => {
1551
- test('behavior: converts P256 signature to RPC', () => {
1552
- const rpc = SignatureEnvelope.toRpc(signature_p256)
1553
-
1554
- expect(rpc.type).toBe('p256')
1555
- expect(rpc.prehash).toBe(true)
1556
- expect(typeof rpc.pubKeyX).toBe('string')
1557
- expect(typeof rpc.pubKeyY).toBe('string')
1558
- expect(typeof rpc.r).toBe('string')
1559
- expect(typeof rpc.s).toBe('string')
1560
- })
1561
-
1562
- test('behavior: converts prehash=false correctly', () => {
1563
- const withPrehashFalse = { ...signature_p256, prehash: false }
1564
- const rpc = SignatureEnvelope.toRpc(withPrehashFalse)
1565
-
1566
- expect(rpc.prehash).toBe(false)
1567
- })
1568
- })
1569
-
1570
- describe('webAuthn', () => {
1571
- test('behavior: converts WebAuthn signature to RPC', () => {
1572
- const rpc = SignatureEnvelope.toRpc(
1573
- signature_webauthn,
1574
- ) as SignatureEnvelope.WebAuthnRpc
1575
-
1576
- expect(rpc.type).toBe('webAuthn')
1577
- expect(typeof rpc.pubKeyX).toBe('string')
1578
- expect(typeof rpc.pubKeyY).toBe('string')
1579
- expect(typeof rpc.r).toBe('string')
1580
- expect(typeof rpc.s).toBe('string')
1581
- expect(typeof rpc.webauthnData).toBe('string')
1582
- expect(rpc.webauthnData.startsWith('0x')).toBe(true)
1583
- })
1584
-
1585
- test('behavior: webauthnData contains authenticatorData and clientDataJSON', () => {
1586
- const rpc = SignatureEnvelope.toRpc(
1587
- signature_webauthn,
1588
- ) as SignatureEnvelope.WebAuthnRpc
1589
-
1590
- // webauthnData should contain the concatenation of authenticatorData and clientDataJSON (as hex)
1591
- expect(rpc.webauthnData).toContain(
1592
- signature_webauthn.metadata.authenticatorData.slice(2),
1593
- )
1594
- })
1595
- })
1596
-
1597
- describe('keychain', () => {
1598
- test('behavior: converts keychain signature with secp256k1 inner to RPC', () => {
1599
- const envelope: SignatureEnvelope.Keychain = {
1600
- type: 'keychain',
1601
- userAddress: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
1602
- inner: {
1603
- signature: signature_secp256k1,
1604
- type: 'secp256k1',
1605
- },
1606
- }
1607
-
1608
- const rpc = SignatureEnvelope.toRpc(
1609
- envelope,
1610
- ) as SignatureEnvelope.KeychainRpc
1611
-
1612
- expect(rpc.type).toBe('keychain')
1613
- expect(rpc.userAddress).toBe('0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266')
1614
- expect(rpc.signature).toMatchObject({
1615
- r: Signature.toRpc(signature_secp256k1).r,
1616
- s: Signature.toRpc(signature_secp256k1).s,
1617
- yParity: Signature.toRpc(signature_secp256k1).yParity,
1618
- type: 'secp256k1',
1619
- })
1620
- })
1621
-
1622
- test('behavior: converts keychain signature with p256 inner to RPC', () => {
1623
- const rpc = SignatureEnvelope.toRpc(
1624
- signature_keychain_p256,
1625
- ) as SignatureEnvelope.KeychainRpc
1626
-
1627
- expect(rpc.type).toBe('keychain')
1628
- expect(rpc.userAddress).toBe(signature_keychain_p256.userAddress)
1629
- expect(rpc.signature.type).toBe('p256')
1630
- expect(typeof rpc.signature.pubKeyX).toBe('string')
1631
- expect(typeof rpc.signature.pubKeyY).toBe('string')
1632
- })
1633
-
1634
- test('behavior: converts keychain signature with webAuthn inner to RPC', () => {
1635
- const rpc = SignatureEnvelope.toRpc(
1636
- signature_keychain_webauthn,
1637
- ) as SignatureEnvelope.KeychainRpc
1638
-
1639
- expect(rpc.type).toBe('keychain')
1640
- expect(rpc.userAddress).toBe(signature_keychain_webauthn.userAddress)
1641
- expect(rpc.signature.type).toBe('webAuthn')
1642
- expect(typeof rpc.signature.pubKeyX).toBe('string')
1643
- expect(typeof rpc.signature.webauthnData).toBe('string')
1644
- })
1645
- })
1646
- })
1647
-
1648
- describe('roundtrip: toRpc <-> fromRpc', () => {
1649
- describe('secp256k1', () => {
1650
- test('behavior: roundtrips toRpc -> fromRpc', () => {
1651
- const envelope: SignatureEnvelope.Secp256k1 = {
1652
- signature: signature_secp256k1,
1653
- type: 'secp256k1',
1654
- }
1655
-
1656
- const rpc = SignatureEnvelope.toRpc(envelope)
1657
- const roundtripped = SignatureEnvelope.fromRpc(rpc)
1658
-
1659
- expect(roundtripped).toMatchObject({
1660
- signature: {
1661
- r: signature_secp256k1.r,
1662
- s: signature_secp256k1.s,
1663
- yParity: signature_secp256k1.yParity,
1664
- },
1665
- type: 'secp256k1',
1666
- })
1667
- })
1668
-
1669
- test('behavior: roundtrips fromRpc -> toRpc', () => {
1670
- const rpc: SignatureEnvelope.Secp256k1Rpc = {
1671
- r: Signature.toRpc(signature_secp256k1).r,
1672
- s: Signature.toRpc(signature_secp256k1).s,
1673
- yParity: Signature.toRpc(signature_secp256k1).yParity,
1674
- type: 'secp256k1',
1675
- }
1676
-
1677
- const envelope = SignatureEnvelope.fromRpc(rpc)
1678
- const roundtripped = SignatureEnvelope.toRpc(envelope)
1679
-
1680
- expect(roundtripped).toMatchObject(rpc)
1681
- })
1682
- })
1683
-
1684
- describe('p256', () => {
1685
- test('behavior: roundtrips toRpc -> fromRpc', () => {
1686
- const rpc = SignatureEnvelope.toRpc(signature_p256)
1687
- const roundtripped = SignatureEnvelope.fromRpc(rpc)
1688
-
1689
- expect(roundtripped).toMatchObject({
1690
- prehash: signature_p256.prehash,
1691
- publicKey: {
1692
- x: signature_p256.publicKey.x,
1693
- y: signature_p256.publicKey.y,
1694
- },
1695
- signature: {
1696
- r: signature_p256.signature.r,
1697
- s: signature_p256.signature.s,
1698
- },
1699
- type: 'p256',
1700
- })
1701
- })
1702
-
1703
- test('behavior: handles prehash=false in roundtrip', () => {
1704
- const withPrehashFalse = { ...signature_p256, prehash: false }
1705
- const rpc = SignatureEnvelope.toRpc(withPrehashFalse)
1706
- const roundtripped = SignatureEnvelope.fromRpc(rpc)
1707
-
1708
- expect(roundtripped.prehash).toBe(false)
1709
- })
1710
- })
1711
-
1712
- describe('webAuthn', () => {
1713
- test('behavior: roundtrips toRpc -> fromRpc', () => {
1714
- const rpc = SignatureEnvelope.toRpc(signature_webauthn)
1715
- const roundtripped = SignatureEnvelope.fromRpc(rpc)
1716
-
1717
- expect(roundtripped).toMatchObject({
1718
- metadata: {
1719
- authenticatorData: signature_webauthn.metadata.authenticatorData,
1720
- clientDataJSON: signature_webauthn.metadata.clientDataJSON,
1721
- },
1722
- publicKey: {
1723
- x: signature_webauthn.publicKey.x,
1724
- y: signature_webauthn.publicKey.y,
1725
- },
1726
- signature: {
1727
- r: signature_webauthn.signature.r,
1728
- s: signature_webauthn.signature.s,
1729
- },
1730
- type: 'webAuthn',
1731
- })
1732
- })
1733
-
1734
- test('behavior: handles variable-length clientDataJSON in roundtrip', () => {
1735
- const longClientData = JSON.stringify({
1736
- type: 'webAuthn.get',
1737
- challenge: 'a'.repeat(100),
1738
- origin: 'https://example.com',
1739
- crossOrigin: false,
1740
- })
1741
-
1742
- const signatureWithLongData = {
1743
- ...signature_webauthn,
1744
- metadata: {
1745
- ...signature_webauthn.metadata,
1746
- clientDataJSON: longClientData,
1747
- },
1748
- }
1749
-
1750
- const rpc = SignatureEnvelope.toRpc(signatureWithLongData)
1751
- const roundtripped = SignatureEnvelope.fromRpc(rpc)
1752
-
1753
- expect(roundtripped.metadata?.clientDataJSON).toBe(longClientData)
1754
- })
1755
- })
1756
-
1757
- describe('keychain', () => {
1758
- test('behavior: roundtrips toRpc -> fromRpc with secp256k1 inner', () => {
1759
- const envelope: SignatureEnvelope.Keychain = {
1760
- type: 'keychain',
1761
- userAddress: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
1762
- inner: {
1763
- signature: signature_secp256k1,
1764
- type: 'secp256k1',
1765
- },
1766
- }
1767
-
1768
- const rpc = SignatureEnvelope.toRpc(envelope)
1769
- const roundtripped = SignatureEnvelope.fromRpc(rpc)
1770
-
1771
- expect(roundtripped).toMatchObject({
1772
- type: 'keychain',
1773
- userAddress: envelope.userAddress,
1774
- inner: {
1775
- type: 'secp256k1',
1776
- signature: {
1777
- r: signature_secp256k1.r,
1778
- s: signature_secp256k1.s,
1779
- yParity: signature_secp256k1.yParity,
1780
- },
1781
- },
1782
- })
1783
- })
1784
-
1785
- test('behavior: roundtrips toRpc -> fromRpc with p256 inner', () => {
1786
- const rpc = SignatureEnvelope.toRpc(signature_keychain_p256)
1787
- const roundtripped = SignatureEnvelope.fromRpc(rpc)
1788
-
1789
- expect(roundtripped).toMatchObject({
1790
- type: 'keychain',
1791
- userAddress: signature_keychain_p256.userAddress,
1792
- inner: {
1793
- type: 'p256',
1794
- prehash: signature_p256.prehash,
1795
- publicKey: {
1796
- x: signature_p256.publicKey.x,
1797
- y: signature_p256.publicKey.y,
1798
- },
1799
- signature: {
1800
- r: signature_p256.signature.r,
1801
- s: signature_p256.signature.s,
1802
- },
1803
- },
1804
- })
1805
- })
1806
-
1807
- test('behavior: roundtrips toRpc -> fromRpc with webAuthn inner', () => {
1808
- const rpc = SignatureEnvelope.toRpc(signature_keychain_webauthn)
1809
- const roundtripped = SignatureEnvelope.fromRpc(rpc)
1810
-
1811
- expect(roundtripped).toMatchObject({
1812
- type: 'keychain',
1813
- userAddress: signature_keychain_webauthn.userAddress,
1814
- inner: {
1815
- type: 'webAuthn',
1816
- metadata: {
1817
- authenticatorData: signature_webauthn.metadata.authenticatorData,
1818
- clientDataJSON: signature_webauthn.metadata.clientDataJSON,
1819
- },
1820
- publicKey: {
1821
- x: signature_webauthn.publicKey.x,
1822
- y: signature_webauthn.publicKey.y,
1823
- },
1824
- signature: {
1825
- r: signature_webauthn.signature.r,
1826
- s: signature_webauthn.signature.s,
1827
- },
1828
- },
1829
- })
1830
- })
1831
-
1832
- test('behavior: roundtrips fromRpc -> toRpc with secp256k1 inner', () => {
1833
- const rpc: SignatureEnvelope.KeychainRpc = {
1834
- type: 'keychain',
1835
- userAddress: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
1836
- signature: {
1837
- type: 'secp256k1',
1838
- r: '0xa2bb71146c20ce932456c043ebb2973ed205e07cd32c35a60bdefca1285fd132',
1839
- s: '0x7cba10692bccdbfba9a215418443c2903dbee6fe5cb55c91172e47efc607840e',
1840
- yParity: '0x1',
1841
- },
1842
- }
1843
-
1844
- const envelope = SignatureEnvelope.fromRpc(rpc)
1845
- const roundtripped = SignatureEnvelope.toRpc(envelope)
1846
-
1847
- expect(roundtripped).toMatchObject(rpc)
1848
- })
1849
- })
1850
- })
1851
-
1852
- describe('types', () => {
1853
- test('behavior: contains all signature types', () => {
1854
- expect(SignatureEnvelope.types).toEqual(['secp256k1', 'p256', 'webAuthn'])
1855
- })
1856
- })
1857
-
1858
- describe('CoercionError', () => {
1859
- test('behavior: formats error message with hex string', () => {
1860
- const error = new SignatureEnvelope.CoercionError({
1861
- envelope: '0xdeadbeef',
1862
- })
1863
- expect(error).toMatchInlineSnapshot(
1864
- `[SignatureEnvelope.CoercionError: Unable to coerce value (\`"0xdeadbeef"\`) to a valid signature envelope.]`,
1865
- )
1866
- })
1867
-
1868
- test('behavior: formats error message with object', () => {
1869
- const error = new SignatureEnvelope.CoercionError({
1870
- envelope: { r: 0n, s: 0n, yParity: 0 },
1871
- })
1872
- expect(error).toMatchInlineSnapshot(
1873
- `[SignatureEnvelope.CoercionError: Unable to coerce value (\`{"r":"0#__bigint","s":"0#__bigint","yParity":0}\`) to a valid signature envelope.]`,
1874
- )
1875
- })
1876
- })