mppx 0.6.25 → 0.6.26
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 +8 -0
- package/dist/Method.d.ts +6 -4
- package/dist/Method.d.ts.map +1 -1
- package/dist/Method.js +2 -1
- package/dist/Method.js.map +1 -1
- package/dist/middlewares/internal/mppx.d.ts +3 -2
- package/dist/middlewares/internal/mppx.d.ts.map +1 -1
- package/dist/middlewares/internal/mppx.js +1 -0
- package/dist/middlewares/internal/mppx.js.map +1 -1
- package/dist/server/Mppx.d.ts +8 -3
- package/dist/server/Mppx.d.ts.map +1 -1
- package/dist/server/Mppx.js +4 -1
- package/dist/server/Mppx.js.map +1 -1
- package/dist/stripe/server/Charge.d.ts +1 -1
- package/dist/stripe/server/Charge.d.ts.map +1 -1
- package/dist/stripe/server/Methods.d.ts +1 -1
- package/dist/stripe/server/Methods.d.ts.map +1 -1
- package/dist/tempo/Methods.d.ts +3 -2
- package/dist/tempo/Methods.d.ts.map +1 -1
- package/dist/tempo/Methods.js +13 -4
- package/dist/tempo/Methods.js.map +1 -1
- package/dist/tempo/client/Subscription.d.ts +3 -2
- package/dist/tempo/client/Subscription.d.ts.map +1 -1
- package/dist/tempo/server/Charge.d.ts +1 -1
- package/dist/tempo/server/Charge.d.ts.map +1 -1
- package/dist/tempo/server/Methods.d.ts +2 -2
- package/dist/tempo/server/Methods.d.ts.map +1 -1
- package/dist/tempo/server/Session.d.ts +1 -1
- package/dist/tempo/server/Session.d.ts.map +1 -1
- package/dist/tempo/server/Subscription.d.ts +28 -12
- package/dist/tempo/server/Subscription.d.ts.map +1 -1
- package/dist/tempo/server/Subscription.js +82 -30
- package/dist/tempo/server/Subscription.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/subscription/KeyAuthorization.d.ts +3 -14
- package/dist/tempo/subscription/KeyAuthorization.d.ts.map +1 -1
- package/dist/tempo/subscription/KeyAuthorization.js +11 -20
- package/dist/tempo/subscription/KeyAuthorization.js.map +1 -1
- package/dist/tempo/subscription/Types.d.ts +2 -2
- package/dist/tempo/subscription/Types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/Method.ts +21 -5
- package/src/middlewares/internal/mppx.test.ts +24 -0
- package/src/middlewares/internal/mppx.ts +6 -2
- package/src/server/Mppx.ts +17 -4
- package/src/tempo/Methods.test.ts +17 -0
- package/src/tempo/Methods.ts +12 -4
- package/src/tempo/client/Subscription.test.ts +5 -7
- package/src/tempo/server/Subscription.test.ts +1 -4
- package/src/tempo/server/Subscription.ts +156 -67
- package/src/tempo/server/internal/html/package.json +1 -1
- package/src/tempo/server/internal/html.gen.ts +1 -1
- package/src/tempo/subscription/KeyAuthorization.test.ts +13 -4
- package/src/tempo/subscription/KeyAuthorization.ts +11 -20
- package/src/tempo/subscription/Types.ts +2 -2
|
@@ -41,6 +41,7 @@ import type {
|
|
|
41
41
|
} from '../subscription/Types.js'
|
|
42
42
|
|
|
43
43
|
type SubscriptionRequest = ReturnType<typeof Methods.subscription.schema.request.parse>
|
|
44
|
+
type RenewalHandler = NonNullable<subscription.Parameters['renew']>
|
|
44
45
|
|
|
45
46
|
/**
|
|
46
47
|
* Creates a Tempo subscription method for recurring TIP-20 token payments.
|
|
@@ -67,22 +68,25 @@ export function subscription<const parameters extends subscription.Parameters>(
|
|
|
67
68
|
periodCount,
|
|
68
69
|
periodUnit,
|
|
69
70
|
subscriptionExpires,
|
|
70
|
-
waitForConfirmation = true,
|
|
71
71
|
} = parameters
|
|
72
72
|
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
getClient: parameters.getClient,
|
|
81
|
-
rpcUrl: defaults.rpcUrl,
|
|
73
|
+
const { recipient } = Account.resolve(parameters)
|
|
74
|
+
const context = createContext(parameters, {
|
|
75
|
+
store: rawStore,
|
|
76
|
+
options: {
|
|
77
|
+
activationTimeoutMs: parameters.activationTimeoutMs,
|
|
78
|
+
renewalTimeoutMs: parameters.renewalTimeoutMs,
|
|
79
|
+
},
|
|
82
80
|
})
|
|
81
|
+
const { feePayer, feePayerPolicy, getClient, store, waitForConfirmation } = context
|
|
83
82
|
|
|
84
83
|
type Defaults = subscription.DeriveDefaults<parameters>
|
|
85
|
-
|
|
84
|
+
const method = Method.toServer<
|
|
85
|
+
typeof Methods.subscription,
|
|
86
|
+
Defaults,
|
|
87
|
+
undefined,
|
|
88
|
+
subscription.Extensions
|
|
89
|
+
>(Methods.subscription, {
|
|
86
90
|
defaults: {
|
|
87
91
|
amount,
|
|
88
92
|
currency,
|
|
@@ -94,6 +98,19 @@ export function subscription<const parameters extends subscription.Parameters>(
|
|
|
94
98
|
recipient,
|
|
95
99
|
subscriptionExpires,
|
|
96
100
|
} as unknown as Defaults,
|
|
101
|
+
extensions: {
|
|
102
|
+
renew: (parameters) =>
|
|
103
|
+
renewWithContext({
|
|
104
|
+
context: {
|
|
105
|
+
...context,
|
|
106
|
+
feePayerPolicy: parameters.feePayerPolicy
|
|
107
|
+
? resolveFeePayerPolicy(context.feePayerPolicy, parameters.feePayerPolicy)
|
|
108
|
+
: context.feePayerPolicy,
|
|
109
|
+
waitForConfirmation: parameters.waitForConfirmation ?? context.waitForConfirmation,
|
|
110
|
+
},
|
|
111
|
+
subscriptionId: parameters.subscriptionId,
|
|
112
|
+
}),
|
|
113
|
+
},
|
|
97
114
|
|
|
98
115
|
async authorize({ input, request }) {
|
|
99
116
|
const resolved = await parameters.resolve({ input, request })
|
|
@@ -107,7 +124,7 @@ export function subscription<const parameters extends subscription.Parameters>(
|
|
|
107
124
|
if (periodIndex > subscription.lastChargedPeriod) {
|
|
108
125
|
const renew = resolveRenewalHandler({
|
|
109
126
|
feePayer,
|
|
110
|
-
feePayerPolicy
|
|
127
|
+
feePayerPolicy,
|
|
111
128
|
getClient,
|
|
112
129
|
parameters,
|
|
113
130
|
store,
|
|
@@ -226,7 +243,9 @@ export function subscription<const parameters extends subscription.Parameters>(
|
|
|
226
243
|
(declaredSource.chainId !== verified.source.chainId ||
|
|
227
244
|
!isAddressEqual(declaredSource.address, verified.source.address))
|
|
228
245
|
) {
|
|
229
|
-
throw new VerificationFailedError({
|
|
246
|
+
throw new VerificationFailedError({
|
|
247
|
+
reason: 'credential source does not match signature',
|
|
248
|
+
})
|
|
230
249
|
}
|
|
231
250
|
|
|
232
251
|
const activation = await store.activate({
|
|
@@ -240,7 +259,7 @@ export function subscription<const parameters extends subscription.Parameters>(
|
|
|
240
259
|
auto: {
|
|
241
260
|
challengeId: credential.challenge.id,
|
|
242
261
|
feePayer,
|
|
243
|
-
feePayerPolicy
|
|
262
|
+
feePayerPolicy,
|
|
244
263
|
getClient,
|
|
245
264
|
keyAuthorization: (credential.payload as SubscriptionCredentialPayload).signature,
|
|
246
265
|
realm: credential.challenge.realm,
|
|
@@ -290,6 +309,19 @@ export function subscription<const parameters extends subscription.Parameters>(
|
|
|
290
309
|
return activation.result.receipt
|
|
291
310
|
},
|
|
292
311
|
})
|
|
312
|
+
return method
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Access-key provisioning can cost around 4M gas.
|
|
316
|
+
const defaultFeePayerPolicy = {
|
|
317
|
+
maxGas: 5_000_000n,
|
|
318
|
+
maxTotalFee: 200_000_000_000_000_000n,
|
|
319
|
+
} satisfies Partial<FeePayer.Policy>
|
|
320
|
+
|
|
321
|
+
function resolveFeePayerPolicy(
|
|
322
|
+
...policies: (Partial<FeePayer.Policy> | undefined)[]
|
|
323
|
+
): Partial<FeePayer.Policy> {
|
|
324
|
+
return Object.assign({}, defaultFeePayerPolicy, ...policies)
|
|
293
325
|
}
|
|
294
326
|
|
|
295
327
|
function requestFromCaptured(capturedRequest: Method.CapturedRequest | undefined): Request {
|
|
@@ -702,25 +734,11 @@ function resolveRenewalHandler(parameters: {
|
|
|
702
734
|
feePayer?: ReturnType<typeof Account.resolve>['feePayer'] | undefined
|
|
703
735
|
feePayerPolicy?: Partial<FeePayer.Policy> | undefined
|
|
704
736
|
getClient: (parameters: { chainId?: number | undefined }) => MaybePromise<ViemClient>
|
|
705
|
-
parameters:
|
|
706
|
-
renew?:
|
|
707
|
-
| ((parameters: {
|
|
708
|
-
inFlightReference: string
|
|
709
|
-
periodIndex: number
|
|
710
|
-
subscription: SubscriptionRecord
|
|
711
|
-
}) => Promise<subscription.RenewalResult>)
|
|
712
|
-
| undefined
|
|
713
|
-
}
|
|
737
|
+
parameters: Pick<createContext.Parameters, 'renew'>
|
|
714
738
|
store: SubscriptionStore.SubscriptionStore
|
|
715
739
|
subscription: SubscriptionRecord
|
|
716
740
|
waitForConfirmation: boolean
|
|
717
|
-
}):
|
|
718
|
-
| ((parameters: {
|
|
719
|
-
inFlightReference: string
|
|
720
|
-
periodIndex: number
|
|
721
|
-
subscription: SubscriptionRecord
|
|
722
|
-
}) => Promise<subscription.RenewalResult>)
|
|
723
|
-
| undefined {
|
|
741
|
+
}): RenewalHandler | undefined {
|
|
724
742
|
const {
|
|
725
743
|
feePayer,
|
|
726
744
|
feePayerPolicy,
|
|
@@ -829,7 +847,7 @@ async function submitSubscriptionPayment(parameters: {
|
|
|
829
847
|
// (without `feePayer: true`) so viem returns the chain's full
|
|
830
848
|
// proof-inclusive gas estimate. With `feePayer: true` viem sets a
|
|
831
849
|
// dummy sig + null feePayerSignature, dropping signature and key
|
|
832
|
-
// authorization verification costs
|
|
850
|
+
// authorization verification costs -- see chainConfig.js FIXME. We add
|
|
833
851
|
// a small buffer for fee-payer overhead, then flip `feePayer = true`
|
|
834
852
|
// and re-sign with the fee-payer-sponsored envelope.
|
|
835
853
|
const prepared = await prepareTransactionRequest(client, {
|
|
@@ -895,31 +913,41 @@ function createSubscriptionId() {
|
|
|
895
913
|
* Returns the renewal result if the subscription was overdue, or `null` if already current.
|
|
896
914
|
*/
|
|
897
915
|
export async function renew(parameters: renew.Parameters): Promise<renew.Result | null> {
|
|
898
|
-
const
|
|
899
|
-
|
|
900
|
-
|
|
916
|
+
const context = createContext(parameters, {
|
|
917
|
+
store: parameters.store,
|
|
918
|
+
options: {
|
|
919
|
+
renewalTimeoutMs: parameters.renewalTimeoutMs,
|
|
920
|
+
},
|
|
901
921
|
})
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
922
|
+
|
|
923
|
+
return renewWithContext({
|
|
924
|
+
context,
|
|
925
|
+
subscriptionId: parameters.subscriptionId,
|
|
906
926
|
})
|
|
927
|
+
}
|
|
907
928
|
|
|
908
|
-
|
|
929
|
+
async function renewWithContext(parameters: {
|
|
930
|
+
context: createContext.Return
|
|
931
|
+
subscriptionId: string
|
|
932
|
+
}): Promise<renew.Result | null> {
|
|
933
|
+
const { context, subscriptionId } = parameters
|
|
934
|
+
const record = await context.store.get(subscriptionId)
|
|
909
935
|
if (!record) return null
|
|
910
936
|
if (!isActive(record)) return null
|
|
911
|
-
const active = await store.getByKey(record.lookupKey)
|
|
937
|
+
const active = await context.store.getByKey(record.lookupKey)
|
|
912
938
|
if (active?.subscriptionId !== record.subscriptionId) return null
|
|
913
939
|
|
|
914
940
|
const periodIndex = getPeriodIndex(record)
|
|
915
941
|
if (periodIndex <= record.lastChargedPeriod) return null
|
|
916
942
|
|
|
917
943
|
const renew = resolveRenewalHandler({
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
944
|
+
feePayer: context.feePayer,
|
|
945
|
+
feePayerPolicy: context.feePayerPolicy,
|
|
946
|
+
getClient: context.getClient,
|
|
947
|
+
parameters: context.parameters,
|
|
948
|
+
store: context.store,
|
|
921
949
|
subscription: record,
|
|
922
|
-
waitForConfirmation,
|
|
950
|
+
waitForConfirmation: context.waitForConfirmation,
|
|
923
951
|
})
|
|
924
952
|
if (!renew) return null
|
|
925
953
|
|
|
@@ -927,35 +955,41 @@ export async function renew(parameters: renew.Parameters): Promise<renew.Result
|
|
|
927
955
|
expectedLookupKey: record.lookupKey,
|
|
928
956
|
periodIndex,
|
|
929
957
|
renew,
|
|
930
|
-
store,
|
|
958
|
+
store: context.store,
|
|
931
959
|
subscription: record,
|
|
932
960
|
})
|
|
933
|
-
|
|
961
|
+
if (renewal?.status !== 'renewed') return null
|
|
962
|
+
|
|
963
|
+
await context.parameters.hooks?.renewed?.({
|
|
964
|
+
periodIndex,
|
|
965
|
+
receipt: renewal.result.receipt,
|
|
966
|
+
subscription: renewal.result.subscription,
|
|
967
|
+
})
|
|
968
|
+
return renewal.result
|
|
934
969
|
}
|
|
935
970
|
|
|
936
971
|
export declare namespace renew {
|
|
937
972
|
/** Parameters for renewing an overdue subscription outside the request path. */
|
|
938
|
-
type Parameters =
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
} & Client.getResolver.Parameters
|
|
973
|
+
type Parameters = Account.resolve.Parameters &
|
|
974
|
+
Client.getResolver.Parameters & {
|
|
975
|
+
/** The subscription to renew. */
|
|
976
|
+
subscriptionId: string
|
|
977
|
+
/** Billing callback -- same signature as the `renew` hook on {@link subscription}. */
|
|
978
|
+
renew?: subscription.Parameters['renew']
|
|
979
|
+
/** Store containing subscription records. */
|
|
980
|
+
store: Store.AtomicStore<Record<string, unknown>>
|
|
981
|
+
/**
|
|
982
|
+
* Milliseconds before an in-flight renewal lock can be replaced.
|
|
983
|
+
* Keeps concurrent renewal safe while allowing recovery from abandoned attempts.
|
|
984
|
+
*/
|
|
985
|
+
renewalTimeoutMs?: number | undefined
|
|
986
|
+
/**
|
|
987
|
+
* Override the fee-payer policy for sponsored subscription payments.
|
|
988
|
+
* Useful when the access key renewal tx requires more gas than the default policy allows.
|
|
989
|
+
*/
|
|
990
|
+
feePayerPolicy?: Partial<FeePayer.Policy> | undefined
|
|
991
|
+
waitForConfirmation?: boolean | undefined
|
|
992
|
+
}
|
|
959
993
|
|
|
960
994
|
/** Renewal result returned by {@link renew}. */
|
|
961
995
|
type Result = subscription.RenewalResult
|
|
@@ -977,6 +1011,23 @@ export declare namespace subscription {
|
|
|
977
1011
|
subscription: SubscriptionRecord
|
|
978
1012
|
}
|
|
979
1013
|
|
|
1014
|
+
/** Parameters for renewing through a configured `mppx.tempo.subscription` handler. */
|
|
1015
|
+
type RenewParameters = {
|
|
1016
|
+
/** The subscription to renew. */
|
|
1017
|
+
subscriptionId: string
|
|
1018
|
+
/**
|
|
1019
|
+
* Override the fee-payer policy for sponsored subscription payments.
|
|
1020
|
+
* Useful when the access key renewal tx requires more gas than the default policy allows.
|
|
1021
|
+
*/
|
|
1022
|
+
feePayerPolicy?: Partial<FeePayer.Policy> | undefined
|
|
1023
|
+
waitForConfirmation?: boolean | undefined
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
/** Handler extensions attached to `mppx.tempo.subscription`. */
|
|
1027
|
+
type Extensions = {
|
|
1028
|
+
renew: (parameters: RenewParameters) => Promise<renew.Result | null>
|
|
1029
|
+
}
|
|
1030
|
+
|
|
980
1031
|
/** Request defaults supported by the subscription method. */
|
|
981
1032
|
type Defaults = LooseOmit<
|
|
982
1033
|
Method.RequestDefaults<typeof Methods.subscription>,
|
|
@@ -1071,3 +1122,41 @@ export declare namespace subscription {
|
|
|
1071
1122
|
decimals: number
|
|
1072
1123
|
}
|
|
1073
1124
|
}
|
|
1125
|
+
|
|
1126
|
+
function createContext<const parameters extends createContext.Parameters>(
|
|
1127
|
+
parameters: parameters,
|
|
1128
|
+
options: createContext.Options,
|
|
1129
|
+
) {
|
|
1130
|
+
const { feePayer } = Account.resolve(parameters)
|
|
1131
|
+
const store = SubscriptionStore.fromStore(options.store, options.options)
|
|
1132
|
+
const getClient = ClientResolver.getResolver({
|
|
1133
|
+
chain: tempo_chain,
|
|
1134
|
+
getClient: parameters.getClient,
|
|
1135
|
+
rpcUrl: defaults.rpcUrl,
|
|
1136
|
+
})
|
|
1137
|
+
return {
|
|
1138
|
+
feePayer,
|
|
1139
|
+
feePayerPolicy: resolveFeePayerPolicy(parameters.feePayerPolicy),
|
|
1140
|
+
getClient,
|
|
1141
|
+
parameters,
|
|
1142
|
+
store,
|
|
1143
|
+
waitForConfirmation: parameters.waitForConfirmation ?? true,
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
declare namespace createContext {
|
|
1148
|
+
type Parameters = Account.resolve.Parameters &
|
|
1149
|
+
Client.getResolver.Parameters & {
|
|
1150
|
+
feePayerPolicy?: Partial<FeePayer.Policy> | undefined
|
|
1151
|
+
hooks?: subscription.Parameters['hooks']
|
|
1152
|
+
renew?: subscription.Parameters['renew']
|
|
1153
|
+
waitForConfirmation?: boolean | undefined
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
type Options = {
|
|
1157
|
+
store: Store.AtomicStore<Record<string, unknown>>
|
|
1158
|
+
options?: SubscriptionStore.fromStore.Options | undefined
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
type Return = ReturnType<typeof createContext>
|
|
1162
|
+
}
|