mppx 0.17.1 → 0.17.4

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 (89) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/dist/Errors.d.ts +94 -0
  3. package/dist/Errors.js +83 -0
  4. package/dist/Store.d.ts +413 -0
  5. package/dist/Store.js +837 -0
  6. package/dist/cli/cli.js +141 -136
  7. package/dist/cli/internal.js +28 -6
  8. package/dist/cli/plugins/index.js +16 -11
  9. package/dist/cli/sessions/Manager.js +12 -1
  10. package/dist/cli/utils.d.ts +2 -0
  11. package/dist/cli/utils.js +6 -0
  12. package/dist/cli/validate/challenge.d.ts +4 -0
  13. package/dist/cli/validate/challenge.js +9 -6
  14. package/dist/client/Mppx.d.ts +1 -1
  15. package/dist/client/Mppx.js +7 -3
  16. package/dist/client/internal/Fetch.d.ts +1 -1
  17. package/dist/client/internal/Fetch.js +9 -0
  18. package/dist/client/internal/MethodResponse.d.ts +8 -0
  19. package/dist/client/internal/MethodResponse.js +9 -0
  20. package/dist/client/internal/ProofRealm.d.ts +3 -3
  21. package/dist/client/internal/ProofRealm.js +4 -4
  22. package/dist/client/internal/protocols/Mcp.d.ts +4 -0
  23. package/dist/client/internal/protocols/Mcp.js +27 -14
  24. package/dist/cloudflare/ChannelStore.d.ts +97 -0
  25. package/dist/cloudflare/ChannelStore.js +304 -0
  26. package/dist/cloudflare/Channels.d.ts +92 -0
  27. package/dist/cloudflare/Channels.js +325 -0
  28. package/dist/cloudflare/index.d.ts +5 -0
  29. package/dist/cloudflare/index.js +5 -0
  30. package/dist/identity/Client.js +7 -1
  31. package/dist/internal/sse.d.ts +21 -0
  32. package/dist/internal/sse.js +82 -0
  33. package/dist/internal/version.d.ts +2 -2
  34. package/dist/internal/version.js +1 -1
  35. package/dist/mcp/client/McpClient.d.ts +2 -2
  36. package/dist/mcp/client/McpClient.js +5 -2
  37. package/dist/proxy/Proxy.d.ts +2 -0
  38. package/dist/proxy/Proxy.js +31 -6
  39. package/dist/proxy/internal/Replay.js +1 -1
  40. package/dist/server/Mppx.js +5 -4
  41. package/dist/server/Transport.js +1 -1
  42. package/dist/server/internal/html/config.d.ts +7 -1
  43. package/dist/server/internal/html/config.js +4 -0
  44. package/dist/stripe/internal/types.d.ts +82 -6
  45. package/dist/stripe/server/Charge.js +1 -2
  46. package/dist/stripe/server/Methods.d.ts +2 -0
  47. package/dist/stripe/server/Methods.js +2 -1
  48. package/dist/stripe/server/internal/html.gen.js +1 -1
  49. package/dist/stripe/server/internal/record-payment.d.ts +49 -0
  50. package/dist/stripe/server/internal/record-payment.js +75 -18
  51. package/dist/tempo/Methods.d.ts +8 -0
  52. package/dist/tempo/Methods.js +18 -2
  53. package/dist/tempo/client/Charge.d.ts +32 -1
  54. package/dist/tempo/client/Charge.js +201 -3
  55. package/dist/tempo/client/Methods.d.ts +16 -0
  56. package/dist/tempo/internal/fee-payer.d.ts +64 -14
  57. package/dist/tempo/internal/fee-payer.js +231 -20
  58. package/dist/tempo/internal/zone.d.ts +79 -0
  59. package/dist/tempo/internal/zone.js +133 -0
  60. package/dist/tempo/legacy/session/Chain.js +2 -0
  61. package/dist/tempo/server/Charge.d.ts +7 -3
  62. package/dist/tempo/server/Charge.js +100 -6
  63. package/dist/tempo/server/Methods.d.ts +40 -0
  64. package/dist/tempo/server/Subscription.js +1 -0
  65. package/dist/tempo/server/internal/html.gen.js +1 -1
  66. package/dist/tempo/server/internal/transport.d.ts +6 -0
  67. package/dist/tempo/server/internal/transport.js +21 -21
  68. package/dist/tempo/session/client/ChannelStore.d.ts +2 -0
  69. package/dist/tempo/session/client/CredentialState.js +2 -0
  70. package/dist/tempo/session/client/Session.d.ts +11 -1
  71. package/dist/tempo/session/client/Session.js +39 -6
  72. package/dist/tempo/session/client/SessionManager.d.ts +1 -1
  73. package/dist/tempo/session/client/SessionManager.js +8 -4
  74. package/dist/tempo/session/client/Transports.d.ts +2 -0
  75. package/dist/tempo/session/client/Transports.js +4 -1
  76. package/dist/tempo/session/precompile/Chain.js +1 -0
  77. package/dist/tempo/session/server/ChannelStore.d.ts +14 -0
  78. package/dist/tempo/session/server/ChannelStore.js +10 -3
  79. package/dist/tempo/session/server/CredentialVerification.js +1 -1
  80. package/dist/tempo/session/server/MeteredStream.d.ts +4 -0
  81. package/dist/tempo/session/server/MeteredStream.js +6 -1
  82. package/dist/tempo/session/server/Sse.d.ts +59 -0
  83. package/dist/tempo/session/server/Sse.js +175 -10
  84. package/dist/validation/core.js +2 -0
  85. package/dist/zod.d.ts +2 -1
  86. package/dist/zod.js +1 -1
  87. package/package.json +5 -3
  88. package/dist/stripe/server/internal/html/types.d.ts +0 -2
  89. package/dist/stripe/server/internal/html/types.js +0 -2
