tempo.ts 0.6.2 → 0.7.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 (91) hide show
  1. package/CHANGELOG.md +134 -0
  2. package/README.md +6 -2
  3. package/dist/ox/Transaction.js +1 -1
  4. package/dist/ox/Transaction.js.map +1 -1
  5. package/dist/server/Handler.d.ts +346 -0
  6. package/dist/server/Handler.d.ts.map +1 -0
  7. package/dist/server/Handler.js +441 -0
  8. package/dist/server/Handler.js.map +1 -0
  9. package/dist/server/Kv.d.ts +16 -0
  10. package/dist/server/Kv.d.ts.map +1 -0
  11. package/dist/server/Kv.js +25 -0
  12. package/dist/server/Kv.js.map +1 -0
  13. package/dist/server/index.d.ts +3 -0
  14. package/dist/server/index.d.ts.map +1 -0
  15. package/dist/server/index.js +3 -0
  16. package/dist/server/index.js.map +1 -0
  17. package/dist/server/internal/requestListener.d.ts +124 -0
  18. package/dist/server/internal/requestListener.d.ts.map +1 -0
  19. package/dist/server/internal/requestListener.js +174 -0
  20. package/dist/server/internal/requestListener.js.map +1 -0
  21. package/dist/viem/Actions/amm.d.ts +9 -41
  22. package/dist/viem/Actions/amm.d.ts.map +1 -1
  23. package/dist/viem/Actions/amm.js +15 -26
  24. package/dist/viem/Actions/amm.js.map +1 -1
  25. package/dist/viem/Actions/reward.d.ts +0 -1067
  26. package/dist/viem/Actions/reward.d.ts.map +1 -1
  27. package/dist/viem/Actions/reward.js +4 -212
  28. package/dist/viem/Actions/reward.js.map +1 -1
  29. package/dist/viem/Decorator.d.ts +0 -263
  30. package/dist/viem/Decorator.d.ts.map +1 -1
  31. package/dist/viem/Decorator.js +0 -10
  32. package/dist/viem/Decorator.js.map +1 -1
  33. package/dist/viem/Storage.d.ts +23 -0
  34. package/dist/viem/Storage.d.ts.map +1 -0
  35. package/dist/viem/Storage.js +47 -0
  36. package/dist/viem/Storage.js.map +1 -0
  37. package/dist/viem/Transport.d.ts +10 -1
  38. package/dist/viem/Transport.d.ts.map +1 -1
  39. package/dist/viem/Transport.js +22 -3
  40. package/dist/viem/Transport.js.map +1 -1
  41. package/dist/viem/internal/utils.d.ts +6 -0
  42. package/dist/viem/internal/utils.d.ts.map +1 -1
  43. package/dist/viem/internal/utils.js +24 -0
  44. package/dist/viem/internal/utils.js.map +1 -1
  45. package/dist/wagmi/Actions/reward.d.ts +0 -110
  46. package/dist/wagmi/Actions/reward.d.ts.map +1 -1
  47. package/dist/wagmi/Actions/reward.js +0 -121
  48. package/dist/wagmi/Actions/reward.js.map +1 -1
  49. package/dist/wagmi/Connector.d.ts +6 -17
  50. package/dist/wagmi/Connector.d.ts.map +1 -1
  51. package/dist/wagmi/Connector.js +17 -43
  52. package/dist/wagmi/Connector.js.map +1 -1
  53. package/dist/wagmi/Hooks/reward.d.ts +0 -88
  54. package/dist/wagmi/Hooks/reward.d.ts.map +1 -1
  55. package/dist/wagmi/Hooks/reward.js +0 -103
  56. package/dist/wagmi/Hooks/reward.js.map +1 -1
  57. package/dist/wagmi/KeyManager.d.ts +57 -0
  58. package/dist/wagmi/KeyManager.d.ts.map +1 -0
  59. package/dist/wagmi/KeyManager.js +101 -0
  60. package/dist/wagmi/KeyManager.js.map +1 -0
  61. package/dist/wagmi/index.d.ts +1 -0
  62. package/dist/wagmi/index.d.ts.map +1 -1
  63. package/dist/wagmi/index.js +1 -0
  64. package/dist/wagmi/index.js.map +1 -1
  65. package/package.json +8 -2
  66. package/src/ox/Transaction.ts +1 -1
  67. package/src/ox/e2e.test.ts +7 -0
  68. package/src/server/Handler.test.ts +566 -0
  69. package/src/server/Handler.ts +577 -0
  70. package/src/server/Kv.ts +40 -0
  71. package/src/server/index.ts +2 -0
  72. package/src/server/internal/requestListener.ts +285 -0
  73. package/src/viem/Actions/amm.test.ts +10 -284
  74. package/src/viem/Actions/amm.ts +32 -40
  75. package/src/viem/Actions/reward.test.ts +4 -212
  76. package/src/viem/Actions/reward.ts +4 -291
  77. package/src/viem/Decorator.ts +0 -294
  78. package/src/viem/Storage.ts +88 -0
  79. package/src/viem/Transport.ts +40 -2
  80. package/src/viem/e2e.test.ts +106 -3
  81. package/src/viem/internal/utils.ts +21 -0
  82. package/src/wagmi/Actions/amm.test.ts +7 -85
  83. package/src/wagmi/Actions/reward.test.ts +0 -99
  84. package/src/wagmi/Actions/reward.ts +0 -203
  85. package/src/wagmi/Connector.test.ts +4 -1
  86. package/src/wagmi/Connector.ts +24 -58
  87. package/src/wagmi/Hooks/amm.test.ts +8 -200
  88. package/src/wagmi/Hooks/reward.test.ts +1 -142
  89. package/src/wagmi/Hooks/reward.ts +0 -196
  90. package/src/wagmi/KeyManager.ts +159 -0
  91. package/src/wagmi/index.ts +1 -0
