mppx 0.12.0 → 0.13.1
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 +55 -0
- package/dist/Method.d.ts +7 -1
- package/dist/Method.js +7 -2
- package/dist/Receipt.d.ts +2 -0
- package/dist/Receipt.js +2 -0
- package/dist/cli/cli.d.ts +8 -0
- package/dist/cli/cli.js +134 -136
- package/dist/cli/plugins/index.js +12 -14
- package/dist/cli/plugins/tempo.js +9 -2
- package/dist/cli/utils.d.ts +11 -1
- package/dist/cli/utils.js +15 -2
- package/dist/cli/validate/challenge.d.ts +2 -0
- package/dist/cli/validate/challenge.js +19 -4
- package/dist/cli/validate/discovery.d.ts +1 -0
- package/dist/cli/validate/discovery.js +10 -1
- package/dist/cli/validate/payment.js +34 -12
- package/dist/client/Methods.d.ts +0 -2
- package/dist/client/Methods.js +0 -2
- package/dist/client/Mppx.js +1 -0
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/internal/Fetch.d.ts +2 -0
- package/dist/client/internal/Fetch.js +8 -2
- package/dist/client/internal/MethodChallenge.d.ts +1 -0
- package/dist/evm/Types.js +1 -1
- package/dist/evm/server/Charge.js +5 -0
- package/dist/integrations/x402/Evm.js +4 -3
- package/dist/internal/HeaderCodec.d.ts +2 -2
- package/dist/internal/version.d.ts +2 -2
- package/dist/internal/version.js +1 -1
- package/dist/mcp/client/McpClient.js +4 -7
- package/dist/middlewares/elysia.d.ts +11 -8
- package/dist/middlewares/elysia.js +67 -14
- package/dist/middlewares/express.js +13 -6
- package/dist/middlewares/hono.js +4 -4
- package/dist/middlewares/nextjs.js +9 -5
- package/dist/proxy/internal/Headers.js +65 -0
- package/dist/proxy/services/stripe.js +1 -0
- package/dist/server/Mppx.d.ts +13 -1
- package/dist/server/Mppx.js +85 -44
- package/dist/server/NodeListener.js +71 -6
- package/dist/server/Request.d.ts +11 -1
- package/dist/server/Request.js +132 -13
- package/dist/server/Transport.d.ts +14 -1
- package/dist/server/Transport.js +1 -0
- package/dist/stripe/server/Methods.d.ts +10 -3
- package/dist/stripe/server/Methods.js +17 -2
- package/dist/stripe/server/internal/html.gen.js +1 -1
- package/dist/tempo/Methods.d.ts +1 -0
- package/dist/tempo/Methods.js +1 -0
- package/dist/tempo/client/Charge.js +17 -13
- package/dist/tempo/client/Methods.d.ts +1 -5
- package/dist/tempo/client/Methods.js +10 -5
- package/dist/tempo/client/ResolveAccount.d.ts +3 -0
- package/dist/tempo/client/index.d.ts +0 -3
- package/dist/tempo/client/index.js +0 -2
- package/dist/tempo/internal/fee-payer.d.ts +6 -3
- package/dist/tempo/internal/fee-payer.js +23 -6
- package/dist/tempo/internal/machine-token-charge.d.ts +23 -0
- package/dist/tempo/internal/machine-token-charge.js +2 -1
- package/dist/tempo/legacy/index.d.ts +1 -3
- package/dist/tempo/legacy/index.js +1 -3
- package/dist/tempo/server/Charge.js +75 -46
- package/dist/tempo/server/Methods.d.ts +55 -10
- package/dist/tempo/server/Subscription.d.ts +1 -1
- package/dist/tempo/server/Subscription.js +31 -4
- package/dist/tempo/server/internal/html.gen.js +1 -1
- package/dist/tempo/server/internal/transport.js +28 -11
- package/dist/tempo/session/client/ChannelOps.js +5 -0
- package/dist/tempo/session/client/Session.d.ts +1 -0
- package/dist/tempo/session/client/Session.js +7 -4
- package/dist/tempo/session/client/SessionManager.js +3 -0
- package/dist/tempo/session/client/Transports.d.ts +8 -1
- package/dist/tempo/session/client/Transports.js +12 -3
- package/dist/tempo/session/precompile/Chain.js +16 -1
- package/dist/tempo/session/precompile/Protocol.d.ts +2 -0
- package/dist/tempo/session/precompile/Voucher.d.ts +16 -0
- package/dist/tempo/session/precompile/Voucher.js +48 -0
- package/dist/tempo/session/server/ChannelStore.d.ts +20 -2
- package/dist/tempo/session/server/ChannelStore.js +25 -4
- package/dist/tempo/session/server/CredentialVerification.d.ts +4 -1
- package/dist/tempo/session/server/CredentialVerification.js +34 -6
- package/dist/tempo/session/server/MeteredStream.d.ts +3 -2
- package/dist/tempo/session/server/MeteredStream.js +85 -29
- package/dist/tempo/session/server/Session.d.ts +2 -1
- package/dist/tempo/session/server/Session.js +2 -1
- package/dist/tempo/session/server/Settlement.d.ts +21 -0
- package/dist/tempo/session/server/Settlement.js +92 -17
- package/dist/tempo/session/server/Sse.d.ts +3 -1
- package/dist/tempo/session/server/Sse.js +105 -32
- package/dist/tempo/session/server/Transports.d.ts +24 -19
- package/dist/tempo/session/server/Transports.js +169 -58
- package/dist/tempo/session/server/Ws.d.ts +5 -1
- package/dist/tempo/session/server/Ws.js +75 -20
- package/dist/tempo/subscription/Store.d.ts +2 -0
- package/dist/tempo/subscription/Store.js +3 -0
- package/dist/validation/core.js +2 -1
- package/dist/x402/server/EvmCharge.d.ts +3 -1
- package/dist/x402/server/EvmCharge.js +14 -1
- package/package.json +6 -6
- package/dist/tempo/legacy/client/ChannelOps.d.ts +0 -69
- package/dist/tempo/legacy/client/ChannelOps.js +0 -168
- package/dist/tempo/legacy/client/Session.d.ts +0 -185
- package/dist/tempo/legacy/client/Session.js +0 -279
- package/dist/tempo/legacy/client/SessionManager.d.ts +0 -100
- package/dist/tempo/legacy/client/SessionManager.js +0 -706
- package/dist/tempo/legacy/client/index.d.ts +0 -7
- package/dist/tempo/legacy/client/index.js +0 -5
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as SignatureEnvelope from 'ox/tempo/SignatureEnvelope';
|
|
2
2
|
import { decodeFunctionData, formatUnits, keccak256, parseEventLogs, } from 'viem';
|
|
3
|
-
import { getTransactionReceipt, sendRawTransaction, sendRawTransactionSync, signTransaction, verifyTypedData, call as viem_call, } from 'viem/actions';
|
|
3
|
+
import { getTransaction, getTransactionReceipt, sendRawTransaction, sendRawTransactionSync, signTransaction, verifyTypedData, call as viem_call, } from 'viem/actions';
|
|
4
4
|
import { Abis, Actions, Transaction } from 'viem/tempo';
|
|
5
5
|
import { tempo as tempo_chain } from 'viem/tempo/chains';
|
|
6
6
|
import { PaymentError, VerificationFailedError } from '../../Errors.js';
|
|
@@ -116,6 +116,43 @@ export function charge(parameters = {}) {
|
|
|
116
116
|
supportedModes,
|
|
117
117
|
};
|
|
118
118
|
}
|
|
119
|
+
/** Validates a recognized payment route and retains its funding metadata. */
|
|
120
|
+
function validatePaymentRoute(calls, context, transfers, isFeePayerTx = false) {
|
|
121
|
+
const { amount, currency, machineTokenEnabled, recipient } = context;
|
|
122
|
+
const machineRoute = machineTokenEnabled
|
|
123
|
+
? MachineTokenCharge.matchRoute({
|
|
124
|
+
calls,
|
|
125
|
+
chainId: context.chainId ?? context.client.chain?.id,
|
|
126
|
+
currency,
|
|
127
|
+
transfers,
|
|
128
|
+
})
|
|
129
|
+
: undefined;
|
|
130
|
+
const matchedCalls = machineRoute?.transfers ??
|
|
131
|
+
assertTransferCalls(calls, {
|
|
132
|
+
currency,
|
|
133
|
+
exactCount: isFeePayerTx,
|
|
134
|
+
transfers,
|
|
135
|
+
});
|
|
136
|
+
assertChallengeBoundCallMemo(matchedCalls, {
|
|
137
|
+
challengeId: context.challenge.id,
|
|
138
|
+
realm: context.challenge.realm,
|
|
139
|
+
});
|
|
140
|
+
if (machineRoute)
|
|
141
|
+
return {
|
|
142
|
+
settlementSenders: [machineRoute.settlementSender],
|
|
143
|
+
fundingCurrency: machineRoute.calls[0].to,
|
|
144
|
+
};
|
|
145
|
+
try {
|
|
146
|
+
const fundingCurrency = FeePayer.validateCalls(calls, { amount, currency, recipient }, { currency, expectedTransfers: transfers });
|
|
147
|
+
return { settlementSenders: [], fundingCurrency };
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
if (isFeePayerTx)
|
|
151
|
+
throw error;
|
|
152
|
+
// Unrecognized routes remain valid for unsponsored payments, without funding metadata.
|
|
153
|
+
return { settlementSenders: [], fundingCurrency: undefined };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
119
156
|
async function validateHashCredential(credential, payload, context) {
|
|
120
157
|
const { amount, chainId, challenge, client, currency, methodDetails, recipient, settlementSenders, supportedModes, } = context;
|
|
121
158
|
if (supportedModes && !supportedModes.includes('push'))
|
|
@@ -141,7 +178,7 @@ export function charge(parameters = {}) {
|
|
|
141
178
|
challengeId: challenge.id,
|
|
142
179
|
realm: challenge.realm,
|
|
143
180
|
});
|
|
144
|
-
return { receipt
|
|
181
|
+
return { receipt, sender, transfers };
|
|
145
182
|
}
|
|
146
183
|
async function validateProofCredential(credential, payload, context) {
|
|
147
184
|
const { chainId, challenge, client, isZeroAmount } = context;
|
|
@@ -181,7 +218,7 @@ export function charge(parameters = {}) {
|
|
|
181
218
|
return { sender: source.address };
|
|
182
219
|
}
|
|
183
220
|
async function validateTransactionCredential(credential, payload, request, context) {
|
|
184
|
-
const {
|
|
221
|
+
const { amount, chainId, challenge, client, methodDetails, recipient, requestAllowsFeePayer, supportedModes, } = context;
|
|
185
222
|
if (supportedModes && !supportedModes.includes('pull'))
|
|
186
223
|
throw new MismatchError('Transaction credentials are not supported for this challenge.', {});
|
|
187
224
|
const serializedTransaction = payload.signature;
|
|
@@ -190,40 +227,30 @@ export function charge(parameters = {}) {
|
|
|
190
227
|
const transaction = Transaction.deserialize(serializedTransaction);
|
|
191
228
|
if (!transaction.signature || !transaction.from)
|
|
192
229
|
throw new MismatchError('Transaction must be signed by the sender before fee payer co-signing.', {});
|
|
230
|
+
if (credential.source) {
|
|
231
|
+
const source = Proof.parsePkhSource(credential.source);
|
|
232
|
+
const resolvedChainId = challenge.request.methodDetails?.chainId ?? chainId ?? client.chain?.id;
|
|
233
|
+
if (!source || source.chainId !== resolvedChainId)
|
|
234
|
+
throw new MismatchError('Transaction credential source is invalid.', {});
|
|
235
|
+
if (!TempoAddress.isEqual(source.address, transaction.from))
|
|
236
|
+
throw new MismatchError('Transaction credential source does not match the sender.', {});
|
|
237
|
+
}
|
|
193
238
|
const isFeePayerTx = methodDetails?.feePayer === true &&
|
|
194
239
|
requestAllowsFeePayer &&
|
|
195
240
|
!!(Account.is(request.feePayer) ? request.feePayer : feePayer || remoteFeePayer);
|
|
196
241
|
const transfers = getExpectedTransfers({ amount, methodDetails, recipient });
|
|
197
|
-
const
|
|
198
|
-
? MachineTokenCharge.matchRoute({
|
|
199
|
-
calls: transaction.calls ?? [],
|
|
200
|
-
chainId: chainId ?? client.chain?.id,
|
|
201
|
-
currency,
|
|
202
|
-
transfers,
|
|
203
|
-
})
|
|
204
|
-
: undefined;
|
|
205
|
-
const matchedCalls = machineTokenRoute?.transfers ??
|
|
206
|
-
assertTransferCalls(transaction.calls ?? [], {
|
|
207
|
-
currency,
|
|
208
|
-
exactCount: isFeePayerTx,
|
|
209
|
-
transfers,
|
|
210
|
-
});
|
|
211
|
-
assertChallengeBoundCallMemo(matchedCalls, {
|
|
212
|
-
challengeId: challenge.id,
|
|
213
|
-
realm: challenge.realm,
|
|
214
|
-
});
|
|
242
|
+
const { settlementSenders, fundingCurrency } = validatePaymentRoute(transaction.calls ?? [], context, transfers, isFeePayerTx);
|
|
215
243
|
if (isFeePayerTx) {
|
|
216
|
-
if (!machineTokenRoute)
|
|
217
|
-
FeePayer.validateCalls(transaction.calls, { amount, currency, recipient }, { currency, expectedTransfers: transfers });
|
|
218
244
|
FeePayer.assertAllowedFeeToken(transaction, context.allowedFeeTokens);
|
|
219
245
|
}
|
|
220
246
|
else {
|
|
221
247
|
await viem_call(client, FeePayer.simulationTransaction(transaction, { feePayer: false }));
|
|
222
248
|
}
|
|
223
249
|
return {
|
|
250
|
+
fundingCurrency,
|
|
224
251
|
isFeePayerTx,
|
|
225
252
|
serializedTransaction,
|
|
226
|
-
settlementSenders
|
|
253
|
+
settlementSenders,
|
|
227
254
|
transaction,
|
|
228
255
|
transfers,
|
|
229
256
|
};
|
|
@@ -244,7 +271,7 @@ export function charge(parameters = {}) {
|
|
|
244
271
|
return { details: { mode: 'proof', sender }, type: 'proof' };
|
|
245
272
|
}
|
|
246
273
|
case 'transaction': {
|
|
247
|
-
const { isFeePayerTx, serializedTransaction, settlementSenders, transaction, transfers } = await validateTransactionCredential(credential, context.payload, request, context);
|
|
274
|
+
const { fundingCurrency, isFeePayerTx, serializedTransaction, settlementSenders, transaction, transfers, } = await validateTransactionCredential(credential, context.payload, request, context);
|
|
248
275
|
return {
|
|
249
276
|
details: {
|
|
250
277
|
mode: 'pull',
|
|
@@ -252,6 +279,7 @@ export function charge(parameters = {}) {
|
|
|
252
279
|
serializedTransaction,
|
|
253
280
|
transfers,
|
|
254
281
|
},
|
|
282
|
+
fundingCurrency,
|
|
255
283
|
isFeePayerTx,
|
|
256
284
|
serializedTransaction,
|
|
257
285
|
settlementSenders,
|
|
@@ -383,7 +411,19 @@ export function charge(parameters = {}) {
|
|
|
383
411
|
reason: 'Transaction hash has already been used',
|
|
384
412
|
});
|
|
385
413
|
}
|
|
386
|
-
|
|
414
|
+
// Only final hash receipts need this lookup; validation stays receipt-only.
|
|
415
|
+
const transaction = await getTransaction(client, { hash }).catch(() => undefined);
|
|
416
|
+
let fundingCurrency;
|
|
417
|
+
if (transaction && TempoAddress.isEqual(transaction.from, validated.details.sender)) {
|
|
418
|
+
try {
|
|
419
|
+
fundingCurrency = validatePaymentRoute(transaction.calls ??
|
|
420
|
+
(transaction.to ? [{ to: transaction.to, data: transaction.input }] : []), context, validated.details.transfers).fundingCurrency;
|
|
421
|
+
}
|
|
422
|
+
catch {
|
|
423
|
+
// Unrecognized calls do not invalidate an already verified payment.
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return toReceipt(receipt.transactionHash, fundingCurrency);
|
|
387
427
|
}
|
|
388
428
|
case 'proof': {
|
|
389
429
|
if (proofStore && !(await tryClaimProof(proofStore, challenge.id, replayExpires))) {
|
|
@@ -391,15 +431,10 @@ export function charge(parameters = {}) {
|
|
|
391
431
|
reason: 'Proof credential has already been used',
|
|
392
432
|
});
|
|
393
433
|
}
|
|
394
|
-
return
|
|
395
|
-
method: 'tempo',
|
|
396
|
-
status: 'success',
|
|
397
|
-
timestamp: new Date().toISOString(),
|
|
398
|
-
reference: challenge.id,
|
|
399
|
-
};
|
|
434
|
+
return toReceipt(challenge.id);
|
|
400
435
|
}
|
|
401
436
|
case 'transaction': {
|
|
402
|
-
const { isFeePayerTx, serializedTransaction, settlementSenders, transaction, transfers } = validated;
|
|
437
|
+
const { fundingCurrency, isFeePayerTx, serializedTransaction, settlementSenders, transaction, transfers, } = validated;
|
|
403
438
|
// Pre-broadcast dedup: catch exact byte-for-byte replays early.
|
|
404
439
|
const hash = keccak256(serializedTransaction);
|
|
405
440
|
if (!(await tryClaimHash(store, hash, replayExpires))) {
|
|
@@ -541,7 +576,7 @@ export function charge(parameters = {}) {
|
|
|
541
576
|
throw new VerificationFailedError({
|
|
542
577
|
reason: 'Broadcast transaction hash does not match the signed transaction',
|
|
543
578
|
});
|
|
544
|
-
return toReceipt(receipt);
|
|
579
|
+
return toReceipt(receipt.transactionHash, fundingCurrency);
|
|
545
580
|
}
|
|
546
581
|
// Optimistic path: broadcast without waiting for confirmation
|
|
547
582
|
// (simulation above already ran). The returned receipt assumes
|
|
@@ -558,12 +593,7 @@ export function charge(parameters = {}) {
|
|
|
558
593
|
throw new VerificationFailedError({
|
|
559
594
|
reason: 'Sponsor budget reservation ownership was lost after broadcast',
|
|
560
595
|
});
|
|
561
|
-
return
|
|
562
|
-
method: 'tempo',
|
|
563
|
-
status: 'success',
|
|
564
|
-
timestamp: new Date().toISOString(),
|
|
565
|
-
reference,
|
|
566
|
-
};
|
|
596
|
+
return toReceipt(reference, fundingCurrency);
|
|
567
597
|
}
|
|
568
598
|
catch (error) {
|
|
569
599
|
if (!broadcastAttempted) {
|
|
@@ -694,6 +724,8 @@ function decodeTransferCall(call, currency) {
|
|
|
694
724
|
return null;
|
|
695
725
|
}
|
|
696
726
|
async function assertTransferLogs(receipt, parameters) {
|
|
727
|
+
if (receipt.status !== 'success')
|
|
728
|
+
throw new Error(`Transaction reverted: ${receipt.transactionHash}`);
|
|
697
729
|
const logs = getTransferLogEffects(receipt);
|
|
698
730
|
const used = new Set();
|
|
699
731
|
const matched = [];
|
|
@@ -896,16 +928,13 @@ async function isActiveAccessKey(client, parameters) {
|
|
|
896
928
|
}
|
|
897
929
|
}
|
|
898
930
|
/** @internal */
|
|
899
|
-
function toReceipt(
|
|
900
|
-
const { status, transactionHash } = receipt;
|
|
901
|
-
if (status !== 'success') {
|
|
902
|
-
throw new Error(`Transaction reverted: ${transactionHash}`);
|
|
903
|
-
}
|
|
931
|
+
function toReceipt(reference, fundingCurrency) {
|
|
904
932
|
return {
|
|
933
|
+
...(fundingCurrency ? { fundingCurrency } : {}),
|
|
905
934
|
method: 'tempo',
|
|
906
935
|
status: 'success',
|
|
907
936
|
timestamp: new Date().toISOString(),
|
|
908
|
-
reference
|
|
937
|
+
reference,
|
|
909
938
|
};
|
|
910
939
|
}
|
|
911
940
|
function assertChallengeBoundMemo(matchedLogs, parameters) {
|