ox 1.7.4 → 1.8.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 (237) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/core/AbiEvent.d.ts.map +1 -1
  3. package/dist/core/AbiEvent.js +10 -9
  4. package/dist/core/AbiEvent.js.map +1 -1
  5. package/dist/core/Blobs.d.ts.map +1 -1
  6. package/dist/core/Blobs.js +6 -2
  7. package/dist/core/Blobs.js.map +1 -1
  8. package/dist/core/Frame.d.ts +290 -0
  9. package/dist/core/Frame.d.ts.map +1 -0
  10. package/dist/core/Frame.js +306 -0
  11. package/dist/core/Frame.js.map +1 -0
  12. package/dist/core/FrameReceipt.d.ts +92 -0
  13. package/dist/core/FrameReceipt.d.ts.map +1 -0
  14. package/dist/core/FrameReceipt.js +72 -0
  15. package/dist/core/FrameReceipt.js.map +1 -0
  16. package/dist/core/FrameSignature.d.ts +357 -0
  17. package/dist/core/FrameSignature.d.ts.map +1 -0
  18. package/dist/core/FrameSignature.js +411 -0
  19. package/dist/core/FrameSignature.js.map +1 -0
  20. package/dist/core/Signature.d.ts +6 -5
  21. package/dist/core/Signature.d.ts.map +1 -1
  22. package/dist/core/Signature.js +17 -7
  23. package/dist/core/Signature.js.map +1 -1
  24. package/dist/core/Siwe.d.ts.map +1 -1
  25. package/dist/core/Siwe.js +21 -1
  26. package/dist/core/Siwe.js.map +1 -1
  27. package/dist/core/Transaction.d.ts +29 -4
  28. package/dist/core/Transaction.d.ts.map +1 -1
  29. package/dist/core/Transaction.js +25 -4
  30. package/dist/core/Transaction.js.map +1 -1
  31. package/dist/core/TransactionReceipt.d.ts +13 -4
  32. package/dist/core/TransactionReceipt.d.ts.map +1 -1
  33. package/dist/core/TransactionReceipt.js +14 -0
  34. package/dist/core/TransactionReceipt.js.map +1 -1
  35. package/dist/core/TransactionRequest.d.ts +15 -7
  36. package/dist/core/TransactionRequest.d.ts.map +1 -1
  37. package/dist/core/TransactionRequest.js +47 -1
  38. package/dist/core/TransactionRequest.js.map +1 -1
  39. package/dist/core/TxEnvelope.d.ts +35 -21
  40. package/dist/core/TxEnvelope.d.ts.map +1 -1
  41. package/dist/core/TxEnvelope.js +46 -6
  42. package/dist/core/TxEnvelope.js.map +1 -1
  43. package/dist/core/TxEnvelopeEip8141.d.ts +341 -0
  44. package/dist/core/TxEnvelopeEip8141.d.ts.map +1 -0
  45. package/dist/core/TxEnvelopeEip8141.js +485 -0
  46. package/dist/core/TxEnvelopeEip8141.js.map +1 -0
  47. package/dist/core/internal/curves.d.ts +2 -0
  48. package/dist/core/internal/curves.d.ts.map +1 -1
  49. package/dist/core/internal/curves.js +2 -0
  50. package/dist/core/internal/curves.js.map +1 -1
  51. package/dist/index.d.ts +73 -0
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +73 -0
  54. package/dist/index.js.map +1 -1
  55. package/dist/tempo/KeyAuthorization.d.ts +42 -38
  56. package/dist/tempo/KeyAuthorization.d.ts.map +1 -1
  57. package/dist/tempo/KeyAuthorization.js +24 -4
  58. package/dist/tempo/KeyAuthorization.js.map +1 -1
  59. package/dist/tempo/MultisigConfig.d.ts +18 -15
  60. package/dist/tempo/MultisigConfig.d.ts.map +1 -1
  61. package/dist/tempo/MultisigConfig.js +49 -20
  62. package/dist/tempo/MultisigConfig.js.map +1 -1
  63. package/dist/tempo/MultisigOperation.d.ts +3 -2
  64. package/dist/tempo/MultisigOperation.d.ts.map +1 -1
  65. package/dist/tempo/MultisigOperation.js +20 -100
  66. package/dist/tempo/MultisigOperation.js.map +1 -1
  67. package/dist/tempo/MultisigSimulation.d.ts +2 -49
  68. package/dist/tempo/MultisigSimulation.d.ts.map +1 -1
  69. package/dist/tempo/MultisigSimulation.js +18 -46
  70. package/dist/tempo/MultisigSimulation.js.map +1 -1
  71. package/dist/tempo/SignatureEnvelope.d.ts +72 -140
  72. package/dist/tempo/SignatureEnvelope.d.ts.map +1 -1
  73. package/dist/tempo/SignatureEnvelope.js +130 -181
  74. package/dist/tempo/SignatureEnvelope.js.map +1 -1
  75. package/dist/tempo/Transaction.d.ts +2 -0
  76. package/dist/tempo/Transaction.d.ts.map +1 -1
  77. package/dist/tempo/TransactionReceipt.d.ts +2 -0
  78. package/dist/tempo/TransactionReceipt.d.ts.map +1 -1
  79. package/dist/tempo/TransactionRequest.d.ts +4 -2
  80. package/dist/tempo/TransactionRequest.d.ts.map +1 -1
  81. package/dist/tempo/TransactionRequest.js +6 -1
  82. package/dist/tempo/TransactionRequest.js.map +1 -1
  83. package/dist/tempo/index.d.ts +3 -1
  84. package/dist/tempo/index.d.ts.map +1 -1
  85. package/dist/tempo/index.js +3 -1
  86. package/dist/tempo/index.js.map +1 -1
  87. package/dist/zod/Block.d.ts +123 -0
  88. package/dist/zod/Block.d.ts.map +1 -1
  89. package/dist/zod/Frame.d.ts +73 -0
  90. package/dist/zod/Frame.d.ts.map +1 -0
  91. package/dist/zod/Frame.js +74 -0
  92. package/dist/zod/Frame.js.map +1 -0
  93. package/dist/zod/FrameReceipt.d.ts +50 -0
  94. package/dist/zod/FrameReceipt.d.ts.map +1 -0
  95. package/dist/zod/FrameReceipt.js +37 -0
  96. package/dist/zod/FrameReceipt.js.map +1 -0
  97. package/dist/zod/FrameSignature.d.ts +63 -0
  98. package/dist/zod/FrameSignature.d.ts.map +1 -0
  99. package/dist/zod/FrameSignature.js +66 -0
  100. package/dist/zod/FrameSignature.js.map +1 -0
  101. package/dist/zod/RpcSchema.d.ts +806 -11
  102. package/dist/zod/RpcSchema.d.ts.map +1 -1
  103. package/dist/zod/Transaction.d.ts +252 -0
  104. package/dist/zod/Transaction.d.ts.map +1 -1
  105. package/dist/zod/Transaction.js +54 -0
  106. package/dist/zod/Transaction.js.map +1 -1
  107. package/dist/zod/TransactionEnvelope.d.ts +233 -4
  108. package/dist/zod/TransactionEnvelope.d.ts.map +1 -1
  109. package/dist/zod/TransactionEnvelope.js +9 -0
  110. package/dist/zod/TransactionEnvelope.js.map +1 -1
  111. package/dist/zod/TransactionReceipt.d.ts +48 -0
  112. package/dist/zod/TransactionReceipt.d.ts.map +1 -1
  113. package/dist/zod/TransactionReceipt.js +8 -3
  114. package/dist/zod/TransactionReceipt.js.map +1 -1
  115. package/dist/zod/TransactionRequest.d.ts +50 -2
  116. package/dist/zod/TransactionRequest.d.ts.map +1 -1
  117. package/dist/zod/TransactionRequest.js +10 -4
  118. package/dist/zod/TransactionRequest.js.map +1 -1
  119. package/dist/zod/TxEnvelopeEip8141.d.ts +166 -0
  120. package/dist/zod/TxEnvelopeEip8141.d.ts.map +1 -0
  121. package/dist/zod/TxEnvelopeEip8141.js +76 -0
  122. package/dist/zod/TxEnvelopeEip8141.js.map +1 -0
  123. package/dist/zod/internal/Frame.d.ts +4 -0
  124. package/dist/zod/internal/Frame.d.ts.map +1 -0
  125. package/dist/zod/internal/Frame.js +17 -0
  126. package/dist/zod/internal/Frame.js.map +1 -0
  127. package/dist/zod/internal/rpcSchemas/Eth.d.ts +842 -11
  128. package/dist/zod/internal/rpcSchemas/Eth.d.ts.map +1 -1
  129. package/dist/zod/internal/rpcSchemas/Wallet.d.ts +56 -2
  130. package/dist/zod/internal/rpcSchemas/Wallet.d.ts.map +1 -1
  131. package/dist/zod/tempo/AuthorizationTempo.d.ts +350 -10
  132. package/dist/zod/tempo/AuthorizationTempo.d.ts.map +1 -1
  133. package/dist/zod/tempo/KeyAuthorization.d.ts +242 -422
  134. package/dist/zod/tempo/KeyAuthorization.d.ts.map +1 -1
  135. package/dist/zod/tempo/KeyAuthorization.js +29 -31
  136. package/dist/zod/tempo/KeyAuthorization.js.map +1 -1
  137. package/dist/zod/tempo/MultisigSimulation.d.ts +8 -76
  138. package/dist/zod/tempo/MultisigSimulation.d.ts.map +1 -1
  139. package/dist/zod/tempo/MultisigSimulation.js +3 -37
  140. package/dist/zod/tempo/MultisigSimulation.js.map +1 -1
  141. package/dist/zod/tempo/RpcSchemaTempo.d.ts +424 -228
  142. package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
  143. package/dist/zod/tempo/SignatureEnvelope.d.ts +105 -3
  144. package/dist/zod/tempo/SignatureEnvelope.d.ts.map +1 -1
  145. package/dist/zod/tempo/SignatureEnvelope.js +21 -9
  146. package/dist/zod/tempo/SignatureEnvelope.js.map +1 -1
  147. package/dist/zod/tempo/Transaction.d.ts +783 -432
  148. package/dist/zod/tempo/Transaction.d.ts.map +1 -1
  149. package/dist/zod/tempo/TransactionRequest.d.ts +590 -473
  150. package/dist/zod/tempo/TransactionRequest.d.ts.map +1 -1
  151. package/dist/zod/tempo/TransactionRequest.js +8 -0
  152. package/dist/zod/tempo/TransactionRequest.js.map +1 -1
  153. package/dist/zod/tempo/TxEnvelopeTempo.d.ts +327 -213
  154. package/dist/zod/tempo/TxEnvelopeTempo.d.ts.map +1 -1
  155. package/dist/zod/tempo/ZoneRpcAuthentication.d.ts +105 -3
  156. package/dist/zod/tempo/ZoneRpcAuthentication.d.ts.map +1 -1
  157. package/dist/zod/z.d.ts +4 -0
  158. package/dist/zod/z.d.ts.map +1 -1
  159. package/dist/zod/z.js +4 -0
  160. package/dist/zod/z.js.map +1 -1
  161. package/package.json +41 -1
  162. package/src/core/AbiEvent.ts +11 -9
  163. package/src/core/Blobs.ts +6 -2
  164. package/src/core/Frame.ts +443 -0
  165. package/src/core/FrameReceipt.ts +117 -0
  166. package/src/core/FrameSignature.ts +593 -0
  167. package/src/core/Signature.ts +41 -11
  168. package/src/core/Siwe.ts +20 -1
  169. package/src/core/Transaction.ts +82 -5
  170. package/src/core/TransactionReceipt.ts +41 -3
  171. package/src/core/TransactionRequest.ts +71 -4
  172. package/src/core/TxEnvelope.ts +159 -90
  173. package/src/core/TxEnvelopeEip8141.ts +715 -0
  174. package/src/core/_test/AbiEvent.test.ts +45 -0
  175. package/src/core/_test/Blobs.test.ts +22 -0
  176. package/src/core/_test/Frame.test-d.ts +96 -0
  177. package/src/core/_test/Frame.test.ts +267 -0
  178. package/src/core/_test/FrameReceipt.test.ts +86 -0
  179. package/src/core/_test/FrameSignature.test-d.ts +93 -0
  180. package/src/core/_test/FrameSignature.test.ts +554 -0
  181. package/src/core/_test/Signature.test.ts +166 -0
  182. package/src/core/_test/Siwe.test.ts +100 -1
  183. package/src/core/_test/Transaction.test.ts +33 -0
  184. package/src/core/_test/TransactionEnvelope.test-d.ts +74 -0
  185. package/src/core/_test/TransactionEnvelope.test.ts +107 -0
  186. package/src/core/_test/TransactionReceipt.test.ts +59 -0
  187. package/src/core/_test/TransactionRequest.test-d.ts +50 -0
  188. package/src/core/_test/TransactionRequest.test.ts +88 -1
  189. package/src/core/_test/TxEnvelopeEip8141.test-d.ts +44 -0
  190. package/src/core/_test/TxEnvelopeEip8141.test.ts +1775 -0
  191. package/src/core/_test/curves.test.ts +6 -1
  192. package/src/core/_test/index.test.ts +4 -0
  193. package/src/core/internal/curves.ts +4 -0
  194. package/src/index.ts +77 -0
  195. package/src/tempo/AuthorizationTempo.test.ts +8 -4
  196. package/src/tempo/KeyAuthorization.test-d.ts +16 -63
  197. package/src/tempo/KeyAuthorization.test.ts +69 -11
  198. package/src/tempo/KeyAuthorization.ts +107 -63
  199. package/src/tempo/MultisigConfig.test.ts +44 -20
  200. package/src/tempo/MultisigConfig.ts +65 -23
  201. package/src/tempo/MultisigOperation.test.ts +79 -550
  202. package/src/tempo/MultisigOperation.ts +31 -146
  203. package/src/tempo/MultisigSimulation.test-d.ts +2 -2
  204. package/src/tempo/MultisigSimulation.test.ts +21 -110
  205. package/src/tempo/MultisigSimulation.ts +25 -99
  206. package/src/tempo/SignatureEnvelope.test-d.ts +29 -47
  207. package/src/tempo/SignatureEnvelope.test.ts +328 -541
  208. package/src/tempo/SignatureEnvelope.ts +196 -291
  209. package/src/tempo/Transaction.test.ts +4 -4
  210. package/src/tempo/TransactionRequest.test.ts +17 -80
  211. package/src/tempo/TransactionRequest.ts +19 -2
  212. package/src/tempo/index.ts +3 -1
  213. package/src/tempo/multisig.e2e.test.ts +321 -860
  214. package/src/version.ts +1 -1
  215. package/src/zod/Frame.ts +82 -0
  216. package/src/zod/FrameReceipt.ts +48 -0
  217. package/src/zod/FrameSignature.ts +78 -0
  218. package/src/zod/Transaction.ts +63 -0
  219. package/src/zod/TransactionEnvelope.ts +33 -0
  220. package/src/zod/TransactionReceipt.ts +14 -3
  221. package/src/zod/TransactionRequest.ts +18 -3
  222. package/src/zod/TxEnvelopeEip8141.ts +102 -0
  223. package/src/zod/_test/Frame.test-d.ts +41 -0
  224. package/src/zod/_test/Frame.test.ts +54 -0
  225. package/src/zod/_test/FrameReceipt.test.ts +75 -0
  226. package/src/zod/_test/FrameSignature.test.ts +94 -0
  227. package/src/zod/_test/TxEnvelopeEip8141.test.ts +116 -0
  228. package/src/zod/internal/Frame.ts +26 -0
  229. package/src/zod/tempo/KeyAuthorization.ts +50 -39
  230. package/src/zod/tempo/MultisigSimulation.ts +3 -48
  231. package/src/zod/tempo/SignatureEnvelope.ts +46 -35
  232. package/src/zod/tempo/TransactionRequest.ts +14 -0
  233. package/src/zod/tempo/_test/KeyAuthorization.test.ts +69 -20
  234. package/src/zod/tempo/_test/MultisigSimulation.test.ts +6 -31
  235. package/src/zod/tempo/_test/SignatureEnvelope.test.ts +118 -85
  236. package/src/zod/tempo/_test/TransactionRequest.test.ts +9 -19
  237. package/src/zod/z.ts +4 -0