@@ -0,0 +1,566 @@
1
+ import express from 'express'
2
+ import { Hono } from 'hono'
3
+ import type { RpcRequest } from 'ox'
4
+ import * as Base64 from 'ox/Base64'
5
+ import * as Hex from 'ox/Hex'
6
+ import { http } from 'viem'
7
+ import { sendTransaction, sendTransactionSync } from 'viem/actions'
8
+ import {
9
+ afterAll,
10
+ afterEach,
11
+ beforeAll,
12
+ beforeEach,
13
+ describe,
14
+ expect,
15
+ test,
16
+ } from 'vitest'
17
+ import { createServer, type Server } from '../../test/server/utils.js'
18
+ import { accounts, getClient, transport } from '../../test/viem/config.js'
19
+ import { withFeePayer } from '../viem/Transport.js'
20
+ import * as Handler from './Handler.js'
21
+ import * as Kv from './Kv.js'
22
+
23
+ describe('from', () => {
24
+ test('default', () => {
25
+ const handler = Handler.from()
26
+ expect(handler).toBeDefined()
27
+ })
28
+
29
+ test('.fetch', async () => {
30
+ const handler = Handler.from()
31
+ handler.get('/test', () => new Response('test'))
32
+
33
+ const response = await handler.fetch(new Request('http://localhost/test'))
34
+ expect(response.status).toBe(200)
35
+ expect(await response.text()).toBe('test')
36
+ })
37
+
38
+ test('.listener', async () => {
39
+ const handler = Handler.from()
40
+ handler.get('/test', () =>
41
+ Response.json({ message: 'hello from listener' }),
42
+ )
43
+
44
+ const server = await createServer(handler.listener)
45
+
46
+ // Make a request to the server
47
+ const response = await fetch(`${server.url}/test`)
48
+ expect(response.status).toBe(200)
49
+
50
+ const data = await response.json()
51
+ expect(data).toEqual({ message: 'hello from listener' })
52
+ })
53
+
54
+ describe('integration', () => {
55
+ const handler = Handler.from()
56
+ handler.get('/foo', () => new Response('foo'))
57
+ handler.post('/bar', () => new Response('bar'))
58
+
59
+ test('hono', async () => {
60
+ const app = new Hono()
61
+ app.use((c) => handler.fetch(c.req.raw))
62
+
63
+ {
64
+ const response = await app.request('/foo')
65
+ expect(await response.text()).toBe('foo')
66
+ }
67
+
68
+ {
69
+ const response = await app.request('/bar', {
70
+ method: 'POST',
71
+ })
72
+ expect(await response.text()).toBe('bar')
73
+ }
74
+ })
75
+
76
+ test('node.js', async () => {
77
+ const server = await createServer(handler.listener)
78
+
79
+ {
80
+ const response = await fetch(`${server.url}/foo`)
81
+ expect(await response.text()).toBe('foo')
82
+ }
83
+
84
+ {
85
+ const response = await fetch(`${server.url}/bar`, {
86
+ method: 'POST',
87
+ })
88
+ expect(await response.text()).toBe('bar')
89
+ }
90
+
91
+ await server.closeAsync()
92
+ })
93
+
94
+ test('express', async () => {
95
+ const app = express()
96
+ app.use(handler.listener)
97
+
98
+ const server = await createServer(app)
99
+
100
+ {
101
+ const response = await fetch(`${server.url}/foo`)
102
+ expect(await response.text()).toBe('foo')
103
+ }
104
+
105
+ {
106
+ const response = await fetch(`${server.url}/bar`, {
107
+ method: 'POST',
108
+ })
109
+ expect(await response.text()).toBe('bar')
110
+ }
111
+
112
+ await server.closeAsync()
113
+ })
114
+ })
115
+ })
116
+
117
+ describe('keyManager', () => {
118
+ let kv: Kv.Kv
119
+ let handler: Handler.Handler
120
+
121
+ const credential = {
122
+ authenticatorAttachment: 'platform',
123
+ clientExtensionResults: { credProps: { rk: true } },
124
+ id: '8S-IAM1gQ2KsH3KNM517PRMdcFQ',
125
+ rawId: '8S-IAM1gQ2KsH3KNM517PRMdcFQ',
126
+ response: {
127
+ attestationObject:
128
+ 'o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViYSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NdAAAAAPv8MAcVTk7MjAtuAgVX170AFPEviADNYENirB9yjTOdez0THXBUpQECAyYgASFYIKjtG4n36vPprMvoOCi1rQC6h5EIBVxHoEW0xq1lQQZuIlgg_V4PIauVB6JcokNxrPCa2ueWylzbd8nqma5nLvg5Gs8',
129
+ authenticatorData:
130
+ 'SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NdAAAAAPv8MAcVTk7MjAtuAgVX170AFPEviADNYENirB9yjTOdez0THXBUpQECAyYgASFYIKjtG4n36vPprMvoOCi1rQC6h5EIBVxHoEW0xq1lQQZuIlgg_V4PIauVB6JcokNxrPCa2ueWylzbd8nqma5nLvg5Gs8',
131
+ clientDataJSON:
132
+ 'eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiOEhURkFrS1E2U2hTbW5MelRra3ZMYkhrXzByU3FteEtRbG9nTy1KdmtvbyIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6NTE3MyIsImNyb3NzT3JpZ2luIjpmYWxzZX0',
133
+ publicKey:
134
+ 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqO0biffq8-msy-g4KLWtALqHkQgFXEegRbTGrWVBBm79Xg8hq5UHolyiQ3Gs8Jra55bKXNt3yeqZrmcu-Dkazw',
135
+ publicKeyAlgorithm: -7,
136
+ transports: ['hybrid', 'internal'],
137
+ },
138
+ type: 'public-key',
139
+ }
140
+ const publicKey =
141
+ '0xa8ed1b89f7eaf3e9accbe83828b5ad00ba879108055c47a045b4c6ad6541066efd5e0f21ab9507a25ca24371acf09adae796ca5cdb77c9ea99ae672ef8391acf'
142
+
143
+ beforeEach(() => {
144
+ kv = Kv.memory()
145
+ handler = Handler.keyManager({ kv })
146
+ })
147
+
148
+ describe('GET /challenge', () => {
149
+ test('default', async () => {
150
+ const response = await handler.fetch(
151
+ new Request('http://localhost/challenge'),
152
+ )
153
+
154
+ expect(response.status).toBe(200)
155
+
156
+ const data =
157
+ (await response.json()) as Handler.keyManager.ChallengeResponse
158
+ expect(Hex.validate(data.challenge)).toBe(true)
159
+
160
+ // Verify challenge was stored in KV
161
+ const stored = await kv.get(`challenge:${data.challenge}`)
162
+ expect(stored).toBe('1')
163
+ })
164
+
165
+ test('behavior: rpId', async () => {
166
+ const handlerWithRpId = Handler.keyManager({
167
+ kv,
168
+ rp: 'example.com',
169
+ })
170
+
171
+ const response = await handlerWithRpId.fetch(
172
+ new Request('http://localhost/challenge'),
173
+ )
174
+
175
+ expect(response.status).toBe(200)
176
+
177
+ const data =
178
+ (await response.json()) as Handler.keyManager.ChallengeResponse
179
+ expect(data.rp).toEqual({
180
+ id: 'example.com',
181
+ name: 'example.com',
182
+ })
183
+ })
184
+ })
185
+
186
+ describe('GET /:id', () => {
187
+ test('default', async () => {
188
+ const credentialId = 'test-credential-id'
189
+ const publicKey = '0x1234567890abcdef'
190
+
191
+ await kv.set(`credential:${credentialId}`, publicKey)
192
+
193
+ const response = await handler.fetch(
194
+ new Request(`http://localhost/${credentialId}`),
195
+ )
196
+
197
+ expect(response.status).toBe(200)
198
+
199
+ const data = await response.json()
200
+ expect(data).toEqual({ publicKey })
201
+ })
202
+
203
+ test('behavior: returns 404 for non-existent credential', async () => {
204
+ const response = await handler.fetch(
205
+ new Request('http://localhost/non-existent'),
206
+ )
207
+
208
+ expect(response.status).toBe(404)
209
+ expect(await response.text()).toBe('Credential not found')
210
+ })
211
+ })
212
+
213
+ describe('POST /:id', () => {
214
+ test('default', async () => {
215
+ const credentialId = credential.id
216
+ const challenge = Base64.toHex(
217
+ JSON.parse(Base64.toString(credential.response.clientDataJSON))
218
+ .challenge,
219
+ )
220
+
221
+ // Store the challenge first
222
+ await kv.set(`challenge:${challenge}`, '1')
223
+
224
+ const response = await handler.fetch(
225
+ new Request(`http://localhost/${credentialId}`, {
226
+ method: 'POST',
227
+ headers: { 'Content-Type': 'application/json' },
228
+ body: JSON.stringify({
229
+ credential,
230
+ publicKey,
231
+ }),
232
+ }),
233
+ )
234
+
235
+ expect(response.status).toBe(204)
236
+
237
+ // Verify public key was stored
238
+ const storedPublicKey = await kv.get(`credential:${credentialId}`)
239
+ expect(storedPublicKey).toBe(publicKey)
240
+
241
+ // Verify challenge was consumed
242
+ const storedChallenge = await kv.get(`challenge:${challenge}`)
243
+ expect(storedChallenge).toBeUndefined()
244
+ })
245
+
246
+ test('behavior: returns 400 when credential is missing', async () => {
247
+ const response = await handler.fetch(
248
+ new Request('http://localhost/test-id', {
249
+ method: 'POST',
250
+ headers: { 'Content-Type': 'application/json' },
251
+ body: JSON.stringify({
252
+ publicKey,
253
+ }),
254
+ }),
255
+ )
256
+
257
+ expect(response.status).toBe(400)
258
+ const data = await response.json()
259
+ expect(data.error).toBe('Missing `credential`')
260
+ })
261
+
262
+ test('behavior: returns 400 when publicKey is missing', async () => {
263
+ const response = await handler.fetch(
264
+ new Request('http://localhost/test-id', {
265
+ method: 'POST',
266
+ headers: { 'Content-Type': 'application/json' },
267
+ body: JSON.stringify({
268
+ credential,
269
+ }),
270
+ }),
271
+ )
272
+
273
+ expect(response.status).toBe(400)
274
+ const data = await response.json()
275
+ expect(data.error).toBe('Missing `publicKey`')
276
+ })
277
+
278
+ test('behavior: returns 400 for invalid or expired challenge', async () => {
279
+ const response = await handler.fetch(
280
+ new Request(`http://localhost/${credential.id}`, {
281
+ method: 'POST',
282
+ headers: { 'Content-Type': 'application/json' },
283
+ body: JSON.stringify({
284
+ credential,
285
+ publicKey,
286
+ }),
287
+ }),
288
+ )
289
+
290
+ expect(response.status).toBe(400)
291
+ const data = await response.json()
292
+ expect(data.error).toBe('Invalid or expired `challenge`')
293
+ })
294
+
295
+ test('behavior: returns 400 for invalid clientDataJSON type', async () => {
296
+ const invalidCredential = {
297
+ ...credential,
298
+ response: {
299
+ ...credential.response,
300
+ clientDataJSON: Base64.fromString(
301
+ JSON.stringify({
302
+ type: 'webauthn.get', // Invalid type
303
+ challenge: '8HTFAkKQ6ShSmnLzTkkvLbHk_0rSqmxKQlogO-Jvkoo',
304
+ origin: 'http://localhost:5173',
305
+ crossOrigin: false,
306
+ }),
307
+ ),
308
+ },
309
+ }
310
+
311
+ const challenge = Base64.toHex(
312
+ JSON.parse(
313
+ Base64.toString(invalidCredential.response.clientDataJSON as string),
314
+ ).challenge,
315
+ )
316
+ await kv.set(`challenge:${challenge}`, '1')
317
+
318
+ const response = await handler.fetch(
319
+ new Request(`http://localhost/${credential.id}`, {
320
+ method: 'POST',
321
+ headers: { 'Content-Type': 'application/json' },
322
+ body: JSON.stringify({
323
+ credential: invalidCredential,
324
+ publicKey,
325
+ }),
326
+ }),
327
+ )
328
+
329
+ expect(response.status).toBe(400)
330
+ const data = await response.json()
331
+ expect(data.error).toBe('Invalid `clientDataJSON.type`')
332
+ })
333
+
334
+ test('behavior: returns 400 for invalid origin when rpId is set', async () => {
335
+ const handlerWithRpId = Handler.keyManager({
336
+ kv,
337
+ rp: 'example.com',
338
+ })
339
+
340
+ const challenge = Base64.toHex(
341
+ JSON.parse(Base64.toString(credential.response.clientDataJSON))
342
+ .challenge,
343
+ )
344
+ await kv.set(`challenge:${challenge}`, '1')
345
+
346
+ const response = await handlerWithRpId.fetch(
347
+ new Request(`http://localhost/${credential.id}`, {
348
+ method: 'POST',
349
+ headers: { 'Content-Type': 'application/json' },
350
+ body: JSON.stringify({
351
+ credential,
352
+ publicKey,
353
+ }),
354
+ }),
355
+ )
356
+
357
+ expect(response.status).toBe(400)
358
+ const data = await response.json()
359
+ expect(data.error).toBe('Invalid `clientDataJSON.origin`')
360
+ })
361
+
362
+ test('behavior: allows localhost origin when rpId includes localhost', async () => {
363
+ const handlerWithLocalhost = Handler.keyManager({
364
+ kv,
365
+ rp: 'localhost',
366
+ })
367
+
368
+ const challenge = Base64.toHex(
369
+ JSON.parse(Base64.toString(credential.response.clientDataJSON))
370
+ .challenge,
371
+ )
372
+ await kv.set(`challenge:${challenge}`, '1')
373
+
374
+ const response = await handlerWithLocalhost.fetch(
375
+ new Request(`http://localhost/${credential.id}`, {
376
+ method: 'POST',
377
+ headers: { 'Content-Type': 'application/json' },
378
+ body: JSON.stringify({
379
+ credential,
380
+ publicKey,
381
+ }),
382
+ }),
383
+ )
384
+
385
+ expect(response.status).toBe(204)
386
+ })
387
+
388
+ test('behavior: returns 400 when user not present flag is not set', async () => {
389
+ // Create credential with UP flag not set (bit 0 = 0)
390
+ const authenticatorDataBytes = Base64.toBytes(
391
+ credential.response.authenticatorData,
392
+ )
393
+ // Clear the UP flag (bit 0) in byte 32
394
+ authenticatorDataBytes[32] = authenticatorDataBytes[32]! & ~0x01
395
+
396
+ const invalidCredential = {
397
+ ...credential,
398
+ response: {
399
+ ...credential.response,
400
+ authenticatorData: Base64.fromBytes(authenticatorDataBytes),
401
+ },
402
+ }
403
+
404
+ const challenge = Base64.toHex(
405
+ JSON.parse(Base64.toString(credential.response.clientDataJSON))
406
+ .challenge,
407
+ )
408
+ await kv.set(`challenge:${challenge}`, '1')
409
+
410
+ const response = await handler.fetch(
411
+ new Request(`http://localhost/${credential.id}`, {
412
+ method: 'POST',
413
+ headers: { 'Content-Type': 'application/json' },
414
+ body: JSON.stringify({
415
+ credential: invalidCredential,
416
+ publicKey,
417
+ }),
418
+ }),
419
+ )
420
+
421
+ expect(response.status).toBe(400)
422
+ const data = await response.json()
423
+ expect(data.error).toBe('User not present')
424
+ })
425
+ })
426
+ })
427
+
428
+ describe('feePayer', () => {
429
+ const userAccount = accounts[9]!
430
+ const feePayerAccount = accounts[0]!
431
+
432
+ let server: Server
433
+ let requests: RpcRequest.RpcRequest[] = []
434
+
435
+ beforeAll(async () => {
436
+ server = await createServer(
437
+ Handler.feePayer({
438
+ account: feePayerAccount,
439
+ client: getClient(),
440
+ onRequest: async (request) => {
441
+ requests.push(request)
442
+ },
443
+ }).listener,
444
+ )
445
+ })
446
+
447
+ afterAll(() => {
448
+ server.close()
449
+ process.on('SIGINT', () => {
450
+ server.close()
451
+ process.exit(0)
452
+ })
453
+ process.on('SIGTERM', () => {
454
+ server.close()
455
+ process.exit(0)
456
+ })
457
+ })
458
+
459
+ afterEach(() => {
460
+ requests = []
461
+ })
462
+
463
+ describe('POST /', () => {
464
+ test('behavior: eth_signRawTransaction', async () => {
465
+ const client = getClient({
466
+ account: userAccount,
467
+ transport: withFeePayer(transport, http(server.url)),
468
+ })
469
+
470
+ await sendTransaction(client, {
471
+ feePayer: true,
472
+ to: '0x0000000000000000000000000000000000000000',
473
+ })
474
+
475
+ expect(
476
+ requests.map(({ method, params }) => [method, params]),
477
+ ).toMatchInlineSnapshot(`
478
+ [
479
+ [
480
+ "eth_signRawTransaction",
481
+ [
482
+ "0x76f871820539808502cb417800825d82d8d79400000000000000000000000000000000000000008080c0808080808000c0b841b907983eed3ef10ace951150a92d4281dc4879db229ec52da8dc1a85370f41255d414cc614dd25feab2b3c741006f6049c50bceb1f1f73cf2c574a956bd62b031c9ac4fDC8e5D72AaADE30F9Ff52D392D60c68A64afeefeefeefee",
483
+ ],
484
+ ],
485
+ ]
486
+ `)
487
+ })
488
+
489
+ test('behavior: eth_sendRawTransaction', async () => {
490
+ const client = getClient({
491
+ account: userAccount,
492
+ transport: withFeePayer(transport, http(server.url), {
493
+ policy: 'sign-and-broadcast',
494
+ }),
495
+ })
496
+
497
+ await sendTransaction(client, {
498
+ feePayer: true,
499
+ to: '0x0000000000000000000000000000000000000000',
500
+ })
501
+
502
+ expect(
503
+ requests.map(({ method, params }) => [method, params]),
504
+ ).toMatchInlineSnapshot(`
505
+ [
506
+ [
507
+ "eth_sendRawTransaction",
508
+ [
509
+ "0x76f871820539808502cb417800825d82d8d79400000000000000000000000000000000000000008080c0800180808000c0b841c5a62964b827e6d9b703beec996c496dec13afda19776b9aedce1125d75cf3220815c2d6466a8100d56e8a5e91e6fb15600143effc7cfb2d5f6f48352361cd711c9ac4fDC8e5D72AaADE30F9Ff52D392D60c68A64afeefeefeefee",
510
+ ],
511
+ ],
512
+ ]
513
+ `)
514
+ })
515
+
516
+ test('behavior: eth_sendRawTransactionSync', async () => {
517
+ const client = getClient({
518
+ account: userAccount,
519
+ transport: withFeePayer(transport, http(server.url), {
520
+ policy: 'sign-and-broadcast',
521
+ }),
522
+ })
523
+
524
+ await sendTransactionSync(client, {
525
+ feePayer: true,
526
+ to: '0x0000000000000000000000000000000000000000',
527
+ })
528
+
529
+ expect(
530
+ requests.map(({ method, params }) => [method, params]),
531
+ ).toMatchInlineSnapshot(`
532
+ [
533
+ [
534
+ "eth_sendRawTransactionSync",
535
+ [
536
+ "0x76f871820539808502cb417800825d82d8d79400000000000000000000000000000000000000008080c0800280808000c0b841f1b96061f66f044829e4237e881924e7d2efcff378df03212451831af5aedbe6262a8f2001ee0ffc4dab7211bc324c42298da463e01ce05402f5c16f884a608b1c9ac4fDC8e5D72AaADE30F9Ff52D392D60c68A64afeefeefeefee",
537
+ ],
538
+ ],
539
+ ]
540
+ `)
541
+ })
542
+
543
+ test('behavior: unsupported method', async () => {
544
+ await expect(
545
+ fetch(server.url, {
546
+ method: 'POST',
547
+ body: JSON.stringify({
548
+ jsonrpc: '2.0',
549
+ id: 1,
550
+ method: 'eth_chainId',
551
+ }),
552
+ }).then((response) => response.json()),
553
+ ).resolves.toMatchInlineSnapshot(`
554
+ {
555
+ "error": {
556
+ "code": -32004,
557
+ "name": "RpcResponse.MethodNotSupportedError",
558
+ "stack": "",
559
+ },
560
+ "id": 1,
561
+ "jsonrpc": "2.0",
562
+ }
563
+ `)
564
+ })
565
+ })
566
+ })