mppx 0.6.21 → 0.6.23

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.
@@ -1 +1 @@
1
- {"version":3,"file":"html.gen.js","sourceRoot":"","sources":["../../../../src/tempo/server/internal/html.gen.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,MAAM,CAAC,MAAM,IAAI,GAAG,s+teAAs+te,CAAA"}
1
+ {"version":3,"file":"html.gen.js","sourceRoot":"","sources":["../../../../src/tempo/server/internal/html.gen.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,MAAM,CAAC,MAAM,IAAI,GAAG,8yteAA8yte,CAAA"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mppx",
3
3
  "type": "module",
4
- "version": "0.6.21",
4
+ "version": "0.6.23",
5
5
  "main": "./dist/index.js",
6
6
  "license": "MIT",
7
7
  "files": [
@@ -104,6 +104,57 @@ describe('tempo.subscription client', () => {
104
104
  )
105
105
  })
106
106
 
107
+ test('passes hex-encoded `scopes` and `limits` to wallet_authorizeAccessKey', async () => {
108
+ const challenge = createChallenge()
109
+ const keyAuthorization = await signSubscriptionKeyAuthorization({
110
+ accessKey,
111
+ account: selectedAccount,
112
+ chainId,
113
+ request: challenge.request,
114
+ })
115
+ if (!keyAuthorization) throw new Error('expected key authorization')
116
+
117
+ let capturedParams: Record<string, unknown> | undefined
118
+ const method = subscription({
119
+ account: selectedAccount.address,
120
+ getClient: async () =>
121
+ ({
122
+ request: async ({ params }: { params: readonly Record<string, unknown>[] }) => {
123
+ capturedParams = params[0]
124
+ return { keyAuthorization: KeyAuthorization.toRpc(keyAuthorization) }
125
+ },
126
+ }) as never,
127
+ })
128
+
129
+ await method.createCredential({ challenge, context: {} })
130
+
131
+ expect(capturedParams).toMatchObject({
132
+ address: accessKey.accessKeyAddress.toLowerCase(),
133
+ keyType: accessKey.keyType,
134
+ expiry: expect.any(Number),
135
+ limits: [
136
+ {
137
+ token: expect.stringMatching(/^0x[0-9a-fA-F]{40}$/),
138
+ limit: '0xf4240',
139
+ period: expect.any(Number),
140
+ },
141
+ ],
142
+ scopes: [
143
+ {
144
+ address: expect.stringMatching(/^0x[0-9a-fA-F]{40}$/),
145
+ selector: expect.stringMatching(/^0x/),
146
+ recipients: expect.any(Array),
147
+ },
148
+ {
149
+ address: expect.stringMatching(/^0x[0-9a-fA-F]{40}$/),
150
+ selector: expect.stringMatching(/^0x/),
151
+ recipients: expect.any(Array),
152
+ },
153
+ ],
154
+ })
155
+ expect(capturedParams).not.toHaveProperty('allowedCalls')
156
+ })
157
+
107
158
  test('rejects key authorizations signed by a different account', async () => {
108
159
  const challenge = createChallenge()
109
160
  const keyAuthorization = await signSubscriptionKeyAuthorization({
@@ -1,3 +1,4 @@
1
+ import { Hex } from 'ox'
1
2
  import { KeyAuthorization } from 'ox/tempo'
2
3
  import { isAddressEqual, type Address } from 'viem'
3
4
  import { tempo as tempo_chain } from 'viem/chains'
@@ -11,7 +12,7 @@ import * as z from '../../zod.js'
11
12
  import * as defaults from '../internal/defaults.js'
12
13
  import * as Methods from '../Methods.js'
13
14
  import {
14
- getSubscriptionRpcAllowedCalls,
15
+ getSubscriptionScopes,
15
16
  signSubscriptionKeyAuthorization,
16
17
  toSubscriptionExpiryDate,
17
18
  toSubscriptionExpirySeconds,
@@ -117,16 +118,16 @@ async function authorizeAccessKey(
117
118
  params: [
118
119
  {
119
120
  address: accessKey.accessKeyAddress,
120
- allowedCalls: getSubscriptionRpcAllowedCalls(request),
121
121
  expiry: toSubscriptionExpirySeconds(toSubscriptionExpiryDate(request.subscriptionExpires)),
122
122
  keyType: accessKey.keyType,
123
123
  limits: [
124
124
  {
125
125
  token: request.currency as Address,
126
- limit: BigInt(request.amount),
126
+ limit: Hex.fromNumber(BigInt(request.amount)),
127
127
  period: toSubscriptionPeriodSeconds(request),
128
128
  },
129
129
  ],
130
+ scopes: getSubscriptionScopes(request),
130
131
  },
131
132
  ],
132
133
  } as never)) as {
@@ -3,6 +3,7 @@ import { KeyAuthorization } from 'ox/tempo'
3
3
  import { encodeFunctionData, isAddressEqual, type Address, type Client as ViemClient } from 'viem'
4
4
  import {
5
5
  call as viem_call,
6
+ prepareTransactionRequest,
6
7
  sendRawTransaction,
7
8
  sendRawTransactionSync,
8
9
  signTransaction,
@@ -19,6 +20,7 @@ import * as ClientResolver from '../../viem/Client.js'
19
20
  import * as Attribution from '../Attribution.js'
20
21
  import * as Account from '../internal/account.js'
21
22
  import * as defaults from '../internal/defaults.js'
23
+ import * as FeePayer from '../internal/fee-payer.js'
22
24
  import * as Proof from '../internal/proof.js'
23
25
  import type * as types from '../internal/types.js'
24
26
  import * as Methods from '../Methods.js'
@@ -72,7 +74,7 @@ export function subscription<const parameters extends subscription.Parameters>(
72
74
  activationTimeoutMs: parameters.activationTimeoutMs,
73
75
  renewalTimeoutMs: parameters.renewalTimeoutMs,
74
76
  })
75
- const { recipient } = Account.resolve(parameters)
77
+ const { feePayer, recipient } = Account.resolve(parameters)
76
78
  const getClient = ClientResolver.getResolver({
77
79
  chain: tempo_chain,
78
80
  getClient: parameters.getClient,
@@ -104,6 +106,8 @@ export function subscription<const parameters extends subscription.Parameters>(
104
106
  const periodIndex = getPeriodIndex(subscription)
105
107
  if (periodIndex > subscription.lastChargedPeriod) {
106
108
  const renew = resolveRenewalHandler({
109
+ feePayer,
110
+ feePayerPolicy: parameters.feePayerPolicy,
107
111
  getClient,
108
112
  parameters,
109
113
  store,
@@ -235,6 +239,8 @@ export function subscription<const parameters extends subscription.Parameters>(
235
239
  accessKey,
236
240
  auto: {
237
241
  challengeId: credential.challenge.id,
242
+ feePayer,
243
+ feePayerPolicy: parameters.feePayerPolicy,
238
244
  getClient,
239
245
  keyAuthorization: (credential.payload as SubscriptionCredentialPayload).signature,
240
246
  realm: credential.challenge.realm,
@@ -346,6 +352,8 @@ async function activateSubscription(parameters: {
346
352
  accessKey: SubscriptionAccessKey
347
353
  auto: {
348
354
  challengeId: string
355
+ feePayer?: ReturnType<typeof Account.resolve>['feePayer'] | undefined
356
+ feePayerPolicy?: Partial<FeePayer.Policy> | undefined
349
357
  getClient: (parameters: { chainId?: number | undefined }) => MaybePromise<ViemClient>
350
358
  keyAuthorization: `0x${string}`
351
359
  realm: string
@@ -391,6 +399,8 @@ async function activateSubscription(parameters: {
391
399
  // billing authority needed for request-path and background renewals.
392
400
  const reference = await submitSubscriptionPayment({
393
401
  accessKey,
402
+ feePayer: auto.feePayer,
403
+ feePayerPolicy: auto.feePayerPolicy,
394
404
  getClient: auto.getClient,
395
405
  keyAuthorization: auto.keyAuthorization,
396
406
  lookupKey: resolved.key,
@@ -689,6 +699,8 @@ function subscriptionBinding(request: SubscriptionRequest) {
689
699
  }
690
700
 
691
701
  function resolveRenewalHandler(parameters: {
702
+ feePayer?: ReturnType<typeof Account.resolve>['feePayer'] | undefined
703
+ feePayerPolicy?: Partial<FeePayer.Policy> | undefined
692
704
  getClient: (parameters: { chainId?: number | undefined }) => MaybePromise<ViemClient>
693
705
  parameters: {
694
706
  renew?:
@@ -710,6 +722,8 @@ function resolveRenewalHandler(parameters: {
710
722
  }) => Promise<subscription.RenewalResult>)
711
723
  | undefined {
712
724
  const {
725
+ feePayer,
726
+ feePayerPolicy,
713
727
  getClient,
714
728
  parameters: subscriptionParameters,
715
729
  store,
@@ -721,6 +735,8 @@ function resolveRenewalHandler(parameters: {
721
735
  return async ({ inFlightReference, periodIndex, subscription }) => {
722
736
  const reference = await submitSubscriptionPayment({
723
737
  accessKey: subscription.accessKey!,
738
+ feePayer,
739
+ feePayerPolicy,
724
740
  getClient,
725
741
  lookupKey: subscription.lookupKey,
726
742
  request: subscription,
@@ -744,6 +760,8 @@ function resolveRenewalHandler(parameters: {
744
760
 
745
761
  async function submitSubscriptionPayment(parameters: {
746
762
  accessKey: SubscriptionAccessKey
763
+ feePayer?: ReturnType<typeof Account.resolve>['feePayer'] | undefined
764
+ feePayerPolicy?: Partial<FeePayer.Policy> | undefined
747
765
  getClient: (parameters: { chainId?: number | undefined }) => MaybePromise<ViemClient>
748
766
  keyAuthorization?: `0x${string}` | undefined
749
767
  lookupKey: string
@@ -757,6 +775,8 @@ async function submitSubscriptionPayment(parameters: {
757
775
  }) {
758
776
  const {
759
777
  accessKey,
778
+ feePayer,
779
+ feePayerPolicy,
760
780
  getClient,
761
781
  keyAuthorization,
762
782
  lookupKey,
@@ -786,7 +806,7 @@ async function submitSubscriptionPayment(parameters: {
786
806
  challengeId: settlementReference,
787
807
  serverId: lookupKey,
788
808
  })
789
- const serializedTransaction = await signTransaction(client, {
809
+ const baseTransaction = {
790
810
  account,
791
811
  calls: [
792
812
  {
@@ -802,7 +822,39 @@ async function submitSubscriptionPayment(parameters: {
802
822
  ...(keyAuthorization
803
823
  ? { keyAuthorization: KeyAuthorization.deserialize(keyAuthorization) }
804
824
  : {}),
805
- } as never)
825
+ }
826
+ const serializedTransaction = await (async () => {
827
+ if (!feePayer) return await signTransaction(client, baseTransaction as never)
828
+ // For sponsored payments, prepare the tx via `prepareTransactionRequest`
829
+ // (without `feePayer: true`) so viem returns the chain's full
830
+ // proof-inclusive gas estimate. With `feePayer: true` viem sets a
831
+ // dummy sig + null feePayerSignature, dropping signature and key
832
+ // authorization verification costs — see chainConfig.js FIXME. We add
833
+ // a small buffer for fee-payer overhead, then flip `feePayer = true`
834
+ // and re-sign with the fee-payer-sponsored envelope.
835
+ const prepared = await prepareTransactionRequest(client, {
836
+ ...baseTransaction,
837
+ nonceKey: 'expiring',
838
+ } as never)
839
+ prepared.gas = (prepared.gas ?? 0n) + 5_000n
840
+ ;(prepared as Record<string, unknown>).feePayer = true
841
+ const userSerialized = await signTransaction(client, prepared as never)
842
+ const userTransaction = Transaction.deserialize(
843
+ userSerialized as Transaction.TransactionSerializedTempo,
844
+ )
845
+ const sponsored = FeePayer.prepareSponsoredTransaction({
846
+ account: feePayer,
847
+ chainId: chainId ?? client.chain!.id,
848
+ details: {
849
+ amount: String(request.amount),
850
+ currency: String(request.currency),
851
+ recipient: String(request.recipient),
852
+ },
853
+ ...(feePayerPolicy ? { policy: feePayerPolicy } : {}),
854
+ transaction: userTransaction as never,
855
+ })
856
+ return await signTransaction(client, sponsored as never)
857
+ })()
806
858
  const transaction = Transaction.deserialize(
807
859
  serializedTransaction as Transaction.TransactionSerializedTempo,
808
860
  )
@@ -810,6 +862,7 @@ async function submitSubscriptionPayment(parameters: {
810
862
  ...transaction,
811
863
  account: transaction.from,
812
864
  calls: transaction.calls,
865
+ feePayerSignature: undefined,
813
866
  } as never)
814
867
 
815
868
  if (!waitForConfirmation) {
@@ -950,6 +1003,12 @@ export declare namespace subscription {
950
1003
  * Keeps concurrent renewal safe while allowing recovery from abandoned attempts.
951
1004
  */
952
1005
  renewalTimeoutMs?: number | undefined
1006
+ /**
1007
+ * Override the fee-payer policy for sponsored subscription payments.
1008
+ * Useful when the access key + key authorization tx requires more gas
1009
+ * than the default policy allows.
1010
+ */
1011
+ feePayerPolicy?: Partial<FeePayer.Policy> | undefined
953
1012
  activate?:
954
1013
  | ((parameters: {
955
1014
  /** Custom activation must verify this access key matches the resolved subscription. */