@@ -1,4 +1,4 @@
1
- import { type Address } from 'viem';
1
+ import { type Address, type Client as viem_Client } from 'viem';
2
2
  import * as Method from '../../Method.js';
3
3
  import * as Account from '../../viem/Account.js';
4
4
  import * as Client from '../../viem/Client.js';
@@ -61,6 +61,11 @@ export declare function charge(parameters?: charge.Parameters): {
61
61
  }, z.core.$strip>, z.ZodMiniObject<{
62
62
  signature: z.ZodMiniString<string>;
63
63
  type: z.ZodMiniLiteral<"proof">;
64
+ }, z.core.$strip>, z.ZodMiniObject<{
65
+ hash: z.ZodMiniString<string>;
66
+ senderTag: z.ZodMiniString<string>;
67
+ type: z.ZodMiniLiteral<"zone">;
68
+ zoneId: z.ZodMiniNumber<number>;
64
69
  }, z.core.$strip>], "type">;
65
70
  };
66
71
  request: z.ZodMiniPipe<z.ZodMiniObject<{
@@ -83,6 +88,7 @@ export declare function charge(parameters?: charge.Parameters): {
83
88
  pull: "pull";
84
89
  push: "push";
85
90
  }>>>;
91
+ zones: z.ZodMiniOptional<z.ZodMiniUnion<readonly [z.ZodMiniLiteral<true>, z.ZodMiniArray<z.ZodMiniNumber<number>>]>>;
86
92
  }, z.core.$strip>, z.ZodMiniTransform<{
87
93
  currency: string;
88
94
  description?: string | undefined;
@@ -99,6 +105,7 @@ export declare function charge(parameters?: charge.Parameters): {
99
105
  amount: string;
100
106
  }[] | undefined;
101
107
  supportedModes?: ("pull" | "push")[] | undefined;
108
+ zones?: true | number[] | undefined;
102
109
  } | undefined;
103
110
  }, {
104
111
  machineTokenEnabled?: boolean | undefined;
@@ -117,6 +124,7 @@ export declare function charge(parameters?: charge.Parameters): {
117
124
  recipient: `0x${string}`;
118
125
  }[] | undefined;
119
126
  supportedModes?: ("pull" | "push")[] | undefined;
127
+ zones?: true | number[] | undefined;
120
128
  }>>;
121
129
  };
122
130
  readonly createCredential: Method.CreateCredentialFn<{
@@ -133,6 +141,11 @@ export declare function charge(parameters?: charge.Parameters): {
133
141
  }, z.core.$strip>, z.ZodMiniObject<{
134
142
  signature: z.ZodMiniString<string>;
135
143
  type: z.ZodMiniLiteral<"proof">;
144
+ }, z.core.$strip>, z.ZodMiniObject<{
145
+ hash: z.ZodMiniString<string>;
146
+ senderTag: z.ZodMiniString<string>;
147
+ type: z.ZodMiniLiteral<"zone">;
148
+ zoneId: z.ZodMiniNumber<number>;
136
149
  }, z.core.$strip>], "type">;
137
150
  };
