tempo.ts 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/CHANGELOG.md +128 -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 +51 -1245
  22. package/dist/viem/Actions/amm.d.ts.map +1 -1
  23. package/dist/viem/Actions/amm.js +15 -478
  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/amm.d.ts +0 -225
  46. package/dist/wagmi/Actions/amm.d.ts.map +1 -1
  47. package/dist/wagmi/Actions/amm.js +0 -248
  48. package/dist/wagmi/Actions/amm.js.map +1 -1
  49. package/dist/wagmi/Actions/reward.d.ts +0 -110
  50. package/dist/wagmi/Actions/reward.d.ts.map +1 -1
  51. package/dist/wagmi/Actions/reward.js +0 -121
  52. package/dist/wagmi/Actions/reward.js.map +1 -1
  53. package/dist/wagmi/Connector.d.ts +6 -17
  54. package/dist/wagmi/Connector.d.ts.map +1 -1
  55. package/dist/wagmi/Connector.js +17 -43
  56. package/dist/wagmi/Connector.js.map +1 -1
  57. package/dist/wagmi/Hooks/amm.d.ts +0 -236
  58. package/dist/wagmi/Hooks/amm.d.ts.map +1 -1
  59. package/dist/wagmi/Hooks/amm.js +0 -285
  60. package/dist/wagmi/Hooks/amm.js.map +1 -1
  61. package/dist/wagmi/Hooks/reward.d.ts +0 -88
  62. package/dist/wagmi/Hooks/reward.d.ts.map +1 -1
  63. package/dist/wagmi/Hooks/reward.js +0 -103
  64. package/dist/wagmi/Hooks/reward.js.map +1 -1
  65. package/dist/wagmi/KeyManager.d.ts +57 -0
  66. package/dist/wagmi/KeyManager.d.ts.map +1 -0
  67. package/dist/wagmi/KeyManager.js +101 -0
  68. package/dist/wagmi/KeyManager.js.map +1 -0
  69. package/dist/wagmi/index.d.ts +1 -0
  70. package/dist/wagmi/index.d.ts.map +1 -1
  71. package/dist/wagmi/index.js +1 -0
  72. package/dist/wagmi/index.js.map +1 -1
  73. package/package.json +8 -2
  74. package/src/ox/Transaction.ts +1 -1
  75. package/src/ox/e2e.test.ts +7 -0
  76. package/src/server/Handler.test.ts +566 -0
  77. package/src/server/Handler.ts +577 -0
  78. package/src/server/Kv.ts +40 -0
  79. package/src/server/index.ts +2 -0
  80. package/src/server/internal/requestListener.ts +285 -0
  81. package/src/viem/Actions/amm.test.ts +10 -284
  82. package/src/viem/Actions/amm.ts +88 -768
  83. package/src/viem/Actions/reward.test.ts +4 -212
  84. package/src/viem/Actions/reward.ts +4 -291
  85. package/src/viem/Decorator.ts +0 -294
  86. package/src/viem/Storage.ts +88 -0
  87. package/src/viem/Transport.ts +40 -2
  88. package/src/viem/e2e.test.ts +106 -3
  89. package/src/viem/internal/utils.ts +21 -0
  90. package/src/wagmi/Actions/amm.test.ts +7 -85
  91. package/src/wagmi/Actions/amm.ts +0 -346
  92. package/src/wagmi/Actions/reward.test.ts +0 -99
  93. package/src/wagmi/Actions/reward.ts +0 -203
  94. package/src/wagmi/Connector.test.ts +4 -1
  95. package/src/wagmi/Connector.ts +24 -58
  96. package/src/wagmi/Hooks/amm.test.ts +8 -200
  97. package/src/wagmi/Hooks/amm.ts +0 -443
  98. package/src/wagmi/Hooks/reward.test.ts +1 -142
  99. package/src/wagmi/Hooks/reward.ts +0 -196
  100. package/src/wagmi/KeyManager.ts +159 -0
  101. 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
+ })