tempo.ts 0.0.0 → 0.0.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 (153) hide show
  1. package/README.md +92 -0
  2. package/dist/chains.d.ts +1477 -0
  3. package/dist/chains.d.ts.map +1 -0
  4. package/dist/chains.js +43 -0
  5. package/dist/chains.js.map +1 -0
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +2 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/internal/types.d.ts +284 -0
  11. package/dist/internal/types.d.ts.map +1 -0
  12. package/dist/internal/types.js +2 -0
  13. package/dist/internal/types.js.map +1 -0
  14. package/dist/ox/TokenId.d.ts +18 -0
  15. package/dist/ox/TokenId.d.ts.map +1 -0
  16. package/dist/ox/TokenId.js +29 -0
  17. package/dist/ox/TokenId.js.map +1 -0
  18. package/dist/ox/TokenRole.d.ts +11 -0
  19. package/dist/ox/TokenRole.d.ts.map +1 -0
  20. package/dist/ox/TokenRole.js +22 -0
  21. package/dist/ox/TokenRole.js.map +1 -0
  22. package/dist/ox/Transaction.d.ts +161 -0
  23. package/dist/ox/Transaction.d.ts.map +1 -0
  24. package/dist/ox/Transaction.js +117 -0
  25. package/dist/ox/Transaction.js.map +1 -0
  26. package/dist/ox/TransactionEnvelopeFeeToken.d.ts +393 -0
  27. package/dist/ox/TransactionEnvelopeFeeToken.d.ts.map +1 -0
  28. package/dist/ox/TransactionEnvelopeFeeToken.js +452 -0
  29. package/dist/ox/TransactionEnvelopeFeeToken.js.map +1 -0
  30. package/dist/ox/TransactionRequest.d.ts +62 -0
  31. package/dist/ox/TransactionRequest.d.ts.map +1 -0
  32. package/dist/ox/TransactionRequest.js +66 -0
  33. package/dist/ox/TransactionRequest.js.map +1 -0
  34. package/dist/ox/index.d.ts +5 -0
  35. package/dist/ox/index.d.ts.map +1 -0
  36. package/dist/ox/index.js +5 -0
  37. package/dist/ox/index.js.map +1 -0
  38. package/dist/prool/Instance.d.ts +92 -0
  39. package/dist/prool/Instance.d.ts.map +1 -0
  40. package/dist/prool/Instance.js +96 -0
  41. package/dist/prool/Instance.js.map +1 -0
  42. package/dist/prool/index.d.ts +2 -0
  43. package/dist/prool/index.d.ts.map +1 -0
  44. package/dist/prool/index.js +2 -0
  45. package/dist/prool/index.js.map +1 -0
  46. package/dist/viem/abis.d.ts +2058 -0
  47. package/dist/viem/abis.d.ts.map +1 -0
  48. package/dist/viem/abis.js +1599 -0
  49. package/dist/viem/abis.js.map +1 -0
  50. package/dist/viem/actions/amm.d.ts +2091 -0
  51. package/dist/viem/actions/amm.d.ts.map +1 -0
  52. package/dist/viem/actions/amm.js +876 -0
  53. package/dist/viem/actions/amm.js.map +1 -0
  54. package/dist/viem/actions/fee.d.ts +727 -0
  55. package/dist/viem/actions/fee.d.ts.map +1 -0
  56. package/dist/viem/actions/fee.js +230 -0
  57. package/dist/viem/actions/fee.js.map +1 -0
  58. package/dist/viem/actions/index.d.ts +5 -0
  59. package/dist/viem/actions/index.d.ts.map +1 -0
  60. package/dist/viem/actions/index.js +5 -0
  61. package/dist/viem/actions/index.js.map +1 -0
  62. package/dist/viem/actions/policy.d.ts +1900 -0
  63. package/dist/viem/actions/policy.d.ts.map +1 -0
  64. package/dist/viem/actions/policy.js +841 -0
  65. package/dist/viem/actions/policy.js.map +1 -0
  66. package/dist/viem/actions/token.d.ts +13759 -0
  67. package/dist/viem/actions/token.d.ts.map +1 -0
  68. package/dist/viem/actions/token.js +2579 -0
  69. package/dist/viem/actions/token.js.map +1 -0
  70. package/dist/viem/addresses.d.ts +8 -0
  71. package/dist/viem/addresses.d.ts.map +1 -0
  72. package/dist/viem/addresses.js +8 -0
  73. package/dist/viem/addresses.js.map +1 -0
  74. package/dist/viem/chain.d.ts +341 -0
  75. package/dist/viem/chain.d.ts.map +1 -0
  76. package/dist/viem/chain.js +22 -0
  77. package/dist/viem/chain.js.map +1 -0
  78. package/dist/viem/client.d.ts +27 -0
  79. package/dist/viem/client.d.ts.map +1 -0
  80. package/dist/viem/client.js +28 -0
  81. package/dist/viem/client.js.map +1 -0
  82. package/dist/viem/decorator.d.ts +1636 -0
  83. package/dist/viem/decorator.d.ts.map +1 -0
  84. package/dist/viem/decorator.js +95 -0
  85. package/dist/viem/decorator.js.map +1 -0
  86. package/dist/viem/formatters.d.ts +4 -0
  87. package/dist/viem/formatters.d.ts.map +1 -0
  88. package/dist/viem/formatters.js +69 -0
  89. package/dist/viem/formatters.js.map +1 -0
  90. package/dist/viem/index.d.ts +9 -0
  91. package/dist/viem/index.d.ts.map +1 -0
  92. package/dist/viem/index.js +9 -0
  93. package/dist/viem/index.js.map +1 -0
  94. package/dist/viem/transaction.d.ts +54 -0
  95. package/dist/viem/transaction.d.ts.map +1 -0
  96. package/dist/viem/transaction.js +108 -0
  97. package/dist/viem/transaction.js.map +1 -0
  98. package/dist/viem/transport.d.ts +16 -0
  99. package/dist/viem/transport.d.ts.map +1 -0
  100. package/dist/viem/transport.js +33 -0
  101. package/dist/viem/transport.js.map +1 -0
  102. package/dist/viem/types.d.ts +10 -0
  103. package/dist/viem/types.d.ts.map +1 -0
  104. package/dist/viem/types.js +2 -0
  105. package/dist/viem/types.js.map +1 -0
  106. package/dist/viem/utils.d.ts +8 -0
  107. package/dist/viem/utils.d.ts.map +1 -0
  108. package/dist/viem/utils.js +9 -0
  109. package/dist/viem/utils.js.map +1 -0
  110. package/package.json +100 -2
  111. package/src/chains.ts +46 -0
  112. package/src/index.ts +1 -0
  113. package/src/internal/types.ts +414 -0
  114. package/src/ox/TokenId.test.ts +29 -0
  115. package/src/ox/TokenId.ts +35 -0
  116. package/src/ox/TokenRole.test.ts +20 -0
  117. package/src/ox/TokenRole.ts +27 -0
  118. package/src/ox/Transaction.test.ts +257 -0
  119. package/src/ox/Transaction.ts +247 -0
  120. package/src/ox/TransactionEnvelopeFeeToken.test.ts +1215 -0
  121. package/src/ox/TransactionEnvelopeFeeToken.ts +717 -0
  122. package/src/ox/TransactionRequest.ts +100 -0
  123. package/src/ox/index.ts +4 -0
  124. package/src/prool/Instance.test.ts +43 -0
  125. package/src/prool/Instance.ts +190 -0
  126. package/src/prool/index.ts +1 -0
  127. package/src/prool/internal/chain.json +106 -0
  128. package/src/prool/internal/consensus.toml +32 -0
  129. package/src/viem/abis.ts +1606 -0
  130. package/src/viem/actions/amm.test.ts +425 -0
  131. package/src/viem/actions/amm.ts +1308 -0
  132. package/src/viem/actions/fee.test.ts +281 -0
  133. package/src/viem/actions/fee.ts +362 -0
  134. package/src/viem/actions/index.ts +4 -0
  135. package/src/viem/actions/policy.test.ts +514 -0
  136. package/src/viem/actions/policy.ts +1284 -0
  137. package/src/viem/actions/token.test.ts +2172 -0
  138. package/src/viem/actions/token.ts +3830 -0
  139. package/src/viem/addresses.ts +10 -0
  140. package/src/viem/chain.ts +27 -0
  141. package/src/viem/client.bench-d.ts +8 -0
  142. package/src/viem/client.test.ts +152 -0
  143. package/src/viem/client.ts +91 -0
  144. package/src/viem/decorator.bench-d.ts +11 -0
  145. package/src/viem/decorator.test.ts +35 -0
  146. package/src/viem/decorator.ts +1914 -0
  147. package/src/viem/e2e.test.ts +410 -0
  148. package/src/viem/formatters.ts +100 -0
  149. package/src/viem/index.ts +8 -0
  150. package/src/viem/transaction.ts +253 -0
  151. package/src/viem/transport.ts +47 -0
  152. package/src/viem/types.ts +55 -0
  153. package/src/viem/utils.ts +37 -0
