mppx 0.8.11 → 0.8.13
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.
- package/CHANGELOG.md +25 -0
- package/README.md +16 -1
- package/dist/cli/account.d.ts +88 -0
- package/dist/cli/account.d.ts.map +1 -1
- package/dist/cli/account.js +39 -9
- package/dist/cli/account.js.map +1 -1
- package/dist/cli/cli.d.ts +2 -1
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +59 -2
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/sessions/Manager.d.ts +31 -0
- package/dist/cli/sessions/Manager.d.ts.map +1 -0
- package/dist/cli/sessions/Manager.js +56 -0
- package/dist/cli/sessions/Manager.js.map +1 -0
- package/dist/cli/sessions/commands.d.ts +36 -0
- package/dist/cli/sessions/commands.d.ts.map +1 -0
- package/dist/cli/sessions/commands.js +407 -0
- package/dist/cli/sessions/commands.js.map +1 -0
- package/dist/cli/sessions/request.d.ts +35 -0
- package/dist/cli/sessions/request.d.ts.map +1 -0
- package/dist/cli/sessions/request.js +271 -0
- package/dist/cli/sessions/request.js.map +1 -0
- package/dist/cli/sessions/store.d.ts +134 -0
- package/dist/cli/sessions/store.d.ts.map +1 -0
- package/dist/cli/sessions/store.js +723 -0
- package/dist/cli/sessions/store.js.map +1 -0
- package/dist/cli/utils.d.ts +2 -0
- package/dist/cli/utils.d.ts.map +1 -1
- package/dist/cli/utils.js +12 -0
- package/dist/cli/utils.js.map +1 -1
- package/dist/cli/validate/index.d.ts.map +1 -1
- package/dist/cli/validate/index.js +12 -6
- package/dist/cli/validate/index.js.map +1 -1
- package/dist/cli/validate/messages.d.ts +2 -0
- package/dist/cli/validate/messages.d.ts.map +1 -0
- package/dist/cli/validate/messages.js +8 -0
- package/dist/cli/validate/messages.js.map +1 -0
- package/dist/client/internal/Fetch.d.ts.map +1 -1
- package/dist/client/internal/Fetch.js +35 -6
- package/dist/client/internal/Fetch.js.map +1 -1
- package/dist/client/internal/MethodResponse.d.ts +23 -0
- package/dist/client/internal/MethodResponse.d.ts.map +1 -0
- package/dist/client/internal/MethodResponse.js +15 -0
- package/dist/client/internal/MethodResponse.js.map +1 -0
- package/dist/client/node.d.ts +29 -0
- package/dist/client/node.d.ts.map +1 -0
- package/dist/client/node.js +177 -0
- package/dist/client/node.js.map +1 -0
- package/dist/internal/types.d.ts +24 -0
- package/dist/internal/types.d.ts.map +1 -1
- package/dist/stripe/server/Charge.d.ts +8 -4
- package/dist/stripe/server/Charge.d.ts.map +1 -1
- package/dist/stripe/server/Charge.js.map +1 -1
- package/dist/stripe/server/internal/html.gen.d.ts +1 -1
- package/dist/stripe/server/internal/html.gen.d.ts.map +1 -1
- package/dist/stripe/server/internal/html.gen.js +1 -1
- package/dist/stripe/server/internal/html.gen.js.map +1 -1
- package/dist/tempo/client/Methods.d.ts +1 -1
- package/dist/tempo/internal/types.d.ts +10 -1
- package/dist/tempo/internal/types.d.ts.map +1 -1
- package/dist/tempo/legacy/client/ChannelOps.d.ts +2 -0
- package/dist/tempo/legacy/client/ChannelOps.d.ts.map +1 -1
- package/dist/tempo/legacy/client/ChannelOps.js +4 -0
- package/dist/tempo/legacy/client/ChannelOps.js.map +1 -1
- package/dist/tempo/legacy/client/Session.d.ts +2 -2
- package/dist/tempo/legacy/client/Session.d.ts.map +1 -1
- package/dist/tempo/legacy/client/Session.js +7 -0
- package/dist/tempo/legacy/client/Session.js.map +1 -1
- package/dist/tempo/legacy/server/Session.d.ts +3 -3
- package/dist/tempo/legacy/server/Session.d.ts.map +1 -1
- package/dist/tempo/legacy/server/Session.js.map +1 -1
- package/dist/tempo/server/Charge.d.ts +3 -2
- package/dist/tempo/server/Charge.d.ts.map +1 -1
- package/dist/tempo/server/Charge.js.map +1 -1
- package/dist/tempo/server/Methods.d.ts +2 -23
- package/dist/tempo/server/Methods.d.ts.map +1 -1
- package/dist/tempo/server/Methods.js +2 -2
- package/dist/tempo/server/Methods.js.map +1 -1
- package/dist/tempo/server/Subscription.d.ts +3 -2
- package/dist/tempo/server/Subscription.d.ts.map +1 -1
- package/dist/tempo/server/Subscription.js.map +1 -1
- package/dist/tempo/server/index.d.ts +1 -1
- package/dist/tempo/server/index.d.ts.map +1 -1
- package/dist/tempo/server/index.js.map +1 -1
- package/dist/tempo/server/internal/html.gen.d.ts +1 -1
- package/dist/tempo/server/internal/html.gen.d.ts.map +1 -1
- package/dist/tempo/server/internal/html.gen.js +1 -1
- package/dist/tempo/server/internal/html.gen.js.map +1 -1
- package/dist/tempo/session/Snapshot.d.ts +9 -0
- package/dist/tempo/session/Snapshot.d.ts.map +1 -1
- package/dist/tempo/session/Snapshot.js +6 -0
- package/dist/tempo/session/Snapshot.js.map +1 -1
- package/dist/tempo/session/client/ChannelOps.d.ts.map +1 -1
- package/dist/tempo/session/client/ChannelOps.js +11 -2
- package/dist/tempo/session/client/ChannelOps.js.map +1 -1
- package/dist/tempo/session/client/CredentialState.d.ts +21 -1
- package/dist/tempo/session/client/CredentialState.d.ts.map +1 -1
- package/dist/tempo/session/client/CredentialState.js +66 -1
- package/dist/tempo/session/client/CredentialState.js.map +1 -1
- package/dist/tempo/session/client/Session.d.ts +1 -1
- package/dist/tempo/session/client/Session.d.ts.map +1 -1
- package/dist/tempo/session/client/Session.js +53 -1
- package/dist/tempo/session/client/Session.js.map +1 -1
- package/dist/tempo/session/client/SessionManager.d.ts +2 -2
- package/dist/tempo/session/client/SessionManager.d.ts.map +1 -1
- package/dist/tempo/session/client/SessionManager.js +130 -30
- package/dist/tempo/session/client/SessionManager.js.map +1 -1
- package/dist/tempo/session/client/Transports.d.ts +21 -2
- package/dist/tempo/session/client/Transports.d.ts.map +1 -1
- package/dist/tempo/session/client/Transports.js +129 -18
- package/dist/tempo/session/client/Transports.js.map +1 -1
- package/dist/tempo/session/client/internal/SessionManager.d.ts +19 -0
- package/dist/tempo/session/client/internal/SessionManager.d.ts.map +1 -0
- package/dist/tempo/session/client/internal/SessionManager.js +13 -0
- package/dist/tempo/session/client/internal/SessionManager.js.map +1 -0
- package/dist/tempo/session/precompile/Chain.js +13 -4
- package/dist/tempo/session/precompile/Chain.js.map +1 -1
- package/dist/tempo/session/server/CredentialVerification.d.ts +3 -0
- package/dist/tempo/session/server/CredentialVerification.d.ts.map +1 -1
- package/dist/tempo/session/server/CredentialVerification.js +14 -0
- package/dist/tempo/session/server/CredentialVerification.js.map +1 -1
- package/dist/tempo/session/server/MeteredStream.d.ts +7 -1
- package/dist/tempo/session/server/MeteredStream.d.ts.map +1 -1
- package/dist/tempo/session/server/MeteredStream.js +5 -4
- package/dist/tempo/session/server/MeteredStream.js.map +1 -1
- package/dist/tempo/session/server/RequestState.d.ts +5 -1
- package/dist/tempo/session/server/RequestState.d.ts.map +1 -1
- package/dist/tempo/session/server/RequestState.js +10 -4
- package/dist/tempo/session/server/RequestState.js.map +1 -1
- package/dist/tempo/session/server/Session.d.ts +21 -6
- package/dist/tempo/session/server/Session.d.ts.map +1 -1
- package/dist/tempo/session/server/Session.js +4 -1
- package/dist/tempo/session/server/Session.js.map +1 -1
- package/dist/tempo/session/server/Settlement.d.ts +20 -0
- package/dist/tempo/session/server/Settlement.d.ts.map +1 -1
- package/dist/tempo/session/server/Settlement.js +20 -0
- package/dist/tempo/session/server/Settlement.js.map +1 -1
- package/dist/tempo/session/server/Ws.d.ts +2 -0
- package/dist/tempo/session/server/Ws.d.ts.map +1 -1
- package/dist/tempo/session/server/Ws.js.map +1 -1
- package/dist/tempo/session/server/index.d.ts +1 -1
- package/dist/tempo/session/server/index.d.ts.map +1 -1
- package/dist/tempo/subscription/KeyAuthorization.d.ts +21 -21
- package/dist/validation/core.d.ts.map +1 -1
- package/dist/validation/core.js +6 -2
- package/dist/validation/core.js.map +1 -1
- package/dist/viem/Client.d.ts.map +1 -1
- package/dist/viem/Client.js +24 -17
- package/dist/viem/Client.js.map +1 -1
- package/package.json +6 -1
- package/src/cli/account.ts +45 -10
- package/src/cli/cli.test.ts +165 -70
- package/src/cli/cli.ts +67 -2
- package/src/cli/mcp.test.ts +11 -0
- package/src/cli/sessions/Manager.test.ts +249 -0
- package/src/cli/sessions/Manager.ts +93 -0
- package/src/cli/sessions/commands.ts +444 -0
- package/src/cli/sessions/request.test.ts +51 -0
- package/src/cli/sessions/request.ts +353 -0
- package/src/cli/sessions/store.test.ts +581 -0
- package/src/cli/sessions/store.ts +940 -0
- package/src/cli/utils.test.ts +23 -0
- package/src/cli/utils.ts +10 -0
- package/src/cli/validate/index.ts +12 -14
- package/src/cli/validate/messages.ts +7 -0
- package/src/cli/validate.test.ts +38 -5
- package/src/client/internal/Fetch.test.ts +29 -0
- package/src/client/internal/Fetch.ts +51 -6
- package/src/client/internal/MethodResponse.ts +39 -0
- package/src/client/node.test.ts +115 -0
- package/src/client/node.ts +247 -0
- package/src/internal/types.test-d.ts +21 -0
- package/src/internal/types.ts +33 -0
- package/src/server/Mppx.test-d.ts +42 -0
- package/src/stripe/Methods.test.ts +10 -0
- package/src/stripe/server/Charge.test-d.ts +66 -0
- package/src/stripe/server/Charge.ts +8 -5
- package/src/stripe/server/internal/html.gen.ts +1 -1
- package/src/tempo/internal/types.ts +13 -2
- package/src/tempo/legacy/client/ChannelOps.test.ts +21 -0
- package/src/tempo/legacy/client/ChannelOps.ts +7 -0
- package/src/tempo/legacy/client/Session.test.ts +102 -1
- package/src/tempo/legacy/client/Session.ts +10 -0
- package/src/tempo/legacy/server/Defaults.test-d.ts +9 -0
- package/src/tempo/legacy/server/Session.ts +6 -5
- package/src/tempo/server/Charge.ts +6 -4
- package/src/tempo/server/Methods.ts +5 -4
- package/src/tempo/server/Subscription.ts +6 -4
- package/src/tempo/server/index.ts +5 -1
- package/src/tempo/server/internal/html.gen.ts +1 -1
- package/src/tempo/session/README.md +201 -0
- package/src/tempo/session/Snapshot.ts +21 -0
- package/src/tempo/session/client/ChannelOps.test.ts +40 -0
- package/src/tempo/session/client/ChannelOps.ts +12 -2
- package/src/tempo/session/client/CredentialState.test.ts +2 -2
- package/src/tempo/session/client/CredentialState.ts +103 -1
- package/src/tempo/session/client/Session.test.ts +86 -4
- package/src/tempo/session/client/Session.ts +73 -1
- package/src/tempo/session/client/SessionManager.test.ts +137 -4
- package/src/tempo/session/client/SessionManager.ts +141 -33
- package/src/tempo/session/client/Transports.test.ts +225 -8
- package/src/tempo/session/client/Transports.ts +156 -21
- package/src/tempo/session/client/internal/SessionManager.ts +36 -0
- package/src/tempo/session/precompile/Chain.test.ts +26 -0
- package/src/tempo/session/precompile/Chain.ts +17 -4
- package/src/tempo/session/server/CredentialVerification.ts +18 -1
- package/src/tempo/session/server/MeteredStream.ts +12 -5
- package/src/tempo/session/server/RequestState.test.ts +39 -3
- package/src/tempo/session/server/RequestState.ts +13 -6
- package/src/tempo/session/server/Session.test.ts +461 -0
- package/src/tempo/session/server/Session.ts +31 -8
- package/src/tempo/session/server/Settlement.ts +45 -0
- package/src/tempo/session/server/Ws.test.ts +113 -0
- package/src/tempo/session/server/Ws.ts +2 -0
- package/src/tempo/session/server/index.ts +2 -0
- package/src/validation/core.ts +7 -1
- package/src/viem/Client.test.ts +44 -1
- package/src/viem/Client.ts +31 -17
|
@@ -408,6 +408,119 @@ describe('isows', () => {
|
|
|
408
408
|
expect(channel?.units).toBe(0)
|
|
409
409
|
})
|
|
410
410
|
|
|
411
|
+
test('aborts a blocked application generator before sending close-ready', async () => {
|
|
412
|
+
const socket = new MockSocket()
|
|
413
|
+
const store = memoryChannelStore()
|
|
414
|
+
await seedChannel(store, 1n)
|
|
415
|
+
let generatorAborted = false
|
|
416
|
+
|
|
417
|
+
await Ws.serve({
|
|
418
|
+
socket,
|
|
419
|
+
store,
|
|
420
|
+
url: 'ws://example.test/stream',
|
|
421
|
+
route: async () => ({
|
|
422
|
+
status: 200,
|
|
423
|
+
withReceipt(response = new Response(null, { status: 204 })) {
|
|
424
|
+
response.headers.set(
|
|
425
|
+
'Payment-Receipt',
|
|
426
|
+
serializeSessionReceipt(
|
|
427
|
+
createSessionReceipt({
|
|
428
|
+
challengeId: challenge.id,
|
|
429
|
+
channelId,
|
|
430
|
+
acceptedCumulative: 1n,
|
|
431
|
+
spent: 0n,
|
|
432
|
+
units: 0,
|
|
433
|
+
}),
|
|
434
|
+
),
|
|
435
|
+
)
|
|
436
|
+
return response
|
|
437
|
+
},
|
|
438
|
+
}),
|
|
439
|
+
generate: async function* (stream) {
|
|
440
|
+
await new Promise<void>((resolve) => {
|
|
441
|
+
stream.signal.addEventListener('abort', () => resolve(), { once: true })
|
|
442
|
+
})
|
|
443
|
+
generatorAborted = true
|
|
444
|
+
yield* []
|
|
445
|
+
},
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
socket.receive(
|
|
449
|
+
Ws.formatAuthorizationMessage(
|
|
450
|
+
makeCredential({
|
|
451
|
+
action: 'open',
|
|
452
|
+
channelId,
|
|
453
|
+
cumulativeAmount: '1',
|
|
454
|
+
signature: `0x${'77'.repeat(65)}`,
|
|
455
|
+
transaction: '0x01',
|
|
456
|
+
type: 'transaction',
|
|
457
|
+
}),
|
|
458
|
+
),
|
|
459
|
+
)
|
|
460
|
+
await sleep(10)
|
|
461
|
+
socket.receive(Ws.formatCloseRequestMessage())
|
|
462
|
+
await sleep(10)
|
|
463
|
+
|
|
464
|
+
expect(generatorAborted).toBe(true)
|
|
465
|
+
expect(
|
|
466
|
+
socket.sent
|
|
467
|
+
.map((message) => Ws.parseMessage(message))
|
|
468
|
+
.some((message) => message?.mpp === 'payment-close-ready'),
|
|
469
|
+
).toBe(true)
|
|
470
|
+
})
|
|
471
|
+
|
|
472
|
+
test('meters an explicit per-message amount independently of the challenge tick cost', async () => {
|
|
473
|
+
const socket = new MockSocket()
|
|
474
|
+
const store = memoryChannelStore()
|
|
475
|
+
await seedChannel(store, 25n)
|
|
476
|
+
|
|
477
|
+
await Ws.serve({
|
|
478
|
+
socket,
|
|
479
|
+
store,
|
|
480
|
+
url: 'ws://example.test/stream',
|
|
481
|
+
route: async () => ({
|
|
482
|
+
status: 200,
|
|
483
|
+
withReceipt(response = new Response(null, { status: 204 })) {
|
|
484
|
+
response.headers.set(
|
|
485
|
+
'Payment-Receipt',
|
|
486
|
+
serializeSessionReceipt(
|
|
487
|
+
createSessionReceipt({
|
|
488
|
+
challengeId: challenge.id,
|
|
489
|
+
channelId,
|
|
490
|
+
acceptedCumulative: 25n,
|
|
491
|
+
spent: 0n,
|
|
492
|
+
units: 0,
|
|
493
|
+
}),
|
|
494
|
+
),
|
|
495
|
+
)
|
|
496
|
+
return response
|
|
497
|
+
},
|
|
498
|
+
}),
|
|
499
|
+
generate: async function* (stream) {
|
|
500
|
+
await stream.charge(7n)
|
|
501
|
+
yield 'priced-response'
|
|
502
|
+
},
|
|
503
|
+
})
|
|
504
|
+
|
|
505
|
+
socket.receive(
|
|
506
|
+
Ws.formatAuthorizationMessage(
|
|
507
|
+
makeCredential({
|
|
508
|
+
action: 'open',
|
|
509
|
+
channelId,
|
|
510
|
+
cumulativeAmount: '25',
|
|
511
|
+
signature: `0x${'77'.repeat(65)}`,
|
|
512
|
+
transaction: '0x01',
|
|
513
|
+
type: 'transaction',
|
|
514
|
+
}),
|
|
515
|
+
),
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
await sleep(10)
|
|
519
|
+
|
|
520
|
+
expect(await store.getChannel(channelId)).toMatchObject({ spent: 7n, units: 1 })
|
|
521
|
+
expect(socket.sent.some((message) => message.includes('priced-response'))).toBe(true)
|
|
522
|
+
})
|
|
523
|
+
|
|
411
524
|
test('does not meter or emit application messages after close is requested on-chain', async () => {
|
|
412
525
|
const socket = new MockSocket()
|
|
413
526
|
const store = memoryChannelStore()
|
|
@@ -322,6 +322,8 @@ export declare namespace serve {
|
|
|
322
322
|
* multiple offers — otherwise a client can select the cheapest offer
|
|
323
323
|
* and still receive the same stream. */
|
|
324
324
|
amount?: string | undefined
|
|
325
|
+
/** Application stream. A manual stream can call `charge(amount)` with a
|
|
326
|
+
* per-message raw-unit amount; omitting it uses the challenge tick cost. */
|
|
325
327
|
generate: AsyncIterable<string> | ((stream: SessionController) => AsyncIterable<string>)
|
|
326
328
|
pollIntervalMs?: number | undefined
|
|
327
329
|
/** Payment route handler. Receives synthetic `POST` requests with only
|
|
@@ -3,8 +3,10 @@ export { charge, session, settle, settleBatch } from './Session.js'
|
|
|
3
3
|
export * as Sse from './Sse.js'
|
|
4
4
|
/** Server-side automatic settlement schedule. */
|
|
5
5
|
export type {
|
|
6
|
+
OnSessionSettlement,
|
|
6
7
|
ResolveSessionChannelId,
|
|
7
8
|
ResolveSessionChannelIdParameters,
|
|
8
9
|
SessionChannelIdRequest,
|
|
10
|
+
SessionSettlementContext,
|
|
9
11
|
SettlementSchedule,
|
|
10
12
|
} from './Session.js'
|
package/src/validation/core.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
parseEndpointArg,
|
|
15
15
|
resolveBodyForEndpoint,
|
|
16
16
|
} from '../cli/validate/helpers.js'
|
|
17
|
+
import { missingDiscoverySuggestion } from '../cli/validate/messages.js'
|
|
17
18
|
import { validatePaymentFlow } from '../cli/validate/payment.js'
|
|
18
19
|
import { validate as validateDiscoveryDoc } from '../discovery/Validate.js'
|
|
19
20
|
|
|
@@ -237,7 +238,7 @@ export async function validate(options: ValidateOptions): Promise<ValidateResult
|
|
|
237
238
|
|
|
238
239
|
if (!discovery) throw new Error('Discovery phase did not complete')
|
|
239
240
|
|
|
240
|
-
const suggestions = computeSuggestions(endpointResults, { sawTestnet, sawMainnet })
|
|
241
|
+
const suggestions = computeSuggestions(endpointResults, discovery, { sawTestnet, sawMainnet })
|
|
241
242
|
|
|
242
243
|
return {
|
|
243
244
|
url: baseUrl,
|
|
@@ -250,11 +251,16 @@ export async function validate(options: ValidateOptions): Promise<ValidateResult
|
|
|
250
251
|
|
|
251
252
|
function computeSuggestions(
|
|
252
253
|
endpoints: EndpointValidationResult[],
|
|
254
|
+
discovery: DiscoveryResult,
|
|
253
255
|
flags: { sawTestnet: boolean; sawMainnet: boolean },
|
|
254
256
|
): string[] {
|
|
255
257
|
const steps: string[] = []
|
|
256
258
|
const allResults = endpoints.flatMap((ep) => [...ep.challenge, ...ep.payment])
|
|
257
259
|
|
|
260
|
+
if (!discovery.found) {
|
|
261
|
+
steps.push(missingDiscoverySuggestion)
|
|
262
|
+
}
|
|
263
|
+
|
|
258
264
|
const sawCryptoMainnet = flags.sawMainnet
|
|
259
265
|
const sawCryptoTestnet = flags.sawTestnet
|
|
260
266
|
const sawStripeLive = allResults.some(
|
package/src/viem/Client.test.ts
CHANGED
|
@@ -3,12 +3,16 @@ import { privateKeyToAccount } from 'viem/accounts'
|
|
|
3
3
|
import { signTransaction } from 'viem/actions'
|
|
4
4
|
import { Account as TempoAccount, Transaction } from 'viem/tempo'
|
|
5
5
|
import { tempoLocalnet } from 'viem/tempo/chains'
|
|
6
|
-
import { describe, expect, test } from 'vp/test'
|
|
6
|
+
import { afterEach, describe, expect, test, vi } from 'vp/test'
|
|
7
7
|
|
|
8
8
|
import * as Client from './Client.js'
|
|
9
9
|
|
|
10
10
|
const rpcUrl = { 42: 'https://rpc.example.com', 99: 'https://rpc2.example.com' } as const
|
|
11
11
|
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
vi.unstubAllGlobals()
|
|
14
|
+
})
|
|
15
|
+
|
|
12
16
|
describe('getResolver', () => {
|
|
13
17
|
test('behavior: creates client from rpcUrl for given chainId', async () => {
|
|
14
18
|
const getClient = Client.getResolver({
|
|
@@ -61,6 +65,45 @@ describe('getResolver', () => {
|
|
|
61
65
|
`[Error: No \`rpcUrl\` configured for \`chainId\` (99).]`,
|
|
62
66
|
)
|
|
63
67
|
})
|
|
68
|
+
|
|
69
|
+
test('behavior: wraps custom client transports with a hosted fee payer', async () => {
|
|
70
|
+
const defaultRequests: string[] = []
|
|
71
|
+
const client = createClient({
|
|
72
|
+
chain: tempoLocalnet,
|
|
73
|
+
transport: custom({
|
|
74
|
+
async request({ method }) {
|
|
75
|
+
defaultRequests.push(method)
|
|
76
|
+
return { source: 'default' }
|
|
77
|
+
},
|
|
78
|
+
}),
|
|
79
|
+
})
|
|
80
|
+
const fetchMock = vi.fn(async (_input: RequestInfo | URL, init?: RequestInit) => {
|
|
81
|
+
const request = JSON.parse(String(init?.body)) as { id: number; jsonrpc: string }
|
|
82
|
+
return Response.json({
|
|
83
|
+
id: request.id,
|
|
84
|
+
jsonrpc: request.jsonrpc,
|
|
85
|
+
result: { source: 'relay' },
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
vi.stubGlobal('fetch', fetchMock)
|
|
89
|
+
|
|
90
|
+
const getClient = Client.getResolver({
|
|
91
|
+
chain: tempoLocalnet,
|
|
92
|
+
feePayerUrl: 'https://sponsor.example',
|
|
93
|
+
getClient: () => client,
|
|
94
|
+
rpcUrl: { [tempoLocalnet.id]: 'https://rpc.example.com' },
|
|
95
|
+
})
|
|
96
|
+
const resolved = await getClient({ chainId: tempoLocalnet.id })
|
|
97
|
+
const result = await resolved.request({
|
|
98
|
+
method: 'eth_fillTransaction',
|
|
99
|
+
params: [{ feePayer: true }],
|
|
100
|
+
} as never)
|
|
101
|
+
|
|
102
|
+
expect(result).toEqual({ source: 'relay' })
|
|
103
|
+
expect(fetchMock).toHaveBeenCalledOnce()
|
|
104
|
+
expect(String(fetchMock.mock.calls[0]?.[0])).toBe('https://sponsor.example/')
|
|
105
|
+
expect(defaultRequests).toEqual([])
|
|
106
|
+
})
|
|
64
107
|
})
|
|
65
108
|
|
|
66
109
|
// ---------------------------------------------------------------------------
|
package/src/viem/Client.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Chain, type Client, createClient, http } from 'viem'
|
|
1
|
+
import { type Chain, type Client, createClient, createTransport, custom, http } from 'viem'
|
|
2
2
|
import { withFeePayer } from 'viem/tempo'
|
|
3
3
|
|
|
4
4
|
import type { MaybePromise } from '../internal/types.js'
|
|
@@ -23,31 +23,45 @@ export function getResolver(
|
|
|
23
23
|
if (!chain?.serializers && !feePayerUrl) return getClient
|
|
24
24
|
return async (params) => {
|
|
25
25
|
const client = await getClient(params)
|
|
26
|
+
let resolvedClient = client
|
|
26
27
|
|
|
27
28
|
// Wrap the client's transport with `withFeePayer` when a fee payer URL is provided.
|
|
28
29
|
if (feePayerUrl && client.transport.key !== 'feePayer') {
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
const request = client.request.bind(client)
|
|
31
|
+
// The supplied client already owns retries. Keep the relay middleware retry-free so
|
|
32
|
+
// failures are not retried once per nested transport layer.
|
|
33
|
+
const feePayerTransport = withFeePayer(
|
|
34
|
+
custom({ request: (args) => request(args as never) }, { retryCount: 0 }),
|
|
35
|
+
http(feePayerUrl, { retryCount: client.transport.retryCount }),
|
|
36
|
+
)({
|
|
37
|
+
account: client.account,
|
|
38
|
+
chain: client.chain,
|
|
39
|
+
pollingInterval: client.pollingInterval,
|
|
40
|
+
retryCount: 0,
|
|
41
|
+
})
|
|
42
|
+
const wrapped = createTransport(
|
|
43
|
+
{ ...feePayerTransport.config, retryCount: 0 },
|
|
44
|
+
feePayerTransport.value,
|
|
45
|
+
)
|
|
46
|
+
resolvedClient = Object.assign({}, client, {
|
|
47
|
+
request: wrapped.request,
|
|
48
|
+
transport: { ...wrapped.config, ...wrapped.value },
|
|
49
|
+
})
|
|
37
50
|
}
|
|
38
51
|
|
|
39
|
-
if (!chain?.serializers ||
|
|
40
|
-
|
|
52
|
+
if (!chain?.serializers || resolvedClient.chain?.serializers?.transaction)
|
|
53
|
+
return resolvedClient
|
|
54
|
+
return Object.assign({}, resolvedClient, {
|
|
41
55
|
chain: {
|
|
42
56
|
...chain,
|
|
43
|
-
...
|
|
44
|
-
formatters:
|
|
57
|
+
...resolvedClient.chain,
|
|
58
|
+
formatters: resolvedClient.chain?.formatters ?? chain.formatters,
|
|
45
59
|
prepareTransactionRequest:
|
|
46
|
-
|
|
47
|
-
serializers:
|
|
48
|
-
?
|
|
60
|
+
resolvedClient.chain?.prepareTransactionRequest ?? chain.prepareTransactionRequest,
|
|
61
|
+
serializers: resolvedClient.chain?.serializers?.transaction
|
|
62
|
+
? resolvedClient.chain.serializers
|
|
49
63
|
: chain.serializers,
|
|
50
|
-
} as typeof
|
|
64
|
+
} as typeof resolvedClient.chain,
|
|
51
65
|
})
|
|
52
66
|
}
|
|
53
67
|
}
|