mppx 0.6.26 → 0.6.28

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 (78) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/Store.d.ts +32 -9
  3. package/dist/Store.d.ts.map +1 -1
  4. package/dist/Store.js +42 -10
  5. package/dist/Store.js.map +1 -1
  6. package/dist/cli/cli.js +1 -1
  7. package/dist/cli/cli.js.map +1 -1
  8. package/dist/cli/utils.js +2 -2
  9. package/dist/cli/utils.js.map +1 -1
  10. package/dist/proxy/internal/Headers.d.ts +13 -1
  11. package/dist/proxy/internal/Headers.d.ts.map +1 -1
  12. package/dist/proxy/internal/Headers.js +14 -1
  13. package/dist/proxy/internal/Headers.js.map +1 -1
  14. package/dist/stripe/server/Charge.d.ts +31 -1
  15. package/dist/stripe/server/Charge.d.ts.map +1 -1
  16. package/dist/stripe/server/Charge.js +88 -11
  17. package/dist/stripe/server/Charge.js.map +1 -1
  18. package/dist/stripe/server/internal/html.gen.d.ts +1 -1
  19. package/dist/stripe/server/internal/html.gen.js +1 -1
  20. package/dist/tempo/client/ChannelOps.d.ts.map +1 -1
  21. package/dist/tempo/client/ChannelOps.js +7 -2
  22. package/dist/tempo/client/ChannelOps.js.map +1 -1
  23. package/dist/tempo/client/Charge.d.ts.map +1 -1
  24. package/dist/tempo/client/Charge.js +6 -4
  25. package/dist/tempo/client/Charge.js.map +1 -1
  26. package/dist/tempo/client/Session.js +1 -1
  27. package/dist/tempo/client/Session.js.map +1 -1
  28. package/dist/tempo/client/Subscription.js +1 -1
  29. package/dist/tempo/client/Subscription.js.map +1 -1
  30. package/dist/tempo/server/Charge.d.ts +6 -0
  31. package/dist/tempo/server/Charge.d.ts.map +1 -1
  32. package/dist/tempo/server/Charge.js +8 -3
  33. package/dist/tempo/server/Charge.js.map +1 -1
  34. package/dist/tempo/server/Session.d.ts +6 -0
  35. package/dist/tempo/server/Session.d.ts.map +1 -1
  36. package/dist/tempo/server/Session.js +2 -2
  37. package/dist/tempo/server/Session.js.map +1 -1
  38. package/dist/tempo/server/Subscription.d.ts +6 -0
  39. package/dist/tempo/server/Subscription.d.ts.map +1 -1
  40. package/dist/tempo/server/Subscription.js +2 -2
  41. package/dist/tempo/server/Subscription.js.map +1 -1
  42. package/dist/tempo/server/internal/html.gen.d.ts +1 -1
  43. package/dist/tempo/server/internal/html.gen.d.ts.map +1 -1
  44. package/dist/tempo/server/internal/html.gen.js +1 -1
  45. package/dist/tempo/server/internal/html.gen.js.map +1 -1
  46. package/dist/tempo/session/Chain.d.ts.map +1 -1
  47. package/dist/tempo/session/Chain.js +6 -1
  48. package/dist/tempo/session/Chain.js.map +1 -1
  49. package/package.json +3 -3
  50. package/src/Store.test-d.ts +58 -0
  51. package/src/Store.test.ts +77 -0
  52. package/src/Store.ts +155 -74
  53. package/src/cli/cli.ts +1 -1
  54. package/src/cli/utils.test.ts +1 -1
  55. package/src/cli/utils.ts +2 -2
  56. package/src/proxy/internal/Headers.test.ts +18 -0
  57. package/src/proxy/internal/Headers.ts +14 -1
  58. package/src/stripe/server/Charge.test.ts +215 -1
  59. package/src/stripe/server/Charge.ts +150 -20
  60. package/src/stripe/server/internal/html.gen.ts +1 -1
  61. package/src/tempo/client/ChannelOps.ts +6 -2
  62. package/src/tempo/client/Charge.ts +5 -4
  63. package/src/tempo/client/Session.ts +1 -1
  64. package/src/tempo/client/Subscription.ts +1 -1
  65. package/src/tempo/server/Charge.test.ts +22 -1
  66. package/src/tempo/server/Charge.ts +17 -3
  67. package/src/tempo/server/Session.ts +10 -2
  68. package/src/tempo/server/Subscription.test.ts +1 -1
  69. package/src/tempo/server/Subscription.ts +8 -2
  70. package/src/tempo/server/internal/html/main.ts +1 -1
  71. package/src/tempo/server/internal/html/package.json +1 -1
  72. package/src/tempo/server/internal/html.gen.ts +1 -1
  73. package/src/tempo/session/Chain.test.ts +16 -8
  74. package/src/tempo/session/Chain.ts +6 -1
  75. package/src/tempo/session/ChannelStore.test.ts +21 -0
  76. package/src/tempo/subscription/Store.test.ts +55 -0
  77. package/src/viem/Account.test.ts +1 -1
  78. package/src/viem/Client.test.ts +1 -1
