ox 1.7.5 → 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 (139) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/core/Frame.d.ts +290 -0
  3. package/dist/core/Frame.d.ts.map +1 -0
  4. package/dist/core/Frame.js +306 -0
  5. package/dist/core/Frame.js.map +1 -0
  6. package/dist/core/FrameReceipt.d.ts +92 -0
  7. package/dist/core/FrameReceipt.d.ts.map +1 -0
  8. package/dist/core/FrameReceipt.js +72 -0
  9. package/dist/core/FrameReceipt.js.map +1 -0
  10. package/dist/core/FrameSignature.d.ts +357 -0
  11. package/dist/core/FrameSignature.d.ts.map +1 -0
  12. package/dist/core/FrameSignature.js +411 -0
  13. package/dist/core/FrameSignature.js.map +1 -0
  14. package/dist/core/Transaction.d.ts +29 -4
  15. package/dist/core/Transaction.d.ts.map +1 -1
  16. package/dist/core/Transaction.js +25 -4
  17. package/dist/core/Transaction.js.map +1 -1
  18. package/dist/core/TransactionReceipt.d.ts +13 -4
  19. package/dist/core/TransactionReceipt.d.ts.map +1 -1
  20. package/dist/core/TransactionReceipt.js +14 -0
  21. package/dist/core/TransactionReceipt.js.map +1 -1
  22. package/dist/core/TransactionRequest.d.ts +15 -7
  23. package/dist/core/TransactionRequest.d.ts.map +1 -1
  24. package/dist/core/TransactionRequest.js +47 -1
  25. package/dist/core/TransactionRequest.js.map +1 -1
  26. package/dist/core/TxEnvelope.d.ts +35 -21
  27. package/dist/core/TxEnvelope.d.ts.map +1 -1
  28. package/dist/core/TxEnvelope.js +46 -6
  29. package/dist/core/TxEnvelope.js.map +1 -1
  30. package/dist/core/TxEnvelopeEip8141.d.ts +341 -0
  31. package/dist/core/TxEnvelopeEip8141.d.ts.map +1 -0
  32. package/dist/core/TxEnvelopeEip8141.js +485 -0
  33. package/dist/core/TxEnvelopeEip8141.js.map +1 -0
  34. package/dist/core/internal/curves.d.ts +2 -0
  35. package/dist/core/internal/curves.d.ts.map +1 -1
  36. package/dist/core/internal/curves.js +2 -0
  37. package/dist/core/internal/curves.js.map +1 -1
  38. package/dist/index.d.ts +73 -0
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +73 -0
  41. package/dist/index.js.map +1 -1
  42. package/dist/tempo/Transaction.d.ts +2 -0
  43. package/dist/tempo/Transaction.d.ts.map +1 -1
  44. package/dist/tempo/TransactionReceipt.d.ts +2 -0
  45. package/dist/tempo/TransactionReceipt.d.ts.map +1 -1
  46. package/dist/zod/Block.d.ts +123 -0
  47. package/dist/zod/Block.d.ts.map +1 -1
  48. package/dist/zod/Frame.d.ts +73 -0
  49. package/dist/zod/Frame.d.ts.map +1 -0
  50. package/dist/zod/Frame.js +74 -0
  51. package/dist/zod/Frame.js.map +1 -0
  52. package/dist/zod/FrameReceipt.d.ts +50 -0
  53. package/dist/zod/FrameReceipt.d.ts.map +1 -0
  54. package/dist/zod/FrameReceipt.js +37 -0
  55. package/dist/zod/FrameReceipt.js.map +1 -0
  56. package/dist/zod/FrameSignature.d.ts +63 -0
  57. package/dist/zod/FrameSignature.d.ts.map +1 -0
  58. package/dist/zod/FrameSignature.js +66 -0
  59. package/dist/zod/FrameSignature.js.map +1 -0
  60. package/dist/zod/RpcSchema.d.ts +806 -11
  61. package/dist/zod/RpcSchema.d.ts.map +1 -1
  62. package/dist/zod/Transaction.d.ts +252 -0
  63. package/dist/zod/Transaction.d.ts.map +1 -1
  64. package/dist/zod/Transaction.js +54 -0
  65. package/dist/zod/Transaction.js.map +1 -1
  66. package/dist/zod/TransactionEnvelope.d.ts +233 -4
  67. package/dist/zod/TransactionEnvelope.d.ts.map +1 -1
  68. package/dist/zod/TransactionEnvelope.js +9 -0
  69. package/dist/zod/TransactionEnvelope.js.map +1 -1
  70. package/dist/zod/TransactionReceipt.d.ts +48 -0
  71. package/dist/zod/TransactionReceipt.d.ts.map +1 -1
  72. package/dist/zod/TransactionReceipt.js +8 -3
  73. package/dist/zod/TransactionReceipt.js.map +1 -1
  74. package/dist/zod/TransactionRequest.d.ts +50 -2
  75. package/dist/zod/TransactionRequest.d.ts.map +1 -1
  76. package/dist/zod/TransactionRequest.js +10 -4
  77. package/dist/zod/TransactionRequest.js.map +1 -1
  78. package/dist/zod/TxEnvelopeEip8141.d.ts +166 -0
  79. package/dist/zod/TxEnvelopeEip8141.d.ts.map +1 -0
  80. package/dist/zod/TxEnvelopeEip8141.js +76 -0
  81. package/dist/zod/TxEnvelopeEip8141.js.map +1 -0
  82. package/dist/zod/internal/Frame.d.ts +4 -0
  83. package/dist/zod/internal/Frame.d.ts.map +1 -0
  84. package/dist/zod/internal/Frame.js +17 -0
  85. package/dist/zod/internal/Frame.js.map +1 -0
  86. package/dist/zod/internal/rpcSchemas/Eth.d.ts +842 -11
  87. package/dist/zod/internal/rpcSchemas/Eth.d.ts.map +1 -1
  88. package/dist/zod/internal/rpcSchemas/Wallet.d.ts +56 -2
  89. package/dist/zod/internal/rpcSchemas/Wallet.d.ts.map +1 -1
  90. package/dist/zod/tempo/RpcSchemaTempo.d.ts +140 -0
  91. package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
  92. package/dist/zod/tempo/Transaction.d.ts +123 -0
  93. package/dist/zod/tempo/Transaction.d.ts.map +1 -1
  94. package/dist/zod/z.d.ts +4 -0
  95. package/dist/zod/z.d.ts.map +1 -1
  96. package/dist/zod/z.js +4 -0
  97. package/dist/zod/z.js.map +1 -1
  98. package/package.json +41 -1
  99. package/src/core/Frame.ts +443 -0
  100. package/src/core/FrameReceipt.ts +117 -0
  101. package/src/core/FrameSignature.ts +593 -0
  102. package/src/core/Transaction.ts +82 -5
  103. package/src/core/TransactionReceipt.ts +41 -3
  104. package/src/core/TransactionRequest.ts +71 -4
  105. package/src/core/TxEnvelope.ts +159 -90
  106. package/src/core/TxEnvelopeEip8141.ts +715 -0
  107. package/src/core/_test/Frame.test-d.ts +96 -0
  108. package/src/core/_test/Frame.test.ts +267 -0
  109. package/src/core/_test/FrameReceipt.test.ts +86 -0
  110. package/src/core/_test/FrameSignature.test-d.ts +93 -0
  111. package/src/core/_test/FrameSignature.test.ts +554 -0
  112. package/src/core/_test/Transaction.test.ts +33 -0
  113. package/src/core/_test/TransactionEnvelope.test-d.ts +74 -0
  114. package/src/core/_test/TransactionEnvelope.test.ts +107 -0
  115. package/src/core/_test/TransactionReceipt.test.ts +59 -0
  116. package/src/core/_test/TransactionRequest.test-d.ts +50 -0
  117. package/src/core/_test/TransactionRequest.test.ts +88 -1
  118. package/src/core/_test/TxEnvelopeEip8141.test-d.ts +44 -0
  119. package/src/core/_test/TxEnvelopeEip8141.test.ts +1775 -0
  120. package/src/core/_test/curves.test.ts +6 -1
  121. package/src/core/_test/index.test.ts +4 -0
  122. package/src/core/internal/curves.ts +4 -0
  123. package/src/index.ts +77 -0
  124. package/src/version.ts +1 -1
  125. package/src/zod/Frame.ts +82 -0
  126. package/src/zod/FrameReceipt.ts +48 -0
  127. package/src/zod/FrameSignature.ts +78 -0
  128. package/src/zod/Transaction.ts +63 -0
  129. package/src/zod/TransactionEnvelope.ts +33 -0
  130. package/src/zod/TransactionReceipt.ts +14 -3
  131. package/src/zod/TransactionRequest.ts +18 -3
  132. package/src/zod/TxEnvelopeEip8141.ts +102 -0
  133. package/src/zod/_test/Frame.test-d.ts +41 -0
  134. package/src/zod/_test/Frame.test.ts +54 -0
  135. package/src/zod/_test/FrameReceipt.test.ts +75 -0
  136. package/src/zod/_test/FrameSignature.test.ts +94 -0
  137. package/src/zod/_test/TxEnvelopeEip8141.test.ts +116 -0
  138. package/src/zod/internal/Frame.ts +26 -0
  139. package/src/zod/z.ts +4 -0
@@ -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
+ })