@@ -0,0 +1,1215 @@
1
+ import { setTimeout } from 'node:timers/promises'
2
+ import { Authorization, Hex, Rlp, RpcTransport, Secp256k1, Value } from 'ox'
3
+ import { TransactionEnvelopeFeeToken } from 'tempo.ts/ox'
4
+ import { Instance } from 'tempo.ts/prool'
5
+ import { afterEach, beforeEach, describe, expect, test } from 'vitest'
6
+
7
+ const privateKey =
8
+ '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
9
+ const privateKey2 =
10
+ '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d'
11
+
12
+ describe('assert', () => {
13
+ test('invalid chainId', () => {
14
+ expect(() =>
15
+ TransactionEnvelopeFeeToken.assert({
16
+ authorizationList: [
17
+ {
18
+ address: '0x0000000000000000000000000000000000000000',
19
+ chainId: -1,
20
+ nonce: 0n,
21
+ r: 0n,
22
+ s: 0n,
23
+ yParity: 0,
24
+ },
25
+ ],
26
+ chainId: 1,
27
+ }),
28
+ ).toThrowErrorMatchingInlineSnapshot(
29
+ `[TransactionEnvelope.InvalidChainIdError: Chain ID "-1" is invalid.]`,
30
+ )
31
+ })
32
+
33
+ test('invalid address', () => {
34
+ expect(() =>
35
+ TransactionEnvelopeFeeToken.assert({
36
+ authorizationList: [
37
+ {
38
+ address: '0x000000000000000000000000000000000000000z',
39
+ chainId: 0,
40
+ nonce: 0n,
41
+ r: 0n,
42
+ s: 0n,
43
+ yParity: 0,
44
+ },
45
+ ],
46
+ chainId: 1,
47
+ }),
48
+ ).toThrowErrorMatchingInlineSnapshot(
49
+ `
50
+ [Address.InvalidAddressError: Address "0x000000000000000000000000000000000000000z" is invalid.
51
+
52
+ Details: Address is not a 20 byte (40 hexadecimal character) value.]
53
+ `,
54
+ )
55
+ })
56
+
57
+ test('fee cap too high', () => {
58
+ expect(() =>
59
+ TransactionEnvelopeFeeToken.assert({
60
+ authorizationList: [
61
+ {
62
+ address: '0x0000000000000000000000000000000000000000',
63
+ chainId: 1,
64
+ nonce: 0n,
65
+ r: 0n,
66
+ s: 0n,
67
+ yParity: 0,
68
+ },
69
+ ],
70
+ maxFeePerGas: 2n ** 256n - 1n + 1n,
71
+ chainId: 1,
72
+ }),
73
+ ).toThrowErrorMatchingInlineSnapshot(
74
+ `[TransactionEnvelope.FeeCapTooHighError: The fee cap (\`maxFeePerGas\`/\`maxPriorityFeePerGas\` = 115792089237316195423570985008687907853269984665640564039457584007913.129639936 gwei) cannot be higher than the maximum allowed value (2^256-1).]`,
75
+ )
76
+ })
77
+ })
78
+
79
+ describe('deserialize', () => {
80
+ const transaction = TransactionEnvelopeFeeToken.from({
81
+ chainId: 1,
82
+ nonce: 785n,
83
+ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
84
+ value: Value.fromEther('1'),
85
+ maxFeePerGas: Value.fromGwei('2'),
86
+ maxPriorityFeePerGas: Value.fromGwei('2'),
87
+ })
88
+
89
+ test('default', () => {
90
+ const serialized = TransactionEnvelopeFeeToken.serialize(transaction)
91
+ const deserialized = TransactionEnvelopeFeeToken.deserialize(serialized)
92
+ expect(deserialized).toEqual(transaction)
93
+ })
94
+
95
+ test('minimal', () => {
96
+ const transaction = TransactionEnvelopeFeeToken.from({
97
+ chainId: 1,
98
+ nonce: 0n,
99
+ })
100
+ const serialized = TransactionEnvelopeFeeToken.serialize(transaction)
101
+ expect(TransactionEnvelopeFeeToken.deserialize(serialized)).toEqual(
102
+ transaction,
103
+ )
104
+ })
105
+
106
+ test('authorizationList', () => {
107
+ const authorization = Authorization.from({
108
+ address: '0x0000000000000000000000000000000000000000',
109
+ chainId: 0,
110
+ nonce: 0n,
111
+ })
112
+
113
+ const signature = Secp256k1.sign({
114
+ payload: Authorization.getSignPayload(authorization),
115
+ privateKey,
116
+ })
117
+
118
+ const authorizationList = [Authorization.from(authorization, { signature })]
119
+
120
+ const transaction_authorizationList = TransactionEnvelopeFeeToken.from({
121
+ ...transaction,
122
+ authorizationList,
123
+ })
124
+ const serialized = TransactionEnvelopeFeeToken.serialize(
125
+ transaction_authorizationList,
126
+ )
127
+ expect(TransactionEnvelopeFeeToken.deserialize(serialized)).toEqual(
128
+ transaction_authorizationList,
129
+ )
130
+ })
131
+
132
+ test('gas', () => {
133
+ const transaction_gas = TransactionEnvelopeFeeToken.from({
134
+ ...transaction,
135
+ gas: 21001n,
136
+ })
137
+ const serialized = TransactionEnvelopeFeeToken.serialize(transaction_gas)
138
+ expect(TransactionEnvelopeFeeToken.deserialize(serialized)).toEqual(
139
+ transaction_gas,
140
+ )
141
+ })
142
+
143
+ test('accessList', () => {
144
+ const transaction_accessList = TransactionEnvelopeFeeToken.from({
145
+ ...transaction,
146
+ accessList: [
147
+ {
148
+ address: '0x0000000000000000000000000000000000000000',
149
+ storageKeys: [
150
+ '0x0000000000000000000000000000000000000000000000000000000000000001',
151
+ '0x60fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe',
152
+ ],
153
+ },
154
+ ],
155
+ })
156
+ const serialized = TransactionEnvelopeFeeToken.serialize(
157
+ transaction_accessList,
158
+ )
159
+ expect(TransactionEnvelopeFeeToken.deserialize(serialized)).toEqual(
160
+ transaction_accessList,
161
+ )
162
+ })
163
+
164
+ test('data', () => {
165
+ const transaction_data = TransactionEnvelopeFeeToken.from({
166
+ ...transaction,
167
+ data: '0x1234',
168
+ })
169
+ const serialized = TransactionEnvelopeFeeToken.serialize(transaction_data)
170
+ expect(TransactionEnvelopeFeeToken.deserialize(serialized)).toEqual(
171
+ transaction_data,
172
+ )
173
+ })
174
+
175
+ test('signature', () => {
176
+ const signature = Secp256k1.sign({
177
+ payload: TransactionEnvelopeFeeToken.getSignPayload(transaction),
178
+ privateKey,
179
+ })
180
+ const serialized = TransactionEnvelopeFeeToken.serialize(transaction, {
181
+ signature,
182
+ })
183
+ expect(TransactionEnvelopeFeeToken.deserialize(serialized)).toEqual({
184
+ ...transaction,
185
+ ...signature,
186
+ })
187
+ })
188
+
189
+ describe('raw', () => {
190
+ test('default', () => {
191
+ const serialized = `0x77${Rlp.fromHex([
192
+ Hex.fromNumber(1), // chainId
193
+ Hex.fromNumber(0), // nonce
194
+ Hex.fromNumber(1), // maxPriorityFeePerGas
195
+ Hex.fromNumber(1), // maxFeePerGas
196
+ Hex.fromNumber(1), // gas
197
+ '0x0000000000000000000000000000000000000000', // to
198
+ Hex.fromNumber(0), // value
199
+ '0x', // data
200
+ '0x', // accessList
201
+ '0x', // authorizationList
202
+ '0x', // feeToken
203
+ '0x', // feePayerSignature
204
+ ]).slice(2)}` as const
205
+ expect(
206
+ TransactionEnvelopeFeeToken.deserialize(serialized),
207
+ ).toMatchInlineSnapshot(`
208
+ {
209
+ "chainId": 1,
210
+ "gas": 1n,
211
+ "maxFeePerGas": 1n,
212
+ "maxPriorityFeePerGas": 1n,
213
+ "nonce": 0n,
214
+ "to": "0x0000000000000000000000000000000000000000",
215
+ "type": "feeToken",
216
+ "value": 0n,
217
+ }
218
+ `)
219
+ })
220
+
221
+ test('empty sig', () => {
222
+ const serialized = `0x77${Rlp.fromHex([
223
+ Hex.fromNumber(1), // chainId
224
+ Hex.fromNumber(0), // nonce
225
+ Hex.fromNumber(1), // maxPriorityFeePerGas
226
+ Hex.fromNumber(1), // maxFeePerGas
227
+ Hex.fromNumber(1), // gas
228
+ '0x0000000000000000000000000000000000000000', // to
229
+ Hex.fromNumber(0), // value
230
+ '0x', // data
231
+ '0x', // accessList
232
+ '0x', // authorizationList
233
+ '0x', // feeToken
234
+ '0x', // feePayerSignature
235
+ '0x', // r
236
+ '0x', // v
237
+ '0x', // s
238
+ ]).slice(2)}` as const
239
+ expect(
240
+ TransactionEnvelopeFeeToken.deserialize(serialized),
241
+ ).toMatchInlineSnapshot(`
242
+ {
243
+ "chainId": 1,
244
+ "gas": 1n,
245
+ "maxFeePerGas": 1n,
246
+ "maxPriorityFeePerGas": 1n,
247
+ "nonce": 0n,
248
+ "r": 0n,
249
+ "s": 0n,
250
+ "to": "0x0000000000000000000000000000000000000000",
251
+ "type": "feeToken",
252
+ "value": 0n,
253
+ "yParity": 0,
254
+ }
255
+ `)
256
+ })
257
+
258
+ test('low sig coords', () => {
259
+ const serialized = `0x77${Rlp.fromHex([
260
+ Hex.fromNumber(1), // chainId
261
+ Hex.fromNumber(0), // nonce
262
+ Hex.fromNumber(1), // maxPriorityFeePerGas
263
+ Hex.fromNumber(1), // maxFeePerGas
264
+ Hex.fromNumber(1), // gas
265
+ '0x0000000000000000000000000000000000000000', // to
266
+ Hex.fromNumber(0), // value
267
+ '0x', // data
268
+ '0x', // accessList
269
+ '0x', // authorizationList
270
+ '0x', // feeToken
271
+ '0x', // feePayerSignature
272
+ '0x', // r
273
+ Hex.fromNumber(69), // v
274
+ Hex.fromNumber(420), // s
275
+ ]).slice(2)}` as const
276
+ expect(
277
+ TransactionEnvelopeFeeToken.deserialize(serialized),
278
+ ).toMatchInlineSnapshot(`
279
+ {
280
+ "chainId": 1,
281
+ "gas": 1n,
282
+ "maxFeePerGas": 1n,
283
+ "maxPriorityFeePerGas": 1n,
284
+ "nonce": 0n,
285
+ "r": 69n,
286
+ "s": 420n,
287
+ "to": "0x0000000000000000000000000000000000000000",
288
+ "type": "feeToken",
289
+ "value": 0n,
290
+ "yParity": 0,
291
+ }
292
+ `)
293
+ })
294
+ })
295
+
296
+ describe('errors', () => {
297
+ test('invalid access list (invalid address)', () => {
298
+ expect(() =>
299
+ TransactionEnvelopeFeeToken.deserialize(
300
+ `0x77${Rlp.fromHex([
301
+ Hex.fromNumber(1), // chainId
302
+ Hex.fromNumber(0), // nonce
303
+ Hex.fromNumber(1), // maxPriorityFeePerGas
304
+ Hex.fromNumber(1), // maxFeePerGas
305
+ Hex.fromNumber(1), // gas
306
+ '0x0000000000000000000000000000000000000000', // to
307
+ Hex.fromNumber(0), // value
308
+ '0x', // data
309
+ [
310
+ [
311
+ '0x',
312
+ [
313
+ '0x0000000000000000000000000000000000000000000000000000000000000001',
314
+ ],
315
+ ],
316
+ ], // accessList
317
+ '0x', // authorizationList
318
+ ]).slice(2)}`,
319
+ ),
320
+ ).toThrowErrorMatchingInlineSnapshot(`
321
+ [TransactionEnvelope.InvalidSerializedError: Invalid serialized transaction of type "feeToken" was provided.
322
+
323
+ Serialized Transaction: "0x77f84201000101019400000000000000000000000000000000000000000080e4e380e1a0000000000000000000000000000000000000000000000000000000000000000180"
324
+ Missing Attributes: feeToken, feePayerSignatureOrSender, yParity, r, s]
325
+ `)
326
+
327
+ expect(() =>
328
+ TransactionEnvelopeFeeToken.deserialize(
329
+ `0x77${Rlp.fromHex([
330
+ Hex.fromNumber(1), // chainId
331
+ Hex.fromNumber(0), // nonce
332
+ Hex.fromNumber(1), // maxPriorityFeePerGas
333
+ Hex.fromNumber(1), // maxFeePerGas
334
+ Hex.fromNumber(1), // gas
335
+ '0x0000000000000000000000000000000000000000', // to
336
+ Hex.fromNumber(0), // value
337
+ '0x', // data
338
+ [['0x123456', ['0x0']]], // accessList
339
+ '0x', // authorizationList
340
+ ]).slice(2)}`,
341
+ ),
342
+ ).toThrowErrorMatchingInlineSnapshot(`
343
+ [TransactionEnvelope.InvalidSerializedError: Invalid serialized transaction of type "feeToken" was provided.
344
+
345
+ Serialized Transaction: "0x77e501000101019400000000000000000000000000000000000000000080c7c683123456c10080"
346
+ Missing Attributes: feeToken, feePayerSignatureOrSender, yParity, r, s]
347
+ `)
348
+ })
349
+
350
+ test('invalid transaction (all missing)', () => {
351
+ expect(() =>
352
+ TransactionEnvelopeFeeToken.deserialize(
353
+ `0x77${Rlp.fromHex([]).slice(2)}`,
354
+ ),
355
+ ).toThrowErrorMatchingInlineSnapshot(`
356
+ [TransactionEnvelope.InvalidSerializedError: Invalid serialized transaction of type "feeToken" was provided.
357
+
358
+ Serialized Transaction: "0x77c0"
359
+ Missing Attributes: chainId, nonce, feeToken, maxPriorityFeePerGas, maxFeePerGas, gas, to, value, data, accessList, authorizationList, feePayerSignatureOrSender]
360
+ `)
361
+ })
362
+
363
+ test('invalid transaction (some missing)', () => {
364
+ expect(() =>
365
+ TransactionEnvelopeFeeToken.deserialize(
366
+ `0x77${Rlp.fromHex(['0x00', '0x01']).slice(2)}`,
367
+ ),
368
+ ).toThrowErrorMatchingInlineSnapshot(`
369
+ [TransactionEnvelope.InvalidSerializedError: Invalid serialized transaction of type "feeToken" was provided.
370
+
371
+ Serialized Transaction: "0x77c20001"
372
+ Missing Attributes: feeToken, maxPriorityFeePerGas, maxFeePerGas, gas, to, value, data, accessList, authorizationList, feePayerSignatureOrSender]
373
+ `)
374
+ })
375
+
376
+ test('invalid transaction (missing signature)', () => {
377
+ expect(() =>
378
+ TransactionEnvelopeFeeToken.deserialize(
379
+ `0x77${Rlp.fromHex([
380
+ '0x',
381
+ '0x',
382
+ '0x',
383
+ '0x',
384
+ '0x',
385
+ '0x',
386
+ '0x',
387
+ '0x',
388
+ '0x',
389
+ '0x',
390
+ '0x',
391
+ '0x',
392
+ '0x',
393
+ ]).slice(2)}`,
394
+ ),
395
+ ).toThrowErrorMatchingInlineSnapshot(`
396
+ [TransactionEnvelope.InvalidSerializedError: Invalid serialized transaction of type "feeToken" was provided.
397
+
398
+ Serialized Transaction: "0x77cd80808080808080808080808080"
399
+ Missing Attributes: r, s]
400
+ `)
401
+ })
402
+ })
403
+ })
404
+
405
+ describe('from', () => {
406
+ test('default', () => {
407
+ {
408
+ const envelope = TransactionEnvelopeFeeToken.from({
409
+ chainId: 1,
410
+ nonce: 0n,
411
+ })
412
+ expect(envelope).toMatchInlineSnapshot(`
413
+ {
414
+ "chainId": 1,
415
+ "nonce": 0n,
416
+ "type": "feeToken",
417
+ }
418
+ `)
419
+ const serialized = TransactionEnvelopeFeeToken.serialize(envelope)
420
+ const envelope2 = TransactionEnvelopeFeeToken.from(serialized)
421
+ expect(envelope2).toEqual(envelope)
422
+ }
423
+
424
+ {
425
+ const envelope = TransactionEnvelopeFeeToken.from({
426
+ chainId: 1,
427
+ nonce: 0n,
428
+ r: 0n,
429
+ s: 1n,
430
+ yParity: 0,
431
+ })
432
+ expect(envelope).toMatchInlineSnapshot(`
433
+ {
434
+ "chainId": 1,
435
+ "nonce": 0n,
436
+ "r": 0n,
437
+ "s": 1n,
438
+ "type": "feeToken",
439
+ "yParity": 0,
440
+ }
441
+ `)
442
+ const serialized = TransactionEnvelopeFeeToken.serialize(envelope)
443
+ const envelope2 = TransactionEnvelopeFeeToken.from(serialized)
444
+ expect(envelope2).toEqual(envelope)
445
+ }
446
+ })
447
+
448
+ test('options: signature', () => {
449
+ const envelope = TransactionEnvelopeFeeToken.from(
450
+ {
451
+ chainId: 1,
452
+ nonce: 0n,
453
+ },
454
+ {
455
+ signature: {
456
+ r: 0n,
457
+ s: 1n,
458
+ yParity: 0,
459
+ },
460
+ },
461
+ )
462
+ expect(envelope).toMatchInlineSnapshot(`
463
+ {
464
+ "chainId": 1,
465
+ "nonce": 0n,
466
+ "r": 0n,
467
+ "s": 1n,
468
+ "type": "feeToken",
469
+ "yParity": 0,
470
+ }
471
+ `)
472
+ const serialized = TransactionEnvelopeFeeToken.serialize(envelope)
473
+ const envelope2 = TransactionEnvelopeFeeToken.from(serialized)
474
+ expect(envelope2).toEqual(envelope)
475
+ })
476
+
477
+ test('options: feePayerSignature', () => {
478
+ const envelope = TransactionEnvelopeFeeToken.from(
479
+ {
480
+ chainId: 1,
481
+ nonce: 0n,
482
+ r: 1n,
483
+ s: 2n,
484
+ yParity: 0,
485
+ },
486
+ {
487
+ feePayerSignature: {
488
+ r: 0n,
489
+ s: 1n,
490
+ yParity: 0,
491
+ },
492
+ },
493
+ )
494
+ expect(envelope).toMatchInlineSnapshot(`
495
+ {
496
+ "chainId": 1,
497
+ "feePayerSignature": {
498
+ "r": 0n,
499
+ "s": 1n,
500
+ "yParity": 0,
501
+ },
502
+ "nonce": 0n,
503
+ "r": 1n,
504
+ "s": 2n,
505
+ "type": "feeToken",
506
+ "yParity": 0,
507
+ }
508
+ `)
509
+ const serialized = TransactionEnvelopeFeeToken.serialize(envelope)
510
+ const envelope2 = TransactionEnvelopeFeeToken.from(serialized)
511
+ expect(envelope2).toEqual(envelope)
512
+ })
513
+ })
514
+
515
+ describe('getSignPayload', () => {
516
+ test('default', () => {
517
+ const authorization = Authorization.from({
518
+ address: '0x0000000000000000000000000000000000000000',
519
+ chainId: 1,
520
+ nonce: 785n,
521
+ })
522
+ const signature_auth = Secp256k1.sign({
523
+ payload: Authorization.getSignPayload(authorization),
524
+ privateKey,
525
+ })
526
+
527
+ const envelope = TransactionEnvelopeFeeToken.from({
528
+ authorizationList: [
529
+ Authorization.from(authorization, { signature: signature_auth }),
530
+ ],
531
+ chainId: 1,
532
+ gas: 21000n,
533
+ maxFeePerGas: 13000000000n,
534
+ maxPriorityFeePerGas: 1000000000n,
535
+ nonce: 665n,
536
+ value: 1000000000000000000n,
537
+ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
538
+ })
539
+
540
+ const signature = Secp256k1.sign({
541
+ payload: TransactionEnvelopeFeeToken.getSignPayload(envelope),
542
+ privateKey,
543
+ })
544
+
545
+ const envelope_signed = TransactionEnvelopeFeeToken.from(envelope, {
546
+ signature,
547
+ })
548
+
549
+ expect(envelope_signed).toMatchInlineSnapshot(`
550
+ {
551
+ "authorizationList": [
552
+ {
553
+ "address": "0x0000000000000000000000000000000000000000",
554
+ "chainId": 1,
555
+ "nonce": 785n,
556
+ "r": 45905576947909600150892513825393874260108741328435165924126757974077129494832n,
557
+ "s": 48243644890612770021063037464756173394424732895207796335842780877380370396087n,
558
+ "yParity": 0,
559
+ },
560
+ ],
561
+ "chainId": 1,
562
+ "gas": 21000n,
563
+ "maxFeePerGas": 13000000000n,
564
+ "maxPriorityFeePerGas": 1000000000n,
565
+ "nonce": 665n,
566
+ "r": 2197655434111937974218303607195887458649942608682485837450202937256281617344n,
567
+ "s": 45514547126785856401831290616133478750134795595263686266026091483746271601472n,
568
+ "to": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8",
569
+ "type": "feeToken",
570
+ "value": 1000000000000000000n,
571
+ "yParity": 0,
572
+ }
573
+ `)
574
+ })
575
+ })
576
+
577
+ describe('hash', () => {
578
+ test('default', () => {
579
+ const envelope = TransactionEnvelopeFeeToken.from({
580
+ authorizationList: [],
581
+ chainId: 1,
582
+ gas: 21000n,
583
+ maxFeePerGas: 13000000000n,
584
+ maxPriorityFeePerGas: 1000000000n,
585
+ nonce: 665n,
586
+ value: 1000000000000000000n,
587
+ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
588
+ r: BigInt(
589
+ '0xacf664dcd984d082b68c434feb66ac684711babdeefe6f101bf8df88fc367a37',
590
+ ),
591
+ s: BigInt(
592
+ '0x5e0800058a9b5c2250bed60ee969a45b7445e562a8298c2d222d114e6dfbfcb9',
593
+ ),
594
+ yParity: 0,
595
+ })
596
+
597
+ const hash = TransactionEnvelopeFeeToken.hash(envelope)
598
+ expect(hash).toMatchInlineSnapshot(
599
+ `"0x28ff6c475c6bc8e5ac460a9fa89ef29c3de20e35f128e5c2b36946a67df572c9"`,
600
+ )
601
+ })
602
+
603
+ test('behavior: presign', () => {
604
+ const envelope = TransactionEnvelopeFeeToken.from({
605
+ authorizationList: [],
606
+ chainId: 1,
607
+ gas: 21000n,
608
+ maxFeePerGas: 13000000000n,
609
+ maxPriorityFeePerGas: 1000000000n,
610
+ nonce: 665n,
611
+ value: 1000000000000000000n,
612
+ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
613
+ r: BigInt(
614
+ '0xacf664dcd984d082b68c434feb66ac684711babdeefe6f101bf8df88fc367a37',
615
+ ),
616
+ s: BigInt(
617
+ '0x5e0800058a9b5c2250bed60ee969a45b7445e562a8298c2d222d114e6dfbfcb9',
618
+ ),
619
+ yParity: 0,
620
+ })
621
+
622
+ const hash = TransactionEnvelopeFeeToken.hash(envelope, { presign: true })
623
+ expect(hash).toMatchInlineSnapshot(
624
+ `"0x19c3cd0e89bbd10237160f3d6b28c5bc41494ea31dc54ceb7d6cad9a48fae7b4"`,
625
+ )
626
+ })
627
+ })
628
+
629
+ describe('serialize', () => {
630
+ const transaction = TransactionEnvelopeFeeToken.from({
631
+ chainId: 1,
632
+ nonce: 785n,
633
+ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
634
+ value: Value.fromEther('1'),
635
+ maxFeePerGas: Value.fromGwei('2'),
636
+ maxPriorityFeePerGas: Value.fromGwei('2'),
637
+ })
638
+
639
+ test('default', () => {
640
+ const serialized = TransactionEnvelopeFeeToken.serialize(transaction)
641
+ expect(serialized).toMatchInlineSnapshot(
642
+ `"0x77f20182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0c08080"`,
643
+ )
644
+ expect(TransactionEnvelopeFeeToken.deserialize(serialized)).toEqual(
645
+ transaction,
646
+ )
647
+ })
648
+
649
+ test('default (all zeros)', () => {
650
+ const transaction = TransactionEnvelopeFeeToken.from({
651
+ to: undefined,
652
+ nonce: 0n,
653
+ chainId: 1,
654
+ maxFeePerGas: 0n,
655
+ maxPriorityFeePerGas: 0n,
656
+ value: 0n,
657
+ })
658
+
659
+ const serialized = TransactionEnvelopeFeeToken.serialize(transaction)
660
+
661
+ expect(serialized).toMatchInlineSnapshot(`"0x77cc0180808080808080c0c08080"`)
662
+ expect(TransactionEnvelopeFeeToken.deserialize(serialized)).toEqual({
663
+ chainId: 1,
664
+ nonce: 0n,
665
+ type: 'feeToken',
666
+ })
667
+ })
668
+
669
+ test('minimal (w/ type)', () => {
670
+ const transaction = TransactionEnvelopeFeeToken.from({
671
+ chainId: 1,
672
+ nonce: 0n,
673
+ })
674
+ const serialized = TransactionEnvelopeFeeToken.serialize(transaction)
675
+ expect(serialized).toMatchInlineSnapshot(`"0x77cc0180808080808080c0c08080"`)
676
+ expect(TransactionEnvelopeFeeToken.deserialize(serialized)).toEqual(
677
+ transaction,
678
+ )
679
+ })
680
+
681
+ test('minimal (w/ maxFeePerGas)', () => {
682
+ const transaction = TransactionEnvelopeFeeToken.from({
683
+ chainId: 1,
684
+ maxFeePerGas: 1n,
685
+ nonce: 0n,
686
+ })
687
+ const serialized = TransactionEnvelopeFeeToken.serialize(transaction)
688
+ expect(serialized).toMatchInlineSnapshot(`"0x77cc0180800180808080c0c08080"`)
689
+ expect(TransactionEnvelopeFeeToken.deserialize(serialized)).toEqual(
690
+ transaction,
691
+ )
692
+ })
693
+
694
+ test('authorizationList', () => {
695
+ const authorization = Authorization.from({
696
+ address: '0x0000000000000000000000000000000000000000',
697
+ chainId: 0,
698
+ nonce: 0n,
699
+ })
700
+
701
+ const signature = Secp256k1.sign({
702
+ payload: Authorization.getSignPayload(authorization),
703
+ privateKey,
704
+ })
705
+
706
+ const authorizationList = [Authorization.from(authorization, { signature })]
707
+
708
+ const transaction_authorizationList = TransactionEnvelopeFeeToken.from({
709
+ ...transaction,
710
+ authorizationList,
711
+ })
712
+ const serialized = TransactionEnvelopeFeeToken.serialize(
713
+ transaction_authorizationList,
714
+ )
715
+ expect(serialized).toMatchInlineSnapshot(
716
+ `"0x77f88f0182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0f85cf85a809400000000000000000000000000000000000000008001a0aa64791de483fa82224c2beeba9ee4c89323db0d1a39c24bcfa555b8d3c31aa9a03cdeba27b1c512236967597d1352b6d71239bebde01237ca72b26d6fd218cb978080"`,
717
+ )
718
+ expect(TransactionEnvelopeFeeToken.deserialize(serialized)).toEqual(
719
+ transaction_authorizationList,
720
+ )
721
+ })
722
+
723
+ test('gas', () => {
724
+ const transaction_gas = TransactionEnvelopeFeeToken.from({
725
+ ...transaction,
726
+ gas: 21001n,
727
+ })
728
+ const serialized = TransactionEnvelopeFeeToken.serialize(transaction_gas)
729
+ expect(serialized).toMatchInlineSnapshot(
730
+ `"0x77f401820311847735940084773594008252099470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0c08080"`,
731
+ )
732
+ expect(TransactionEnvelopeFeeToken.deserialize(serialized)).toEqual(
733
+ transaction_gas,
734
+ )
735
+ })
736
+
737
+ test('feeToken', () => {
738
+ const transaction_feeToken = TransactionEnvelopeFeeToken.from({
739
+ ...transaction,
740
+ feeToken: '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef',
741
+ })
742
+ const serialized =
743
+ TransactionEnvelopeFeeToken.serialize(transaction_feeToken)
744
+ expect(serialized).toMatchInlineSnapshot(
745
+ `"0x77f8460182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0c094deadbeefdeadbeefdeadbeefdeadbeefdeadbeef80"`,
746
+ )
747
+ expect(TransactionEnvelopeFeeToken.deserialize(serialized)).toEqual(
748
+ transaction_feeToken,
749
+ )
750
+ })
751
+
752
+ test('accessList', () => {
753
+ const transaction_accessList = TransactionEnvelopeFeeToken.from({
754
+ ...transaction,
755
+ accessList: [
756
+ {
757
+ address: '0x0000000000000000000000000000000000000000',
758
+ storageKeys: [
759
+ '0x0000000000000000000000000000000000000000000000000000000000000001',
760
+ '0x60fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe',
761
+ ],
762
+ },
763
+ ],
764
+ })
765
+ const serialized = TransactionEnvelopeFeeToken.serialize(
766
+ transaction_accessList,
767
+ )
768
+ expect(serialized).toMatchInlineSnapshot(
769
+ `"0x77f88e0182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080f85bf859940000000000000000000000000000000000000000f842a00000000000000000000000000000000000000000000000000000000000000001a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fec08080"`,
770
+ )
771
+ expect(TransactionEnvelopeFeeToken.deserialize(serialized)).toEqual(
772
+ transaction_accessList,
773
+ )
774
+ })
775
+
776
+ test('data', () => {
777
+ const transaction_data = TransactionEnvelopeFeeToken.from({
778
+ ...transaction,
779
+ data: '0x1234',
780
+ })
781
+ const serialized = TransactionEnvelopeFeeToken.serialize(transaction_data)
782
+ expect(serialized).toMatchInlineSnapshot(
783
+ `"0x77f40182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a7640000821234c0c08080"`,
784
+ )
785
+ expect(TransactionEnvelopeFeeToken.deserialize(serialized)).toEqual(
786
+ transaction_data,
787
+ )
788
+ })
789
+
790
+ test('options: signature', async () => {
791
+ const signature = Secp256k1.sign({
792
+ payload: TransactionEnvelopeFeeToken.getSignPayload(transaction),
793
+ privateKey,
794
+ })
795
+ const serialized = TransactionEnvelopeFeeToken.serialize(transaction, {
796
+ signature,
797
+ })
798
+ expect(serialized).toMatchInlineSnapshot(
799
+ `"0x77f8750182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0c0808001a069cf330f0a61a4d7712f2cbbb933eafc1ed6be732cf49b7269c782bb301c727ea0474916716fd0000d30e69a7b629697f04b542cc6abcb3c4e61dfdf3e9541dcc7"`,
800
+ )
801
+ expect(TransactionEnvelopeFeeToken.deserialize(serialized)).toEqual({
802
+ ...transaction,
803
+ ...signature,
804
+ })
805
+ })
806
+
807
+ test('options: signature', () => {
808
+ expect(
809
+ TransactionEnvelopeFeeToken.serialize(transaction, {
810
+ signature: {
811
+ r: BigInt(
812
+ '0x60fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe',
813
+ ),
814
+ s: BigInt(
815
+ '0x60fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe',
816
+ ),
817
+ yParity: 1,
818
+ },
819
+ }),
820
+ ).toMatchInlineSnapshot(
821
+ `"0x77f8750182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0c0808001a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe"`,
822
+ )
823
+
824
+ expect(
825
+ TransactionEnvelopeFeeToken.serialize(transaction, {
826
+ signature: {
827
+ r: BigInt(
828
+ '0x60fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe',
829
+ ),
830
+ s: BigInt(
831
+ '0x60fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe',
832
+ ),
833
+ yParity: 0,
834
+ },
835
+ }),
836
+ ).toMatchInlineSnapshot(
837
+ `"0x77f8750182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0c0808080a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe"`,
838
+ )
839
+
840
+ expect(
841
+ TransactionEnvelopeFeeToken.serialize(transaction, {
842
+ signature: {
843
+ r: 0n,
844
+ s: 0n,
845
+ yParity: 0,
846
+ },
847
+ }),
848
+ ).toMatchInlineSnapshot(
849
+ `"0x77f50182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0c08080808080"`,
850
+ )
851
+ })
852
+
853
+ test('options: feePayerSignature', async () => {
854
+ const signature = Secp256k1.sign({
855
+ payload: TransactionEnvelopeFeeToken.getSignPayload(transaction),
856
+ privateKey,
857
+ })
858
+ const transaction_signed = TransactionEnvelopeFeeToken.from(transaction, {
859
+ signature,
860
+ })
861
+
862
+ const sender = Secp256k1.recoverAddress({
863
+ payload: TransactionEnvelopeFeeToken.getSignPayload(transaction),
864
+ signature,
865
+ })
866
+
867
+ const feePayerSignature = Secp256k1.sign({
868
+ payload: TransactionEnvelopeFeeToken.getFeePayerSignPayload(
869
+ transaction_signed,
870
+ {
871
+ sender,
872
+ },
873
+ ),
874
+ privateKey: privateKey2,
875
+ })
876
+ const serialized_feePayer = TransactionEnvelopeFeeToken.serialize(
877
+ transaction_signed,
878
+ {
879
+ feePayerSignature,
880
+ },
881
+ )
882
+
883
+ expect(serialized_feePayer).toMatchInlineSnapshot(
884
+ `"0x77f8b90182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0c080f84380a0e64a93976c4cfd0ab07d40d58ef3f80f8c9dcc71c78c78b063654c7cab16326ba0090430ef7278d4d0ca33832eab5f92161cbf05e0b8b4db309ef9b5fe0b2a31ef01a069cf330f0a61a4d7712f2cbbb933eafc1ed6be732cf49b7269c782bb301c727ea0474916716fd0000d30e69a7b629697f04b542cc6abcb3c4e61dfdf3e9541dcc7"`,
885
+ )
886
+ expect(
887
+ TransactionEnvelopeFeeToken.deserialize(serialized_feePayer),
888
+ ).toEqual({
889
+ ...transaction,
890
+ ...signature,
891
+ feePayerSignature,
892
+ })
893
+ })
894
+ })
895
+
896
+ describe('validate', () => {
897
+ test('default', () => {
898
+ expect(
899
+ TransactionEnvelopeFeeToken.validate({
900
+ authorizationList: [],
901
+ chainId: 1,
902
+ }),
903
+ ).toBe(true)
904
+ expect(
905
+ TransactionEnvelopeFeeToken.validate({
906
+ authorizationList: [],
907
+ maxFeePerGas: 2n ** 257n,
908
+ chainId: 1,
909
+ }),
910
+ ).toBe(false)
911
+ })
912
+ })
913
+
914
+ test('exports', () => {
915
+ expect(Object.keys(TransactionEnvelopeFeeToken)).toMatchInlineSnapshot(`
916
+ [
917
+ "feePayerMagic",
918
+ "serializedType",
919
+ "type",
920
+ "assert",
921
+ "deserialize",
922
+ "from",
923
+ "getFeePayerSignPayload",
924
+ "getSignPayload",
925
+ "hash",
926
+ "serialize",
927
+ "validate",
928
+ ]
929
+ `)
930
+ })
931
+
932
+ describe('e2e', () => {
933
+ const node = Instance.tempo({ port: 3000 })
934
+ beforeEach(() => node.start())
935
+ afterEach(() => node.stop())
936
+
937
+ test('behavior: default', async () => {
938
+ const address = '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266'
939
+ const privateKey =
940
+ '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
941
+
942
+ const transport = RpcTransport.fromHttp('http://localhost:3000')
943
+
944
+ const nonce = await transport.request({
945
+ method: 'eth_getTransactionCount',
946
+ params: [address, 'pending'],
947
+ })
948
+
949
+ const transaction = TransactionEnvelopeFeeToken.from({
950
+ chainId: 1337,
951
+ feeToken: '0x20c0000000000000000000000000000000000000',
952
+ nonce: BigInt(nonce),
953
+ gas: 21000n,
954
+ to: '0x0000000000000000000000000000000000000000',
955
+ value: Value.fromEther('1'),
956
+ maxFeePerGas: Value.fromGwei('20'),
957
+ maxPriorityFeePerGas: Value.fromGwei('10'),
958
+ })
959
+
960
+ const signature = Secp256k1.sign({
961
+ payload: TransactionEnvelopeFeeToken.getSignPayload(transaction),
962
+ privateKey,
963
+ })
964
+
965
+ const serialized_signed = TransactionEnvelopeFeeToken.serialize(
966
+ transaction,
967
+ {
968
+ signature,
969
+ },
970
+ )
971
+
972
+ const receipt = await transport.request({
973
+ method: 'eth_sendRawTransactionSync',
974
+ params: [serialized_signed],
975
+ })
976
+
977
+ expect(receipt).toBeDefined()
978
+
979
+ {
980
+ const response = await transport.request({
981
+ method: 'eth_getTransactionByHash',
982
+ params: [receipt.transactionHash],
983
+ })
984
+ if (!response) throw new Error()
985
+
986
+ const { blockNumber, blockHash, ...rest } = response
987
+
988
+ expect(blockNumber).toBeDefined()
989
+ expect(blockHash).toBeDefined()
990
+ expect(rest).toMatchInlineSnapshot(`
991
+ {
992
+ "accessList": [],
993
+ "authorizationList": [],
994
+ "chainId": "0x539",
995
+ "feePayerSignature": null,
996
+ "feeToken": "0x20c0000000000000000000000000000000000000",
997
+ "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
998
+ "gas": "0x5208",
999
+ "gasPrice": "0x2540be42c",
1000
+ "hash": "0xffec2c3aee1b7ce955120c950286eb3d4f758685e440855a220ff341d6d665d7",
1001
+ "input": "0x",
1002
+ "maxFeePerGas": "0x4a817c800",
1003
+ "maxPriorityFeePerGas": "0x2540be400",
1004
+ "nonce": "0x0",
1005
+ "r": "0xa95419f113415ccb9d7f0041e6989d09e031dc21a1605849edaddd72cff286ea",
1006
+ "s": "0x56b4faf46d2b07c294c4efa2db09e669a50b018d51b94529987d53ff011e09ee",
1007
+ "to": "0x0000000000000000000000000000000000000000",
1008
+ "transactionIndex": "0x0",
1009
+ "type": "0x77",
1010
+ "v": "0x1",
1011
+ "value": "0xde0b6b3a7640000",
1012
+ "yParity": "0x1",
1013
+ }
1014
+ `)
1015
+ }
1016
+
1017
+ const { blockNumber, blockHash, ...rest } = receipt
1018
+
1019
+ expect(blockNumber).toBeDefined()
1020
+ expect(blockHash).toBeDefined()
1021
+ expect(rest).toMatchInlineSnapshot(`
1022
+ {
1023
+ "contractAddress": null,
1024
+ "cumulativeGasUsed": "0x5208",
1025
+ "effectiveGasPrice": "0x2540be42c",
1026
+ "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
1027
+ "gasUsed": "0x5208",
1028
+ "logs": [],
1029
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
1030
+ "status": "0x0",
1031
+ "to": "0x0000000000000000000000000000000000000000",
1032
+ "transactionHash": "0xffec2c3aee1b7ce955120c950286eb3d4f758685e440855a220ff341d6d665d7",
1033
+ "transactionIndex": "0x0",
1034
+ "type": "0x77",
1035
+ }
1036
+ `)
1037
+ })
1038
+
1039
+ test('behavior: feePayerSignature (user → feePayer)', async () => {
1040
+ const feePayer = {
1041
+ address: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
1042
+ privateKey:
1043
+ '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
1044
+ } as const
1045
+ const sender = {
1046
+ address: '0x0a275bEE91B39092Dfd57089Dee0EB0539020B90',
1047
+ privateKey:
1048
+ '0xfe24691eff5297c76e847dc78a8966b96cf65a44140b9a0d3f5100ce71d74a59',
1049
+ } as const
1050
+
1051
+ const transport = RpcTransport.fromHttp('http://localhost:3000')
1052
+
1053
+ const nonce = await transport.request({
1054
+ method: 'eth_getTransactionCount',
1055
+ params: [sender.address, 'pending'],
1056
+ })
1057
+
1058
+ const transaction = TransactionEnvelopeFeeToken.from({
1059
+ chainId: 1337,
1060
+ feePayerSignature: null,
1061
+ nonce: BigInt(nonce),
1062
+ gas: 21000n,
1063
+ to: '0x0000000000000000000000000000000000000000',
1064
+ value: 0n,
1065
+ maxFeePerGas: Hex.toBigInt('0x59'),
1066
+ maxPriorityFeePerGas: Hex.toBigInt('0x59'),
1067
+ })
1068
+
1069
+ const signature = Secp256k1.sign({
1070
+ payload: TransactionEnvelopeFeeToken.getSignPayload(transaction),
1071
+ // unfunded PK
1072
+ privateKey: sender.privateKey,
1073
+ })
1074
+
1075
+ const transaction_signed = TransactionEnvelopeFeeToken.from(transaction, {
1076
+ signature,
1077
+ })
1078
+
1079
+ const feePayerSignature = Secp256k1.sign({
1080
+ payload: TransactionEnvelopeFeeToken.getFeePayerSignPayload(
1081
+ transaction_signed,
1082
+ { sender: sender.address },
1083
+ ),
1084
+ privateKey: feePayer.privateKey,
1085
+ })
1086
+
1087
+ const serialized_signed = TransactionEnvelopeFeeToken.serialize(
1088
+ transaction_signed,
1089
+ {
1090
+ feePayerSignature,
1091
+ },
1092
+ )
1093
+
1094
+ const hash = await transport.request({
1095
+ method: 'eth_sendRawTransaction',
1096
+ params: [serialized_signed],
1097
+ })
1098
+
1099
+ expect(hash).toBeDefined()
1100
+ {
1101
+ const response = await transport.request({
1102
+ method: 'eth_getTransactionByHash',
1103
+ params: [hash],
1104
+ })
1105
+
1106
+ expect(response).toMatchInlineSnapshot(`
1107
+ {
1108
+ "accessList": [],
1109
+ "authorizationList": [],
1110
+ "blockHash": null,
1111
+ "blockNumber": null,
1112
+ "chainId": "0x539",
1113
+ "feePayerSignature": {
1114
+ "r": "0xffc8a1a6c9e4f8cb7e796ff0521d9172d29c87664365f25bb3fb599aee562040",
1115
+ "s": "0x47f505ea8cf3ad98dc20864119c160bf3c5f706c13e0f13283d9dec869b3cb8e",
1116
+ "v": "0x1",
1117
+ "yParity": "0x1",
1118
+ },
1119
+ "feeToken": null,
1120
+ "from": "0x0a275bee91b39092dfd57089dee0eb0539020b90",
1121
+ "gas": "0x5208",
1122
+ "gasPrice": "0x59",
1123
+ "hash": "0x872e61efb1d53706b955f7a0dfc14de5a060bffb1237b32ca7ace529329d3ffd",
1124
+ "input": "0x",
1125
+ "maxFeePerGas": "0x59",
1126
+ "maxPriorityFeePerGas": "0x59",
1127
+ "nonce": "0x0",
1128
+ "r": "0xefe01851e57c9cb6401daced9013773302816a3c9280153c3ec1839650ac24d8",
1129
+ "s": "0xa06f8aa8dce5f90da82f528c9c9e7f427ee40896427509f9f734b6b63fb59bc",
1130
+ "to": "0x0000000000000000000000000000000000000000",
1131
+ "transactionIndex": null,
1132
+ "type": "0x77",
1133
+ "v": "0x1",
1134
+ "value": "0x0",
1135
+ "yParity": "0x1",
1136
+ }
1137
+ `)
1138
+ }
1139
+
1140
+ // Wait for inclusion.
1141
+ await setTimeout(4)
1142
+
1143
+ {
1144
+ const response = await transport.request({
1145
+ method: 'eth_getTransactionByHash',
1146
+ params: [hash],
1147
+ })
1148
+ if (!response) throw new Error()
1149
+
1150
+ const { blockNumber, blockHash, ...rest } = response
1151
+
1152
+ expect(blockNumber).toBeDefined()
1153
+ expect(blockHash).toBeDefined()
1154
+ expect(rest).toMatchInlineSnapshot(`
1155
+ {
1156
+ "accessList": [],
1157
+ "authorizationList": [],
1158
+ "chainId": "0x539",
1159
+ "feePayerSignature": {
1160
+ "r": "0xffc8a1a6c9e4f8cb7e796ff0521d9172d29c87664365f25bb3fb599aee562040",
1161
+ "s": "0x47f505ea8cf3ad98dc20864119c160bf3c5f706c13e0f13283d9dec869b3cb8e",
1162
+ "v": "0x1",
1163
+ "yParity": "0x1",
1164
+ },
1165
+ "feeToken": null,
1166
+ "from": "0x0a275bee91b39092dfd57089dee0eb0539020b90",
1167
+ "gas": "0x5208",
1168
+ "gasPrice": "0x59",
1169
+ "hash": "0x872e61efb1d53706b955f7a0dfc14de5a060bffb1237b32ca7ace529329d3ffd",
1170
+ "input": "0x",
1171
+ "maxFeePerGas": "0x59",
1172
+ "maxPriorityFeePerGas": "0x59",
1173
+ "nonce": "0x0",
1174
+ "r": "0xefe01851e57c9cb6401daced9013773302816a3c9280153c3ec1839650ac24d8",
1175
+ "s": "0xa06f8aa8dce5f90da82f528c9c9e7f427ee40896427509f9f734b6b63fb59bc",
1176
+ "to": "0x0000000000000000000000000000000000000000",
1177
+ "transactionIndex": "0x0",
1178
+ "type": "0x77",
1179
+ "v": "0x1",
1180
+ "value": "0x0",
1181
+ "yParity": "0x1",
1182
+ }
1183
+ `)
1184
+ }
1185
+
1186
+ {
1187
+ const receipt = await transport.request({
1188
+ method: 'eth_getTransactionReceipt',
1189
+ params: [hash],
1190
+ })
1191
+ if (!receipt) throw new Error()
1192
+
1193
+ const { blockNumber, blockHash, ...rest } = receipt
1194
+
1195
+ expect(blockNumber).toBeDefined()
1196
+ expect(blockHash).toBeDefined()
1197
+ expect(rest).toMatchInlineSnapshot(`
1198
+ {
1199
+ "contractAddress": null,
1200
+ "cumulativeGasUsed": "0x5208",
1201
+ "effectiveGasPrice": "0x59",
1202
+ "from": "0x0a275bee91b39092dfd57089dee0eb0539020b90",
1203
+ "gasUsed": "0x5208",
1204
+ "logs": [],
1205
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
1206
+ "status": "0x1",
1207
+ "to": "0x0000000000000000000000000000000000000000",
1208
+ "transactionHash": "0x872e61efb1d53706b955f7a0dfc14de5a060bffb1237b32ca7ace529329d3ffd",
1209
+ "transactionIndex": "0x0",
1210
+ "type": "0x77",
1211
+ }
1212
+ `)
1213
+ }
1214
+ })
1215
+ })