ox 1.7.5 → 1.8.1

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 +16 -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 +484 -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 +442 -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 +714 -0
  107. package/src/core/_test/Frame.test-d.ts +108 -0
  108. package/src/core/_test/Frame.test.ts +280 -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 +57 -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,108 @@
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
+ })
97
+
98
+ test('destination is named to', () => {
99
+ const frame = Frame.from({ to: '0x1111111111111111111111111111111111111111' })
100
+ expectTypeOf(
101
+ frame.to,
102
+ ).toEqualTypeOf<'0x1111111111111111111111111111111111111111'>()
103
+ const invalid: Frame.Frame = {
104
+ // @ts-expect-error The decoded destination is named to.
105
+ target: '0x1111111111111111111111111111111111111111',
106
+ }
107
+ void invalid
108
+ })
@@ -0,0 +1,280 @@
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('to')
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('to', 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
+ { to: '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
+ })
268
+
269
+ test('maps to to the RPC target without changing tuple encoding', () => {
270
+ const to = '0x1111111111111111111111111111111111111111'
271
+ const frame = Frame.from({ to })
272
+ const rpc = Frame.toRpc(frame)
273
+ expect(rpc.target).toBe(to)
274
+ expect(rpc).not.toHaveProperty('to')
275
+ const decoded = Frame.fromRpc(rpc)
276
+ expect(decoded.to).toBe(to)
277
+ expect(decoded).not.toHaveProperty('target')
278
+ expect(Frame.toTuple(frame)[2]).toBe(to)
279
+ expect(Frame.fromTuple(Frame.toTuple(frame)).to).toBe(to)
280
+ })
@@ -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
+ })