138
151
  request: z.ZodMiniPipe<z.ZodMiniObject<{
@@ -155,6 +168,7 @@ export declare function charge(parameters?: charge.Parameters): {
155
168
  pull: "pull";
156
169
  push: "push";
157
170
  }>>>;
171
+ zones: z.ZodMiniOptional<z.ZodMiniUnion<readonly [z.ZodMiniLiteral<true>, z.ZodMiniArray<z.ZodMiniNumber<number>>]>>;
158
172
  }, z.core.$strip>, z.ZodMiniTransform<{
159
173
  currency: string;
160
174
  description?: string | undefined;
@@ -171,6 +185,7 @@ export declare function charge(parameters?: charge.Parameters): {
171
185
  amount: string;
172
186
  }[] | undefined;
173
187
  supportedModes?: ("pull" | "push")[] | undefined;
188
+ zones?: true | number[] | undefined;
174
189
  } | undefined;
175
190
  }, {
176
191
  machineTokenEnabled?: boolean | undefined;
@@ -189,6 +204,7 @@ export declare function charge(parameters?: charge.Parameters): {
189
204
  recipient: `0x${string}`;
190
205
  }[] | undefined;
191
206
  supportedModes?: ("pull" | "push")[] | undefined;
207
+ zones?: true | number[] | undefined;
192
208
  }>>;
193
209
  };