@@ -157,6 +157,7 @@ export async function createOpenPayload(
157
157
  functionName: 'open',
158
158
  args: [payee, currency, deposit, salt, authorizedSigner],
159
159
  })
160
+ const validBefore = Math.floor(Date.now() / 1_000) + 25
160
161
 
161
162
  const prepared = await prepareTransactionRequest(client, {
162
163
  account,
@@ -164,10 +165,13 @@ export async function createOpenPayload(
164
165
  { to: currency, data: approveData },
165
166
  { to: escrowContract, data: openData },
166
167
  ],
167
- ...(feePayer && { feePayer: true }),
168
168
  feeToken: currency,
169
+ ...(feePayer ? { nonceKey: 'expiring', validBefore } : {}),
169
170
  } as never)
170
- prepared.gas = prepared.gas! + 5_000n
171
+ // Estimate before enabling fee-payer mode so Tempo includes sender
172
+ // signature and access-key verification costs in the gas budget.
173
+ prepared.gas = (prepared.gas ?? 0n) + 5_000n
174
+ if (feePayer) (prepared as Record<string, unknown>).feePayer = true
171
175
  const transaction = (await signTransaction(client, prepared as never)) as Hex.Hex
172
176
 
173
177
  const signature = await signVoucher(
@@ -6,8 +6,8 @@ import {
6
6
  signTypedData,
7
7
  signTransaction,
8
8
  } from 'viem/actions'
9
- import { tempo as tempo_chain } from 'viem/chains'
10
9
  import { Actions } from 'viem/tempo'
10
+ import { tempo as tempo_chain } from 'viem/tempo/chains'
11
11
 
12
12
  import * as Credential from '../../Credential.js'
13
13
  import * as Method from '../../Method.js'
@@ -163,12 +163,13 @@ export function charge(parameters: charge.Parameters = {}) {
163
163
  const prepared = await prepareTransactionRequest(client, {
164
164
  account,
165
165
  calls,
166
- ...(methodDetails?.feePayer && { feePayer: true }),
167
166
  nonceKey: 'expiring',
168
167
  validBefore,
169
168
  } as never)
170
- // FIXME: figure out gas estimation issue for fee payer tx
171
- prepared.gas = prepared.gas! + 5_000n
169
+ // Estimate before enabling fee-payer mode so Tempo includes sender
170
+ // signature and access-key verification costs in the gas budget.
171
+ prepared.gas = (prepared.gas ?? 0n) + 5_000n
172
+ if (methodDetails?.feePayer) (prepared as Record<string, unknown>).feePayer = true
172
173
  const signature = await signTransaction(client, prepared as never)
173
174
 
174
175
  return Credential.serialize({
@@ -1,6 +1,6 @@
1
1
  import type { Hex } from 'ox'
2
2
  import { type Address, parseUnits, type Account as viem_Account } from 'viem'
3
- import { tempo as tempo_chain } from 'viem/chains'
3
+ import { tempo as tempo_chain } from 'viem/tempo/chains'
4
4
 
5
5
  import type * as Challenge from '../../Challenge.js'
6
6
  import * as Method from '../../Method.js'
@@ -1,7 +1,7 @@
1
1
  import { Hex } from 'ox'
2
2
  import { KeyAuthorization } from 'ox/tempo'
3
3
  import { isAddressEqual, type Address } from 'viem'
4
- import { tempo as tempo_chain } from 'viem/chains'
4
+ import { tempo as tempo_chain } from 'viem/tempo/chains'
5
5
 
6
6
  import * as Credential from '../../Credential.js'
7
7
  import type { MaybePromise } from '../../internal/types.js'
@@ -286,6 +286,7 @@ describe('tempo', () => {
286
286
  })
287
287
 
288
288
  test('behavior: rejects replayed transaction hash', async () => {
289
+ const dedupStore = Store.memory()
289
290
  const dedupServer = Mppx_server.create({
290
291
  methods: [
291
292
  tempo_server.charge({
@@ -294,7 +295,8 @@ describe('tempo', () => {
294
295
  },
295
296
  currency: asset,
296
297
  account: accounts[0],
297
- store: Store.memory(),
298
+ store: dedupStore,
299
+ storeKeyPrefix: 'tenant:',
298
300
  }),
299
301
  ],
300
302
  realm,
@@ -336,6 +338,8 @@ describe('tempo', () => {
336
338
  })
337
339
  expect(response.status).toBe(200)
338
340
  }
341
+ expect(await dedupStore.get(`tenant:mppx:charge:${receipt.transactionHash}`)).not.toBeNull()
342
+ expect(await dedupStore.get(`mppx:charge:${receipt.transactionHash}`)).toBeNull()
339
343
 
340
344
  const response2 = await fetch(httpServer.url)
341
345
  expect(response2.status).toBe(402)
@@ -1616,6 +1620,7 @@ describe('tempo', () => {
1616
1620
  currency: asset,
1617
1621
  account: accounts[0],
1618
1622
  store: sponsoredStore,
1623
+ storeKeyPrefix: 'tenant:',
1619
1624
  }),
1620
1625
  ],
1621
1626
  realm,
@@ -1661,6 +1666,16 @@ describe('tempo', () => {
1661
1666
 
1662
1667
  const first = fetch(httpServer.url, { headers: { Authorization: credential1 } })
1663
1668
  await simulationStarted
1669
+ expect(
1670
+ await sponsoredStore.get(
1671
+ `tenant:mppx:charge:sponsor:${chain.id}:${accounts[1].address.toLowerCase()}`,
1672
+ ),
1673
+ ).not.toBeNull()
1674
+ expect(
1675
+ await sponsoredStore.get(
1676
+ `mppx:charge:sponsor:${chain.id}:${accounts[1].address.toLowerCase()}`,
1677
+ ),
1678
+ ).toBeNull()
1664
1679
  const second = fetch(httpServer.url, { headers: { Authorization: credential2 } })
1665
1680
 
1666
1681
  try {
@@ -2870,6 +2885,7 @@ describe('tempo', () => {
2870
2885
 
2871
2886
  test('behavior: store keys proof replay protection by challenge ID', async () => {
2872
2887
  const replayStore = Store.memory()
2888
+ const storeKeyPrefix = 'tenant:'
2873
2889
  const server_ = Mppx_server.create({
2874
2890
  methods: [
2875
2891
  tempo_server.charge({
@@ -2879,6 +2895,7 @@ describe('tempo', () => {
2879
2895
  currency: asset,
2880
2896
  account: accounts[0],
2881
2897
  store: replayStore,
2898
+ storeKeyPrefix,
2882
2899
  }),
2883
2900
  ],
2884
2901
  realm,
@@ -2918,6 +2935,10 @@ describe('tempo', () => {
2918
2935
  headers: { Authorization: Credential.serialize(credential1) },
2919
2936
  })
2920
2937
  expect(response2.status).toBe(200)
2938
+ expect(
2939
+ await replayStore.get(`${storeKeyPrefix}mppx:charge:proof:${challenge1.id}`),
2940
+ ).not.toBeNull()
2941
+ expect(await replayStore.get(`mppx:charge:proof:${challenge1.id}`)).toBeNull()
2921
2942
 
2922
2943
  const response3 = await fetch(httpServer.url)
2923
2944
  expect(response3.status).toBe(402)
@@ -15,8 +15,8 @@ import {
15
15
  verifyTypedData,
16
16
  call as viem_call,
17
17
  } from 'viem/actions'
18
- import { tempo as tempo_chain } from 'viem/chains'
19
18
  import { Abis, Actions, Transaction } from 'viem/tempo'
19
+ import { tempo as tempo_chain } from 'viem/tempo/chains'
20
20
 
21
21
  import { PaymentError, VerificationFailedError } from '../../Errors.js'
22
22
  import * as Expires from '../../Expires.js'
@@ -66,8 +66,16 @@ export function charge<const parameters extends charge.Parameters>(
66
66
  validateSender,
67
67
  waitForConfirmation = true,
68
68
  } = parameters
69
- const store = (parameters.store ?? Store.memory()) as Store.AtomicStore<charge.StoreItemMap>
70
- const proofStore = parameters.store as Store.AtomicStore<charge.StoreItemMap> | undefined
69
+ const storeKeyPrefix = parameters.storeKeyPrefix ?? ''
70
+ const store = Store.from(
71
+ (parameters.store ?? Store.memory()) as Store.AtomicStore<charge.StoreItemMap>,
72
+ { keyPrefix: storeKeyPrefix },
73
+ )
74
+ const proofStore = parameters.store
75
+ ? Store.from(parameters.store as Store.AtomicStore<charge.StoreItemMap>, {
76
+ keyPrefix: storeKeyPrefix,
77
+ })
78
+ : undefined
71
79
 
72
80
  const { recipient, feePayer, feePayerUrl } = Account.resolve(parameters)
73
81
 
@@ -539,6 +547,12 @@ export declare namespace charge {
539
547
  * memo binding, transaction success, and replay protection.
540
548
  */
541
549
  validateSender?: ValidateSender | undefined
550
+ /**
551
+ * Prefix prepended to charge replay-protection store keys.
552
+ *
553
+ * By default, no prefix is applied.
554
+ */
555
+ storeKeyPrefix?: string | undefined
542
556
  /**
543
557
  * Whether to wait for the charge transaction to confirm on-chain before
544
558
  * responding. @default true
@@ -18,7 +18,7 @@ import {
18
18
  type Account as viem_Account,
19
19
  type Client as viem_Client,
20
20
  } from 'viem'
21
- import { tempo as tempo_chain } from 'viem/chains'
21
+ import { tempo as tempo_chain } from 'viem/tempo/chains'
22
22
 
23
23
  import {
24
24
  AmountExceedsDepositError,
@@ -104,7 +104,9 @@ export function session<const parameters extends session.Parameters>(
104
104
 
105
105
  const lastOnChainVerified = new Map<Hex, number>()
106
106
 
107
- const store = ChannelStore.fromStore(rawStore)
107
+ const store = ChannelStore.fromStore(
108
+ Store.from(rawStore, { keyPrefix: parameters.storeKeyPrefix }),
109
+ )
108
110
 
109
111
  const { account, recipient, feePayer, feePayerUrl } = Account.resolve(parameters)
110
112
 
@@ -357,6 +359,12 @@ export declare namespace session {
357
359
  * local single-process usage.
358
360
  */
359
361
  store?: Store.AtomicStore | undefined
362
+ /**
363
+ * Prefix prepended to channel state store keys.
364
+ *
365
+ * By default, no prefix is applied.
366
+ */
367
+ storeKeyPrefix?: string | undefined
360
368
  /**
361
369
  * Enable SSE streaming.
362
370
  *
@@ -3,7 +3,7 @@ import { Mppx } from 'mppx/server'
3
3
  import { KeyAuthorization } from 'ox/tempo'
4
4
  import { createClient, custom } from 'viem'
5
5
  import { privateKeyToAccount } from 'viem/accounts'
6
- import { tempo as tempo_chain } from 'viem/chains'
6
+ import { tempo as tempo_chain } from 'viem/tempo/chains'
7
7
  import { describe, expect, test } from 'vp/test'
8
8
 
9
9
  import * as Store from '../../Store.js'
@@ -8,8 +8,8 @@ import {
8
8
  sendRawTransactionSync,
9
9
  signTransaction,
10
10
  } from 'viem/actions'
11
- import { tempo as tempo_chain } from 'viem/chains'
12
11
  import { Abis, Account as TempoAccount, Transaction } from 'viem/tempo'
12
+ import { tempo as tempo_chain } from 'viem/tempo/chains'
13
13
 
14
14
  import { VerificationFailedError } from '../../Errors.js'
15
15
  import type { LooseOmit, MaybePromise, NoExtraKeys } from '../../internal/types.js'
@@ -72,7 +72,7 @@ export function subscription<const parameters extends subscription.Parameters>(
72
72
 
73
73
  const { recipient } = Account.resolve(parameters)
74
74
  const context = createContext(parameters, {
75
- store: rawStore,
75
+ store: Store.from(rawStore, { keyPrefix: parameters.storeKeyPrefix }),
76
76
  options: {
77
77
  activationTimeoutMs: parameters.activationTimeoutMs,
78
78
  renewalTimeoutMs: parameters.renewalTimeoutMs,
@@ -1110,6 +1110,12 @@ export declare namespace subscription {
1110
1110
  subscription: SubscriptionRecord
1111
1111
  }) => Promise<RenewalResult>
1112
1112
  store?: Store.AtomicStore<Record<string, unknown>> | undefined
1113
+ /**
1114
+ * Prefix prepended to all subscription store keys.
1115
+ *
1116
+ * By default, no prefix is applied.
1117
+ */
1118
+ storeKeyPrefix?: string | undefined
1113
1119
  testnet?: boolean | undefined
1114
1120
  waitForConfirmation?: boolean | undefined
1115
1121
  } & Defaults
@@ -1,6 +1,6 @@
1
1
  import { local, Provider } from 'accounts'
2
2
  import { createClient, custom, http } from 'viem'
3
- import { tempoModerato, tempoLocalnet } from 'viem/chains'
3
+ import { tempoModerato, tempoLocalnet } from 'viem/tempo/chains'
4
4
 
5
5
  import { tempo } from '../../../../client/index.js'
6
6
  import * as Html from '../../../../Html.js'
@@ -5,6 +5,6 @@
5
5
  "dependencies": {
6
6
  "accounts": "0.10.2",
7
7
  "mppx": "workspace:*",
8
- "viem": "2.47.5"
8
+ "viem": "2.50.4"
9
9
  }
10
10
  }