@@ -330,6 +330,51 @@ describe('decode', () => {
330
330
  `)
331
331
  })
332
332
 
333
+ test('behavior: unnamed, non-indexed input before indexed input (array mode)', () => {
334
+ // Declaration order (uint256, bool indexed) differs from indexed-subset
335
+ // order (bool is the only indexed input). Args must still come back in
336
+ // declaration order, not indexed-subset order.
337
+ const foo = AbiEvent.from('event Foo(uint256, bool indexed)')
338
+ const decoded = AbiEvent.decode(foo, {
339
+ data: '0x0000000000000000000000000000000000000000000000000000000000000111',
340
+ topics: [
341
+ '0x79c52e97493a8f32348c3cf1ebfe4a8dfaeb083ca12cddd87b5d9f7c00d3ccaa',
342
+ '0x0000000000000000000000000000000000000000000000000000000000000001',
343
+ ],
344
+ })
345
+ expect(decoded).toMatchInlineSnapshot(`
346
+ [
347
+ 273n,
348
+ true,
349
+ ]
350
+ `)
351
+ })
352
+
353
+ test('behavior: named + unnamed, indexed input interleaved with non-indexed (mixed mode)', () => {
354
+ // `a` (indexed) and the trailing unnamed `address` (indexed) are not a
355
+ // contiguous prefix of declaration order — a non-indexed `bool` sits
356
+ // between them. All three values must be present and at the correct
357
+ // declaration-order keys; none may be silently dropped or overwritten.
358
+ const foo = AbiEvent.from(
359
+ 'event Foo(uint256 indexed a, bool, address indexed)',
360
+ )
361
+ const decoded = AbiEvent.decode(foo, {
362
+ data: '0x0000000000000000000000000000000000000000000000000000000000000001',
363
+ topics: [
364
+ '0xbf5963a29fe665781e91d832989689110056ee09e32656ea57265dbb1084006e',
365
+ '0x0000000000000000000000000000000000000000000000000000000000000aaa',
366
+ '0x00000000000000000000000000cccccccccccccccccccccccccccccccccccccc',
367
+ ],
368
+ })
369
+ expect(decoded).toMatchInlineSnapshot(`
370
+ {
371
+ "1": true,
372
+ "2": "0x00CCCCCCccCcCccCCcccCCcccCcCcCccCcCcCCcc",
373
+ "a": 2730n,
374
+ }
375
+ `)
376
+ })
377
+
333
378
  test('error: topics mismatch, named', () => {
334
379
  const transfer = AbiEvent.from(
335
380
  'event Transfer(address indexed from, address to, uint256 indexed value)',
@@ -415,6 +415,28 @@ describe('to', () => {
415
415
  const blobs = Blobs.from(data)
416
416
  expect(Blobs.to(blobs, 'Bytes')).toEqual(data)
417
417
  })
418
+
419
+ test('0x80 byte in a non-final blob is not mistaken for the terminator', () => {
420
+ // This payload spans multiple blobs and places terminator-shaped data in the first blob.
421
+ const size = 200_000
422
+ const data = new Uint8Array(size).fill(0x41)
423
+ data[1_000] = 0x80
424
+ data[100_000] = 0x80
425
+
426
+ const blobs = Blobs.from(data)
427
+ expect(blobs.length).toBeGreaterThan(1)
428
+ expect(Blobs.to(blobs, 'Bytes')).toEqual(data)
429
+ })
430
+
431
+ test('all-0x80 data exactly filling the first blob leaves a terminator-only final blob', () => {
432
+ // Exact first-blob capacity forces `Blobs.from` to place the terminator in a second blob.
433
+ const size = 31 * Blobs.fieldElementsPerBlob
434
+ const data = new Uint8Array(size).fill(0x80)
435
+
436
+ const blobs = Blobs.from(data)
437
+ expect(blobs.length).toBe(2)
438
+ expect(Blobs.to(blobs, 'Bytes')).toEqual(data)
439
+ })
418
440
  })
419
441
 
420
442
  describe('toCommitments', () => {
@@ -0,0 +1,96 @@
1
+ import { Frame } from 'ox'
2
+ import { expectTypeOf, test } from 'vite-plus/test'
3
+
4
+ test('from preserves literals', () => {
5
+ const frame = Frame.from({
6
+ data: '0x',
7
+ flags: 3,
8
+ gas: 50_000n,
9
+ mode: 1,
10
+ stateGas: 0n,
11
+ value: 0n,
12
+ })
13
+ expectTypeOf(frame.mode).toEqualTypeOf<1>()
14
+ expectTypeOf(frame.gas).toEqualTypeOf<50_000n>()
15
+ expectTypeOf(Frame.toTuple(frame)).toEqualTypeOf<Frame.Tuple>()
16
+ })
17
+
18
+ test('tuple accepts readonly input', () => {
19
+ expectTypeOf(
20
+ Frame.fromTuple([
21
+ '0x01',
22
+ '0x03',
23
+ '0x',
24
+ ['0xc350', '0x'],
25
+ '0x',
26
+ '0x',
27
+ ] as const),
28
+ ).toEqualTypeOf<Frame.Frame>()
29
+ })
30
+
31
+ test('rejects unsupported modes and unsafe numeric budgets', () => {
32
+ Frame.from({
33
+ data: '0x',
34
+ flags: 0,
35
+ gas: 0n,
36
+ // @ts-expect-error Unknown mode names are not supported.
37
+ mode: 'unknown',
38
+ stateGas: 0n,
39
+ value: 0n,
40
+ })
41
+ Frame.from({
42
+ data: '0x',
43
+ flags: 0,
44
+ // @ts-expect-error Gas budgets use bigint.
45
+ gas: 1,
46
+ mode: 1,
47
+ stateGas: 0n,
48
+ value: 0n,
49
+ })
50
+ })
51
+
52
+ test('flags accept numbers and a strict named union', () => {
53
+ const input = {
54
+ data: '0x',
55
+ flags: 'approvePayment',
56
+ gas: 0n,
57
+ mode: 1,
58
+ stateGas: 0n,
59
+ value: 0n,
60
+ } as const
61
+ expectTypeOf(Frame.from(input).flags).toEqualTypeOf<'approvePayment'>()
62
+ expectTypeOf<Frame.Flags>().toEqualTypeOf<
63
+ | number
64
+ | 'none'
65
+ | 'approvePayment'
66
+ | 'approveExecution'
67
+ | 'approveExecutionAndPayment'
68
+ | 'atomicBatch'
69
+ >()
70
+ // @ts-expect-error Unknown flag names are not supported.
71
+ Frame.from({ ...input, flags: 'unknown' })
72
+ })
73
+
74
+ test('mode accepts numbers and a strict named union', () => {
75
+ const frame = Frame.from({
76
+ data: '0x',
77
+ flags: 0,
78
+ gas: 0n,
79
+ mode: 'sender',
80
+ stateGas: 0n,
81
+ value: 1n,
82
+ })
83
+ expectTypeOf(frame.mode).toEqualTypeOf<'sender'>()
84
+ expectTypeOf<Frame.Mode>().toEqualTypeOf<
85
+ number | 'default' | 'verify' | 'sender'
86
+ >()
87
+ })
88
+
89
+ test('allows omitted zero fields without widening supplied literals', () => {
90
+ expectTypeOf(Frame.from({})).toEqualTypeOf<{}>()
91
+ const frame = Frame.from({ mode: 'verify' })
92
+ expectTypeOf(frame.mode).toEqualTypeOf<'verify'>()
93
+ expectTypeOf(
94
+ Frame.toTuple({ mode: 'sender', value: 1n }),
95
+ ).toEqualTypeOf<Frame.Tuple>()
96
+ })
@@ -0,0 +1,267 @@
1
+ import { describe, expect, test } from 'vite-plus/test'
2
+ import { Rlp, Frame } from 'ox'
3
+
4
+ const frame = {
5
+ data: '0x',
6
+ flags: 3,
7
+ gas: 50_000n,
8
+ mode: 1,
9
+ stateGas: 0n,
10
+ value: 0n,
11
+ } as const
12
+
13
+ const tuple = ['0x01', '0x03', '0x', ['0xc350', '0x'], '0x', '0x'] as const
14
+
15
+ describe('from', () => {
16
+ test('preserves omitted fields', () => {
17
+ expect(Frame.from({ mode: 'verify' })).toEqual({ mode: 'verify' })
18
+ expect(Frame.from({})).toEqual({})
19
+ })
20
+
21
+ test('copies the frame without changing its fields', () => {
22
+ const result = Frame.from(frame)
23
+ expect(result).toEqual(frame)
24
+ expect(result).not.toBe(frame)
25
+ })
26
+ })
27
+
28
+ describe('toTuple', () => {
29
+ test('encodes defaults for omitted fields', () => {
30
+ expect(Frame.toTuple({})).toEqual([
31
+ '0x',
32
+ '0x',
33
+ '0x',
34
+ ['0x', '0x'],
35
+ '0x',
36
+ '0x',
37
+ ])
38
+ expect(
39
+ Frame.toTuple({
40
+ data: undefined,
41
+ flags: undefined,
42
+ gas: undefined,
43
+ mode: undefined,
44
+ stateGas: undefined,
45
+ value: undefined,
46
+ }),
47
+ ).toEqual(Frame.toTuple({}))
48
+ expect(
49
+ Frame.toTuple({
50
+ flags: 'approveExecutionAndPayment',
51
+ gas: 50_000n,
52
+ mode: 'verify',
53
+ }),
54
+ ).toEqual(tuple)
55
+ })
56
+
57
+ test.each([
58
+ ['default', 0],
59
+ ['verify', 1],
60
+ ['sender', 2],
61
+ ] as const)('encodes named mode %s as %i', (mode, numeric) => {
62
+ const input = { ...frame, mode }
63
+ const encoded = Frame.toTuple(input)
64
+ expect(encoded).toEqual(Frame.toTuple({ ...input, mode: numeric }))
65
+ expect(encoded[0]).toBe(numeric === 0 ? '0x' : `0x0${numeric}`)
66
+ expect(Frame.from(input).mode).toBe(mode)
67
+ expect(Frame.fromTuple(encoded).mode).toBe(numeric)
68
+ })
69
+
70
+ test('encodes value transfers in sender mode', () => {
71
+ expect(
72
+ Frame.toTuple({ ...frame, flags: 'none', mode: 'sender', value: 1n }),
73
+ ).toEqual(['0x02', '0x', '0x', ['0xc350', '0x'], '0x01', '0x'])
74
+ })
75
+
76
+ test.each([
77
+ ['none', 0],
78
+ ['approvePayment', 1],
79
+ ['approveExecution', 2],
80
+ ['approveExecutionAndPayment', 3],
81
+ ['atomicBatch', 4],
82
+ ] as const)('encodes named flag %s as %i', (flags, numeric) => {
83
+ const input = { ...frame, flags, mode: 2 } as const
84
+ const encoded = Frame.toTuple(input)
85
+ expect(encoded).toEqual(Frame.toTuple({ ...input, flags: numeric }))
86
+ expect(encoded[1]).toBe(numeric === 0 ? '0x' : `0x0${numeric}`)
87
+ expect(Frame.from(input).flags).toBe(flags)
88
+ expect(Frame.fromTuple(encoded).flags).toBe(numeric)
89
+ })
90
+
91
+ test('encodes the specification field order', () => {
92
+ expect(Frame.toTuple(frame)).toEqual(tuple)
93
+ expect(Rlp.fromHex(Frame.toTuple(frame))).toMatchInlineSnapshot(
94
+ '"0xca010380c482c350808080"',
95
+ )
96
+ })
97
+
98
+ test('encodes zero integers as empty bytes', () => {
99
+ expect(
100
+ Rlp.fromHex(Frame.toTuple({ ...frame, flags: 0, gas: 0n, mode: 0 })),
101
+ ).toMatchInlineSnapshot('"0xc8808080c280808080"')
102
+ })
103
+
104
+ test('preserves large integers and opaque data', () => {
105
+ const input = {
106
+ ...frame,
107
+ data: '0x0001',
108
+ flags: 0,
109
+ gas: 2n ** 63n,
110
+ mode: 2,
111
+ stateGas: 2n ** 63n - 1n,
112
+ value: 2n ** 256n - 1n,
113
+ } as const
114
+ expect(Frame.fromTuple(Frame.toTuple(input))).toEqual(input)
115
+ })
116
+ })
117
+
118
+ describe('fromTuple', () => {
119
+ test('decodes a fixed tuple', () => {
120
+ expect(Frame.fromTuple(tuple)).toEqual(frame)
121
+ })
122
+
123
+ test('distinguishes an implicit target from the zero address', () => {
124
+ const target = '0x0000000000000000000000000000000000000000'
125
+ expect(Frame.fromTuple(tuple)).not.toHaveProperty('target')
126
+ expect(
127
+ Frame.fromTuple([
128
+ tuple[0],
129
+ tuple[1],
130
+ target,
131
+ tuple[3],
132
+ tuple[4],
133
+ tuple[5],
134
+ ]),
135
+ ).toHaveProperty('target', target)
136
+ })
137
+
138
+ test.each(['0x00', '0x0001', '0x1', '0xgg'])(
139
+ 'rejects noncanonical integer %s',
140
+ (value) => {
141
+ expect(() =>
142
+ Frame.fromTuple([value, ...tuple.slice(1)] as never),
143
+ ).toThrow()
144
+ },
145
+ )
146
+
147
+ test.each([
148
+ [],
149
+ [...tuple, '0x'],
150
+ [tuple[0], tuple[1], '0x', [], '0x', '0x'],
151
+ [tuple[0], tuple[1], '0x', '0x', '0x', '0x'],
152
+ [tuple[0], tuple[1], undefined, tuple[3], '0x', '0x'],
153
+ [tuple[0], tuple[1], '0x', tuple[3], '0x', undefined],
154
+ [tuple[0], tuple[1], '0x01', tuple[3], '0x', '0x'],
155
+ ])('rejects malformed tuple %#', (...value) => {
156
+ expect(() => Frame.fromTuple(value as never)).toThrow()
157
+ })
158
+ })
159
+
160
+ describe('assert', () => {
161
+ test.each([
162
+ { data: null },
163
+ { flags: null },
164
+ { gas: null },
165
+ { mode: null },
166
+ { stateGas: null },
167
+ { value: null },
168
+ { mode: -1 },
169
+ { mode: 3 },
170
+ { mode: 0.5 },
171
+ { mode: NaN },
172
+ { mode: 'unknown' },
173
+ { mode: 'toString' },
174
+ { flags: 'atomicBatch', mode: 'verify' },
175
+ { mode: 'verify', value: 1n },
176
+ { mode: 'default', value: 1n },
177
+ { flags: -1 },
178
+ { flags: 8 },
179
+ { flags: 1.5 },
180
+ { flags: 'unknown' },
181
+ { flags: 'toString' },
182
+ { flags: 'atomicBatch', mode: 1 },
183
+ { flags: 4, mode: 1 },
184
+ { flags: 5, mode: 2 },
185
+ { value: 1n },
186
+ { mode: 2, value: -1n },
187
+ { mode: 2, value: 2n ** 256n },
188
+ { gas: -1n },
189
+ { gas: 2n ** 64n },
190
+ { stateGas: -1n },
191
+ { stateGas: 2n ** 64n },
192
+ { gas: 2n ** 64n - 1n, stateGas: 1n },
193
+ { data: '0x0' },
194
+ { data: '0xgg' },
195
+ { target: '0x01' },
196
+ ])('rejects invalid fields %#', (fields) => {
197
+ expect(() => Frame.assert({ ...frame, ...fields } as never)).toThrow()
198
+ })
199
+
200
+ test('leaves batch adjacency to the envelope', () => {
201
+ expect(() => Frame.assert({ ...frame, flags: 4, mode: 2 })).not.toThrow()
202
+ })
203
+ })
204
+
205
+ describe('validate', () => {
206
+ test('validates omitted fields using their defaults', () => {
207
+ expect(Frame.validate({})).toBe(true)
208
+ expect(Frame.validate({ value: 1n })).toBe(false)
209
+ expect(Frame.validate({ mode: 'sender', value: 1n })).toBe(true)
210
+ expect(Frame.validate({ flags: 'atomicBatch', mode: 'verify' })).toBe(false)
211
+ })
212
+
213
+ test('returns structural validity', () => {
214
+ expect(Frame.validate(frame)).toBe(true)
215
+ expect(Frame.validate({ ...frame, value: 1n })).toBe(false)
216
+ })
217
+ })
218
+
219
+ describe('fromRpc', () => {
220
+ test('maps budgets and preserves uint64 precision', () => {
221
+ expect(
222
+ Frame.fromRpc({
223
+ data: '0xdeadbeef',
224
+ executionGasLimit: '0x20000000000001',
225
+ flags: 0,
226
+ mode: 2,
227
+ stateGasLimit: '0x0',
228
+ target: null,
229
+ value: '0x1',
230
+ }),
231
+ ).toEqual({
232
+ data: '0xdeadbeef',
233
+ flags: 0,
234
+ gas: 9007199254740993n,
235
+ mode: 2,
236
+ stateGas: 0n,
237
+ value: 1n,
238
+ })
239
+ })
240
+ })
241
+
242
+ describe('toRpc', () => {
243
+ test('encodes defaults and named modes and flags', () => {
244
+ expect(
245
+ Frame.toRpc(
246
+ Frame.from({ flags: 'approveExecutionAndPayment', mode: 'verify' }),
247
+ ),
248
+ ).toEqual({
249
+ data: '0x',
250
+ executionGasLimit: '0x0',
251
+ flags: 3,
252
+ mode: 1,
253
+ stateGasLimit: '0x0',
254
+ value: '0x0',
255
+ })
256
+ })
257
+ test('accepts numberish budgets', () => {
258
+ expect(Frame.toRpc({ gas: '0xc350', stateGas: 12, value: 0n })).toEqual({
259
+ data: '0x',
260
+ executionGasLimit: '0xc350',
261
+ flags: 0,
262
+ mode: 0,
263
+ stateGasLimit: '0xc',
264
+ value: '0x0',
265
+ })
266
+ })
267
+ })
@@ -0,0 +1,86 @@
1
+ import { FrameReceipt } from 'ox'
2
+ import { describe, expect, test } from 'vp/test'
3
+
4
+ test('exports', () => {
5
+ expect(Object.keys(FrameReceipt).sort()).toMatchInlineSnapshot(`
6
+ [
7
+ "fromRpc",
8
+ "fromRpcStatus",
9
+ "toRpc",
10
+ "toRpcStatus",
11
+ ]
12
+ `)
13
+ })
14
+
15
+ describe('fromRpc', () => {
16
+ test.each([
17
+ [0, 'reverted'],
18
+ [1, 'success'],
19
+ [2, 'skipped'],
20
+ ] as const)('status %s', (status, expected) => {
21
+ expect(
22
+ FrameReceipt.fromRpc({
23
+ executionGasUsed: '0x20000000000001',
24
+ logs: [
25
+ {
26
+ address: '0x1111111111111111111111111111111111111111',
27
+ data: '0xab',
28
+ topics: ['0xcd'],
29
+ },
30
+ ],
31
+ stateGasUsed: '0x2',
32
+ status,
33
+ }),
34
+ ).toEqual({
35
+ gasUsed: 9007199254740993n,
36
+ logs: [
37
+ {
38
+ address: '0x1111111111111111111111111111111111111111',
39
+ data: '0xab',
40
+ topics: ['0xcd'],
41
+ },
42
+ ],
43
+ stateGasUsed: 2n,
44
+ status: expected,
45
+ })
46
+ })
47
+ })
48
+
49
+ describe('toRpc', () => {
50
+ test.each([
51
+ ['reverted', 0],
52
+ ['success', 1],
53
+ ['skipped', 2],
54
+ ] as const)('status %s', (status, expected) => {
55
+ const receipt = FrameReceipt.fromRpc({
56
+ executionGasUsed: '0x20000000000001',
57
+ logs: [],
58
+ stateGasUsed: '0x2',
59
+ status: expected,
60
+ })
61
+ expect(FrameReceipt.toRpc({ ...receipt, status })).toEqual({
62
+ executionGasUsed: '0x20000000000001',
63
+ logs: [],
64
+ stateGasUsed: '0x2',
65
+ status: expected,
66
+ })
67
+ })
68
+
69
+ test('numberish gas', () => {
70
+ expect(
71
+ FrameReceipt.toRpc({
72
+ gasUsed: '0x5208',
73
+ logs: [],
74
+ stateGasUsed: 2,
75
+ status: 'success',
76
+ }),
77
+ ).toMatchInlineSnapshot(`
78
+ {
79
+ "executionGasUsed": "0x5208",
80
+ "logs": [],
81
+ "stateGasUsed": "0x2",
82
+ "status": 1,
83
+ }
84
+ `)
85
+ })
86
+ })
@@ -0,0 +1,93 @@
1
+ import { FrameSignature } from 'ox'
2
+ import { expectTypeOf, test } from 'vite-plus/test'
3
+
4
+ test('from preserves literals and supplies defaults', () => {
5
+ const entry = FrameSignature.from({ signature: '0xaabb' })
6
+ expectTypeOf(entry.scheme).toEqualTypeOf<'arbitrary'>()
7
+ expectTypeOf(entry.payload).toEqualTypeOf<'0x'>()
8
+ expectTypeOf(entry.signature).toEqualTypeOf<'0xaabb'>()
9
+ expectTypeOf(
10
+ FrameSignature.toTuple(entry),
11
+ ).toEqualTypeOf<FrameSignature.Tuple>()
12
+ const numeric = FrameSignature.from({
13
+ scheme: 1,
14
+ signature: { r: '0x01', s: '0x02', yParity: 1 },
15
+ })
16
+ expectTypeOf(numeric.scheme).toEqualTypeOf<1>()
17
+ expectTypeOf(numeric.signature.yParity).toEqualTypeOf<1>()
18
+ const named = FrameSignature.from({ scheme: 'p256' })
19
+ expectTypeOf(named.scheme).toEqualTypeOf<'p256'>()
20
+ })
21
+
22
+ test('accepts typed inputs', () => {
23
+ const input = {} as FrameSignature.from.Input
24
+ expectTypeOf(
25
+ FrameSignature.from(input),
26
+ ).toExtend<FrameSignature.FrameSignature>()
27
+ })
28
+
29
+ test('rejects invalid schemes and signature shapes', () => {
30
+ const arbitrary = {
31
+ scheme: 'arbitrary',
32
+ signature: '0x',
33
+ signer: '0x0000000000000000000000000000000000000000',
34
+ } as const
35
+ // @ts-expect-error Arbitrary entries cannot specify a signer.
36
+ FrameSignature.from(arbitrary)
37
+ // @ts-expect-error Scheme 3 is reserved.
38
+ FrameSignature.from({ scheme: 3, signature: '0x' })
39
+ // @ts-expect-error Named schemes form a strict union.
40
+ FrameSignature.from({ scheme: 'unknown', signature: '0x' })
41
+ // @ts-expect-error P-256 signatures require a public key.
42
+ FrameSignature.from({ scheme: 'p256', signature: { r: '0x01', s: '0x02' } })
43
+ // @ts-expect-error Default arbitrary signatures require bytes.
44
+ FrameSignature.from({ signature: { r: '0x01', s: '0x02', yParity: 1 } })
45
+ })
46
+
47
+ test('explicit undefined defaults remain narrow', () => {
48
+ const entry = FrameSignature.from({
49
+ payload: undefined,
50
+ scheme: undefined,
51
+ signature: '0x',
52
+ })
53
+ expectTypeOf(entry.scheme).toEqualTypeOf<'arbitrary'>()
54
+ expectTypeOf(entry.payload).toEqualTypeOf<'0x'>()
55
+ })
56
+
57
+ test('from hex preserves signature literals and default metadata', () => {
58
+ const entry = FrameSignature.from('0xaabb')
59
+ expectTypeOf(entry).toEqualTypeOf<{
60
+ payload: '0x'
61
+ scheme: 'arbitrary'
62
+ signature: '0xaabb'
63
+ }>()
64
+ // @ts-expect-error Signature bytes must be hex-prefixed.
65
+ FrameSignature.from('aabb')
66
+ })
67
+
68
+ test('from preserves structured signature types', () => {
69
+ const secp = FrameSignature.from({
70
+ scheme: 'secp256k1',
71
+ signature: {
72
+ r: '0x01',
73
+ s: '0x02',
74
+ yParity: 1,
75
+ },
76
+ })
77
+ expectTypeOf(secp.scheme).toEqualTypeOf<'secp256k1'>()
78
+ expectTypeOf(secp.signature.yParity).toEqualTypeOf<1>()
79
+ const p256 = FrameSignature.from({
80
+ publicKey: { prefix: 4, x: '0x01', y: '0x02' },
81
+ scheme: 'p256',
82
+ signature: { r: '0x01', s: '0x02' },
83
+ })
84
+ expectTypeOf(p256.scheme).toEqualTypeOf<'p256'>()
85
+ expectTypeOf(p256.signature.r).toEqualTypeOf<'0x01'>()
86
+ // @ts-expect-error P-256 requires a public key.
87
+ FrameSignature.from({ scheme: 'p256', signature: { r: '0x01', s: '0x02' } })
88
+ FrameSignature.from({
89
+ scheme: 'secp256k1',
90
+ // @ts-expect-error secp256k1 requires recovery parity.
91
+ signature: { r: '0x01', s: '0x02' },
92
+ })
93
+ })