194
210
  }, {
@@ -203,6 +219,15 @@ export declare namespace charge {
203
219
  type Context = ChargeContext;
204
220
  type ResolveAccount = AccountResolution.ResolveAccount;
205
221
  type ResolveAccountInfo = AccountResolution.ResolveAccountInfo;
222
+ /** Pays from a Zone balance when a challenge accepts that Zone. */
223
+ type Zone = {
224
+ /** Client for the Zone chain, with the paying account and Zone RPC authorization. */
225
+ client: viem_Client;
226
+ /** Zone ID. Derived from `client.chain.id` when omitted. */
227
+ zoneId?: number | undefined;
228
+ /** Milliseconds to wait for the withdrawal to settle on Tempo. @default 60_000 */
229
+ settlementTimeoutMs?: number | undefined;
230
+ };
206
231
  type Parameters = {
207
232
  /**
208
233
  * Automatically swap from a fallback currency (pathUsd, USDC.e) via the
@@ -277,6 +302,12 @@ export declare namespace charge {
277
302
  mode?: Methods.ChargeMode | undefined;
278
303
  /** Selects the account that signs this charge after the challenge and chain are known. */
279
304
  resolveAccount?: ResolveAccount | undefined;
305
+ /**
306
+ * Pays from a Zone balance instead of the account's Tempo balance when the
307
+ * challenge accepts this Zone (`methodDetails.zones`). Other challenges,
308
+ * and split payments, are paid on Tempo as usual.
309
+ */
310
+ zone?: Zone | undefined;
280
311
  } & Account.getResolver.Parameters & Client.getResolver.Parameters;
281
312
  }
282
313
  //# sourceMappingURL=Charge.d.ts.map
@@ -1,10 +1,11 @@
1
1
  import { TxEnvelopeTempo } from 'ox/tempo';
2
- import { keccak256, parseUnits } from 'viem';
3
- import { prepareTransactionRequest, sendCallsSync, sendTransactionSync, signTypedData, signTransaction, } from 'viem/actions';
4
- import { Actions } from 'viem/tempo';
2
+ import { keccak256, parseEventLogs, parseUnits, } from 'viem';
3
+ import { prepareTransactionRequest, sendCallsSync, sendRawTransactionSync, sendTransactionSync, signTypedData, signTransaction, waitForTransactionReceipt, } from 'viem/actions';
4
+ import { Abis, Actions, Addresses } from 'viem/tempo';
5
5
  import { tempo as tempo_chain } from 'viem/tempo/chains';
6
6
  import * as MethodResponse from '../../client/internal/MethodResponse.js';
7
7
  import * as Credential from '../../Credential.js';
8
+ import * as Errors from '../../Errors.js';
8
9
  import * as Method from '../../Method.js';
9
10
  import * as Account from '../../viem/Account.js';
10
11
  import * as Client from '../../viem/Client.js';
@@ -16,6 +17,7 @@ import * as defaults from '../internal/defaults.js';
16
17
  import { defaultFeeTokens, resolveFeeToken } from '../internal/fee-token.js';
17
18
  import * as MachineTokenCharge from '../internal/machine-token-charge.js';
18
19
  import * as Proof from '../internal/proof.js';
20
+ import * as Zone from '../internal/zone.js';
19
21
  import * as Methods from '../Methods.js';
20
22
  /**
21
23
  * Seconds from now the client sets as the expiring-nonce `validBefore`,
@@ -63,6 +65,9 @@ export function charge(parameters = {}) {
63
65
  rpcUrl: defaults.rpcUrl,
64
66
  });
65
67
  const getAccount = Account.getResolver({ account: parameters.account });
68
+ assertZone(parameters.zone);
69
+ // Zone withdrawals still settling, by challenge ID, so a retry resumes waiting instead of paying again.
70
+ const pendingZoneWithdrawals = new Map();
66
71
  const method = Method.toClient(Methods.charge, {
67
72
  // Skip offers that violate client policy so an acceptable offer can be chosen.
68
73
  canHandleChallenge({ challenge }) {
@@ -91,6 +96,36 @@ export function charge(parameters = {}) {
91
96
  const { request } = challenge;
92
97
  const { amount, methodDetails } = request;
93
98
  const supportedModes = methodDetails?.supportedModes ?? ['pull', 'push'];
99
+ const zoneChain = parameters.zone?.client.chain;
100
+ const zoneId = parameters.zone
101
+ ? (parameters.zone.zoneId ?? Zone.idFromChainId(zoneChain?.id ?? 0))
102
+ : undefined;
103
+ // A Zone ID is only unique per parent chain, so the Zone must settle on the challenge's chain.
104
+ const zoneParentChainId = zoneChain?.sourceId ??
105
+ (zoneChain ? Zone.parentChainIdFromChainId(zoneChain.id) : undefined);
106
+ const explicitMode = context?.mode ?? parameters.mode;
107
+ // Pay from a Zone balance when the challenge accepts the configured Zone.
108
+ // Zone payments are push payments, so they follow the challenge's and client's modes.
109
+ if (BigInt(amount) > 0n &&
110
+ parameters.zone &&
111
+ zoneId !== undefined &&
112
+ zoneParentChainId === chainId &&
113
+ supportedModes.includes('push') &&
114
+ (explicitMode === undefined || explicitMode === 'push') &&
115
+ Zone.isAccepted(methodDetails?.zones, zoneId) &&
116
+ !methodDetails?.splits &&
117
+ // A withdrawal can't be undone, so start one only when it can settle before the
118
+ // challenge expires; an expired challenge would invite another payment.
119
+ (pendingZoneWithdrawals.has(zoneWithdrawalKey(challenge)) ||
120
+ settlesBeforeExpiry(challenge.expires, parameters.zone.settlementTimeoutMs)))
121
+ return createZoneCredential({
122
+ challenge,
123
+ clientId,
124
+ client,
125
+ pending: pendingZoneWithdrawals,
126
+ zone: parameters.zone,
127
+ zoneId,
128
+ });
94
129
  const defaultAccount = getAccount(client, context);
95
130
  const resolveAccount = async (operation) => (await parameters.resolveAccount?.({
96
131
  account: defaultAccount,
@@ -326,6 +361,8 @@ function transactionHash(credential) {
326
361
  const payload = parsed.data;
327
362
  if (payload.type === 'hash')
328
363
  return payload.hash;
364
+ if (payload.type === 'zone')
365
+ return payload.hash;
329
366
  const serialized = payload.signature;
330
367
  if (payload.type !== 'transaction' || !serialized.startsWith(TxEnvelopeTempo.serializedType))
331
368
  return undefined;
@@ -337,6 +374,167 @@ function transactionHash(credential) {
337
374
  return undefined;
338
375
  return keccak256(serialized);
339
376
  }
377
+ /**
378
+ * Pays a challenge from a Zone balance: requests a withdrawal to the recipient
379
+ * with the challenge-bound memo, and returns a `zone` credential that names it
380
+ * by sender tag and the Tempo transaction hash that settled it.
381
+ */
382
+ async function createZoneCredential(options) {
383
+ const { challenge, client, pending, zone, zoneId } = options;
384
+ const request = challenge.request;
385
+ const recipient = request.recipient;
386
+ // The whole challenge, so a challenge from another realm or request with the same ID never matches.
387
+ const key = zoneWithdrawalKey(challenge);
388
+ // A withdrawal can't be undone, so a retry after a failed wait resumes it instead of paying again.
389
+ // Concurrent calls for one challenge share the request, which is stored before it is sent.
390
+ let withdrawalRequest = pending.get(key);
391
+ if (!withdrawalRequest) {
392
+ const state = { sent: false };
393
+ withdrawalRequest = requestZoneWithdrawal(options, state);
394
+ pending.set(key, withdrawalRequest);
395
+ // Only a failure before the transaction was sent is safe to retry. After that, the
396
+ // withdrawal may have gone through, so retries of this challenge keep failing.
397
+ withdrawalRequest.catch(() => {
398
+ if (!state.sent)
399
+ pending.delete(key);
400
+ });
401
+ }
402
+ const senderTag = await withdrawalRequest;
403
+ let hash;
404
+ try {
405
+ hash = await Zone.waitForSettlement(client, {
406
+ portal: Zone.portalAddress(zoneId),
407
+ senderTag,
408
+ timeoutMs: settlementTimeoutMs(challenge.expires, zone.settlementTimeoutMs),
409
+ to: recipient,
410
+ });
411
+ }
412
+ catch (cause) {
413
+ throw new Errors.ZoneSettlementPendingError({
414
+ cause,
415
+ challengeId: challenge.id,
416
+ senderTag,
417
+ zoneId,
418
+ });
419
+ }
420
+ pending.delete(key);
421
+ return Credential.serialize({
422
+ challenge,
423
+ payload: { hash, senderTag, type: 'zone', zoneId },
424
+ });
425
+ }
426
+ /**
427
+ * Throws on Zone options that would fail only after an irreversible withdrawal:
428
+ * a `settlementTimeoutMs` that is not a finite, non-negative number, or a
429
+ * `zoneId` that names a different Zone from the one `zone.client` serves.
430
+ */
431
+ function assertZone(zone) {
432
+ const timeoutMs = zone?.settlementTimeoutMs;
433
+ if (timeoutMs !== undefined && (!Number.isFinite(timeoutMs) || timeoutMs < 0))
434
+ throw new Error('`zone.settlementTimeoutMs` must be a finite, non-negative number.');
435
+ const chainId = zone?.client.chain?.id;
436
+ if (zone?.zoneId === undefined || chainId === undefined)
437
+ return;
438
+ // Only chain IDs that follow a known Zone scheme name their Zone.
439
+ if (Zone.parentChainIdFromChainId(chainId) === undefined)
440
+ return;
441
+ const derived = Zone.idFromChainId(chainId);
442
+ if (derived !== zone.zoneId)
443
+ throw new Error(`zone.zoneId ${zone.zoneId} does not match Zone ${derived} of the zone.client chain ${chainId}.`);
444
+ }
445
+ /** Time the client allows for sending a Zone withdrawal before it waits for settlement. */
446
+ const zoneSubmissionMarginMs = 15_000;
447
+ /**
448
+ * Returns whether a Zone withdrawal can be sent and waited on for up to
449
+ * `settlementTimeoutMs` before `expires`.
450
+ */
451
+ function settlesBeforeExpiry(expires, settlementTimeoutMs) {
452
+ if (!expires)
453
+ return true;
454
+ const timeoutMs = settlementTimeoutMs ?? Zone.defaultSettlementTimeoutMs;
455
+ return new Date(expires).getTime() - Date.now() > timeoutMs + zoneSubmissionMarginMs;
456
+ }
457
+ /** Returns how long to wait for settlement: `settlementTimeoutMs`, cut short at `expires`. */
458
+ function settlementTimeoutMs(expires, settlementTimeoutMs) {
459
+ const timeoutMs = settlementTimeoutMs ?? Zone.defaultSettlementTimeoutMs;
460
+ if (!expires)
461
+ return timeoutMs;
462
+ return Math.max(0, Math.min(timeoutMs, new Date(expires).getTime() - Date.now()));
463
+ }
464
+ /** Keys a Zone withdrawal by its whole Challenge. Unlike `Challenge.serialize`, it accepts any field value. */
465
+ function zoneWithdrawalKey(challenge) {
466
+ return JSON.stringify(challenge);
467
+ }
468
+ /** How long to look for a withdrawal's receipt on the Zone after its send response fails. */
469
+ const zoneReceiptRecoveryTimeoutMs = 15_000;
470
+ /** Requests a Zone withdrawal of the challenge amount to its recipient, bound to the challenge by its memo. */
471
+ async function requestZoneWithdrawal(options, state) {
472
+ const { challenge, clientId, zone } = options;
473
+ const request = challenge.request;
474
+ const account = zone.client.account;
475
+ if (!account)
476
+ throw new Error('`zone.client` requires an account.');
477
+ const amount = BigInt(request.amount);
478
+ const currency = request.currency;
479
+ const recipient = request.recipient;
480
+ const memo = Attribution.encode({
481
+ challengeId: challenge.id,
482
+ clientId,
483
+ serverId: challenge.realm,
484
+ });
485
+ // The outbox pulls the amount plus its withdrawal fee.
486
+ const fee = await Actions.zone.getWithdrawalFee(zone.client);
487
+ const [, withdrawal] = Actions.zone.requestWithdrawal.calls({
488
+ amount,
489
+ // A bounced payout refunds the payer, not the payee's Zone address.
490
+ fallbackRecipient: account.address,
491
+ memo,
492
+ to: recipient,
493
+ token: currency,
494
+ });
495
+ // Prepare and sign first: a failure there sent nothing, so the challenge may be retried.
496
+ const prepared = await prepareTransactionRequest(zone.client, {
497
+ account,
498
+ calls: [
499
+ Actions.token.approve.call({
500
+ amount: amount + fee,
501
+ spender: Addresses.zoneOutbox,
502
+ token: currency,
503
+ }),
504
+ withdrawal,
505
+ ],
506
+ });
507
+ const serializedTransaction = await signTransaction(zone.client, prepared);
508
+ state.sent = true;
509
+ // The signed transaction fixes its hash, so a lost send response can still be recovered from the Zone.
510
+ const hash = keccak256(serializedTransaction);
511
+ const receipt = await sendRawTransactionSync(zone.client, { serializedTransaction }).catch((cause) => waitForTransactionReceipt(zone.client, {
512
+ hash,
513
+ timeout: zoneReceiptRecoveryTimeoutMs,
514
+ }).catch(() => {
515
+ const error = new Error(`Zone withdrawal outcome is unknown: transaction ${hash} may have been sent. Retrying this challenge does not withdraw again.`);
516
+ // Defined manually (non-enumerable, like native `cause`) so consumers on pre-ES2022 libs still typecheck.
517
+ Object.defineProperty(error, 'cause', { configurable: true, value: cause, writable: true });
518
+ throw error;
519
+ }));
520
+ // A reverted withdrawal moved nothing, so the challenge may be retried.
521
+ if (receipt.status === 'reverted') {
522
+ state.sent = false;
523
+ throw new Error(`Zone withdrawal transaction ${receipt.transactionHash} reverted.`);
524
+ }
525
+ const [requested] = parseEventLogs({
526
+ abi: Abis.zoneOutbox,
527
+ eventName: 'WithdrawalRequested',
528
+ logs: receipt.logs,
529
+ });
530
+ if (!requested)
531
+ throw new Error('Zone withdrawal request did not emit `WithdrawalRequested`.');
532
+ return Zone.senderTag({
533
+ fallbackNonce: requested.args.fallbackNonce,
534
+ sender: requested.args.sender,
535
+ transactionHash: receipt.transactionHash,
536
+ });
537
+ }
340
538
  /** Parses `maxAmount` to base units, rejecting values that cannot be represented exactly. */
341
539
  function parseMaxAmount(maxAmount, decimals) {
342
540
  if (maxAmount === undefined)
@@ -43,6 +43,11 @@ export declare function tempo(parameters?: tempo.Parameters): readonly [{
43
43
  }, import("zod/v4/core").$strip>, import("zod/mini").ZodMiniObject<{
44
44
  signature: import("zod/mini").ZodMiniString<string>;
45
45
  type: import("zod/mini").ZodMiniLiteral<"proof">;
46
+ }, import("zod/v4/core").$strip>, import("zod/mini").ZodMiniObject<{
47
+ hash: import("zod/mini").ZodMiniString<string>;
48
+ senderTag: import("zod/mini").ZodMiniString<string>;
49
+ type: import("zod/mini").ZodMiniLiteral<"zone">;
50
+ zoneId: import("zod/mini").ZodMiniNumber<number>;
46
51
  }, import("zod/v4/core").$strip>], "type">;
47
52
  };
48
53
  request: import("zod/mini").ZodMiniPipe<import("zod/mini").ZodMiniObject<{
@@ -65,6 +70,7 @@ export declare function tempo(parameters?: tempo.Parameters): readonly [{
65
70
  pull: "pull";
66
71
  push: "push";
67
72
  }>>>;
73
+ zones: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniUnion<readonly [import("zod/mini").ZodMiniLiteral<true>, import("zod/mini").ZodMiniArray<import("zod/mini").ZodMiniNumber<number>>]>>;
68
74
  }, import("zod/v4/core").$strip>, import("zod/mini").ZodMiniTransform<{
69
75
  currency: string;
70
76
  description?: string | undefined;
@@ -81,6 +87,7 @@ export declare function tempo(parameters?: tempo.Parameters): readonly [{
81
87
  amount: string;
82
88
  }[] | undefined;
83
89
  supportedModes?: ("pull" | "push")[] | undefined;
90
+ zones?: true | number[] | undefined;
84
91
  } | undefined;
85
92
  }, {
86
93
  machineTokenEnabled?: boolean | undefined;
@@ -99,6 +106,7 @@ export declare function tempo(parameters?: tempo.Parameters): readonly [{
99
106
  recipient: `0x${string}`;
100
107
  }[] | undefined;
101
108
  supportedModes?: ("pull" | "push")[] | undefined;
109
+ zones?: true | number[] | undefined;
102
110
  }>>;
103
111
  };
104
112
  readonly createCredential: import("../../Method.js").CreateCredentialFn<{
@@ -115,6 +123,11 @@ export declare function tempo(parameters?: tempo.Parameters): readonly [{
115
123
  }, import("zod/v4/core").$strip>, import("zod/mini").ZodMiniObject<{
116
124
  signature: import("zod/mini").ZodMiniString<string>;
117
125
  type: import("zod/mini").ZodMiniLiteral<"proof">;
126
+ }, import("zod/v4/core").$strip>, import("zod/mini").ZodMiniObject<{
127
+ hash: import("zod/mini").ZodMiniString<string>;
128
+ senderTag: import("zod/mini").ZodMiniString<string>;
129
+ type: import("zod/mini").ZodMiniLiteral<"zone">;
130
+ zoneId: import("zod/mini").ZodMiniNumber<number>;
118
131
  }, import("zod/v4/core").$strip>], "type">;
119
132
  };
120
133
  request: import("zod/mini").ZodMiniPipe<import("zod/mini").ZodMiniObject<{
@@ -137,6 +150,7 @@ export declare function tempo(parameters?: tempo.Parameters): readonly [{
137
150
  pull: "pull";
138
151
  push: "push";
139
152
  }>>>;
153
+ zones: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniUnion<readonly [import("zod/mini").ZodMiniLiteral<true>, import("zod/mini").ZodMiniArray<import("zod/mini").ZodMiniNumber<number>>]>>;
140
154
  }, import("zod/v4/core").$strip>, import("zod/mini").ZodMiniTransform<{
141
155
  currency: string;
142
156
  description?: string | undefined;
@@ -153,6 +167,7 @@ export declare function tempo(parameters?: tempo.Parameters): readonly [{
153
167
  amount: string;
154
168
  }[] | undefined;
155
169
  supportedModes?: ("pull" | "push")[] | undefined;
170
+ zones?: true | number[] | undefined;
156
171
  } | undefined;
157
172
  }, {
158
173
  machineTokenEnabled?: boolean | undefined;
@@ -171,6 +186,7 @@ export declare function tempo(parameters?: tempo.Parameters): readonly [{
171
186
  recipient: `0x${string}`;
172
187
  }[] | undefined;
173
188
  supportedModes?: ("pull" | "push")[] | undefined;
189
+ zones?: true | number[] | undefined;
174
190
  }>>;
175
191
  };
176
192
  }, {
@@ -1,3 +1,4 @@
1
+ import { Signature } from 'ox';
1
2
  import { TempoAddress } from 'ox/tempo';
2
3
  import type { Hex } from 'viem';
3
4
  import type { Account } from 'viem';
@@ -67,50 +68,99 @@ export declare function fillHostedFeePayerTransaction(parameters: {
67
68
  transaction: SponsoredTransaction;
68
69
  }): Promise<{
69
70
  feePayer: `0x${string}`;
71
+ feePayerSignature: {
72
+ readonly r: bigint;
73
+ readonly s: bigint;
74
+ readonly yParity: number;
75
+ };
70
76
  feeToken: TempoAddress.Address;
71
77
  serializedTransaction: `0x${string}`;
72
78
  }>;
73
79
  /**
74
80
  * Returns a transaction shape suitable for pre-broadcast simulation.
75
81
  *
76
- * Sponsored transactions are first simulated as calls from the sender with no
77
- * fee fields or signatures. This checks call execution without requiring the
78
- * sender to hold the fee token; transferred value and call-level balances are
79
- * still checked by the RPC.
82
+ * Sponsored transactions are first simulated as calls from the sender, without
83
+ * signatures, fee payer or fee token, and with zero fee fields: Tempo nodes
84
+ * (v1.18+) skip the fee-token balance check for a zero-fee `eth_call`, so the
85
+ * sender's fee balance is irrelevant. The call keeps the transaction's exact
86
+ * gas limit and the fields that set its intrinsic gas (nonce, nonce key,
87
+ * validity window, key authorization, signature type), so it is the gas-bounded
88
+ * check at the broadcast limit: a transaction whose gas is too low fails here
89
+ * with out of gas instead of reverting onchain at the sponsor's expense.
90
+ * Transferred value and call-level balances are still checked by the RPC.
80
91
  */
81
92
  export declare function simulationTransaction(transaction: SponsoredTransaction, options: {
82
93
  feePayer: boolean;
83
94
  }): any;
84
95
  /**
85
96
  * Returns the final fee-sponsored transaction shape for pre-broadcast
86
- * simulation. RPC `eth_call` does not carry either transaction signature.
97
+ * simulation.
98
+ *
99
+ * Tempo nodes recover the fee payer of an `eth_call` only from
100
+ * `feePayerSignature`; a bare `feePayer` address is ignored and the sender is
101
+ * billed instead. Pass the sponsor's signature so the node charges the fee
102
+ * token to the sponsor, not to a sender that may not hold it. The sender's own
103
+ * signature is never sent.
87
104
  */
88
105
  export declare function sponsoredSimulationTransaction(transaction: SponsoredTransaction, options: {
89
106
  feePayer: TempoAddress.Address;
107
+ feePayerSignature?: Signature.Signature | undefined;
90
108
  feeToken?: TempoAddress.Address | undefined;
91
109
  }): any;
92
110
  /** A completed sponsorship envelope that can be simulated before signing or broadcast. */
93
111
  export type PreflightSponsorship = {
94
112
  feePayer: TempoAddress.Address;
113
+ /**
114
+ * The sponsor's signature over the final envelope. Hosted sponsors return it
115
+ * from `eth_fillTransaction`; local accounts are signed in `preflightSponsorship`.
116
+ */
117
+ feePayerSignature?: Signature.Signature | undefined;
95
118
  feeToken?: TempoAddress.Address | undefined;
96
119
  transaction: SponsoredTransaction;
97
120
  };
98
121
  /**
99
- * Runs execution-only sender and final-envelope simulations around sponsorship.
100
- *
101
- * First, it simulates the calls with the sender as `from`, omitting fee fields
102
- * and signatures so the sender's fee balance is irrelevant. Next, `complete`
103
- * resolves the sponsor and produces the co-signed transaction. Finally, it
104
- * simulates that transaction with its concrete fee payer and fee token.
105
- *
106
- * `complete` runs only after sender-context execution succeeds, so a reverting
107
- * transaction never reaches a local signer or hosted fee-payer.
122
+ * Signs the fee-payer payload of a sponsored envelope with a local fee-payer
123
+ * account, the same payload `Transaction.serialize` signs before broadcast.
124
+ * Returns `undefined` when the envelope's fee payer cannot sign locally.
108
125
  */
126
+ export declare function signFeePayerSimulation(transaction: SponsoredTransaction): Promise<Signature.Signature | undefined>;
109
127
  export declare function preflightSponsorship<sponsorship extends PreflightSponsorship>(parameters: {
110
128
  complete: () => Promise<sponsorship>;
129
+ /** The fee payer policy the completed transaction was checked against. */
130
+ policy?: Partial<Policy> | undefined;
111
131
  simulate: (request: Record<string, unknown>) => Promise<unknown>;
112
132
  transaction: SponsoredTransaction;
113
133
  }): Promise<sponsorship>;
134
+ /**
135
+ * Returns the gas limit of a local fee payer's simulation-only copy: the
136
+ * transaction's gas plus {@link sponsoredSimulationGasBuffer}, clamped to the
137
+ * fee payer policy's `maxGas` and to `maxTotalFee / maxFeePerGas`, so the
138
+ * signed copy never exceeds a cap the real transaction is held to.
139
+ */
140
+ export declare function sponsoredSimulationGas(transaction: Pick<SponsoredTransaction, 'chainId' | 'gas' | 'maxFeePerGas'>, policyOverrides?: Partial<Policy> | undefined): bigint;
141
+ /**
142
+ * Returns true when a simulation failed only because it ran out of gas.
143
+ *
144
+ * Tempo nodes report out of gas as `out of gas: …` (code -32003, shared with
145
+ * `insufficient fee token balance`) or `intrinsic gas too low` (code -32000),
146
+ * so this reads the node's own message rather than the code. Only the terminal
147
+ * RPC failure counts: the deepest `details` in the cause chain (viem's
148
+ * `RpcRequestError` carries the node's message there), or the deepest message
149
+ * when no error has details. Wrapper messages are ignored, the match is anchored
150
+ * to the start of the node's message, and an error carrying revert data is never
151
+ * treated as gas exhaustion, so a revert reason or wrapper text that merely
152
+ * mentions gas cannot hide another failure.
153
+ */
154
+ export declare function isOutOfGasError(error: unknown): boolean;
155
+ /**
156
+ * Extra gas a local fee payer signs into its simulation-only envelope. Tempo's sponsored
157
+ * `eth_call` charges more gas than the same transaction uses when mined (about 6,000 for a
158
+ * session top-up on v1.18.1), so simulating at the signed limit would reject transactions
159
+ * that succeed onchain. The padding is clamped to the fee payer policy; hosted signatures
160
+ * cover the client's gas and are simulated as signed. In both cases the gas bound at the
161
+ * broadcast limit comes from the sender simulation.
162
+ */
163
+ export declare const sponsoredSimulationGasBuffer = 10000n;
114
164
  /**
115
165
  * Validates an allowed fee-payer pattern. Returns the funding currency when the
116
166
  * calls are bound to the expected payment transfers.