mppx 0.12.0 → 0.13.2
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 +64 -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/sessions/request.d.ts +1 -1
- package/dist/cli/sessions/request.js +6 -8
- 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 +22 -7
- 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 +14 -2
- 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 +87 -53
- 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))) {
|
|
@@ -409,6 +444,9 @@ export function charge(parameters = {}) {
|
|
|
409
444
|
}
|
|
410
445
|
let broadcastAttempted = false;
|
|
411
446
|
let finalHash;
|
|
447
|
+
// Set only after this request claims the final hash, so cleanup never
|
|
448
|
+
// releases a marker that another request holds.
|
|
449
|
+
let claimedFinalHash;
|
|
412
450
|
let reservation;
|
|
413
451
|
try {
|
|
414
452
|
const { allowedFeeTokens } = context;
|
|
@@ -475,11 +513,13 @@ export function charge(parameters = {}) {
|
|
|
475
513
|
// Broadcast the same canonical envelope so replay keys match the node reference.
|
|
476
514
|
const serializedTransaction_final = await Transaction.serialize(Transaction.deserialize(completedTransaction.serializedTransaction));
|
|
477
515
|
finalHash = keccak256(serializedTransaction_final);
|
|
478
|
-
if (finalHash.toLowerCase() !== hash.toLowerCase()
|
|
479
|
-
!(await tryClaimHash(store, finalHash, replayExpires)))
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
516
|
+
if (finalHash.toLowerCase() !== hash.toLowerCase()) {
|
|
517
|
+
if (!(await tryClaimHash(store, finalHash, replayExpires)))
|
|
518
|
+
throw new VerificationFailedError({
|
|
519
|
+
reason: 'Transaction hash has already been used',
|
|
520
|
+
});
|
|
521
|
+
claimedFinalHash = finalHash;
|
|
522
|
+
}
|
|
483
523
|
if (isFeePayerTx) {
|
|
484
524
|
const sponsor = completedTransaction.sponsor;
|
|
485
525
|
if (!sponsor)
|
|
@@ -541,7 +581,7 @@ export function charge(parameters = {}) {
|
|
|
541
581
|
throw new VerificationFailedError({
|
|
542
582
|
reason: 'Broadcast transaction hash does not match the signed transaction',
|
|
543
583
|
});
|
|
544
|
-
return toReceipt(receipt);
|
|
584
|
+
return toReceipt(receipt.transactionHash, fundingCurrency);
|
|
545
585
|
}
|
|
546
586
|
// Optimistic path: broadcast without waiting for confirmation
|
|
547
587
|
// (simulation above already ran). The returned receipt assumes
|
|
@@ -558,19 +598,14 @@ export function charge(parameters = {}) {
|
|
|
558
598
|
throw new VerificationFailedError({
|
|
559
599
|
reason: 'Sponsor budget reservation ownership was lost after broadcast',
|
|
560
600
|
});
|
|
561
|
-
return
|
|
562
|
-
method: 'tempo',
|
|
563
|
-
status: 'success',
|
|
564
|
-
timestamp: new Date().toISOString(),
|
|
565
|
-
reference,
|
|
566
|
-
};
|
|
601
|
+
return toReceipt(reference, fundingCurrency);
|
|
567
602
|
}
|
|
568
603
|
catch (error) {
|
|
569
604
|
if (!broadcastAttempted) {
|
|
570
605
|
if (reservation)
|
|
571
606
|
await SponsorBudget.release(sponsorBudgetStore, reservation);
|
|
572
|
-
if (
|
|
573
|
-
await releaseHashUse(store,
|
|
607
|
+
if (claimedFinalHash)
|
|
608
|
+
await releaseHashUse(store, claimedFinalHash);
|
|
574
609
|
await releaseHashUse(store, hash);
|
|
575
610
|
}
|
|
576
611
|
throw error;
|
|
@@ -694,6 +729,8 @@ function decodeTransferCall(call, currency) {
|
|
|
694
729
|
return null;
|
|
695
730
|
}
|
|
696
731
|
async function assertTransferLogs(receipt, parameters) {
|
|
732
|
+
if (receipt.status !== 'success')
|
|
733
|
+
throw new Error(`Transaction reverted: ${receipt.transactionHash}`);
|
|
697
734
|
const logs = getTransferLogEffects(receipt);
|
|
698
735
|
const used = new Set();
|
|
699
736
|
const matched = [];
|
|
@@ -896,16 +933,13 @@ async function isActiveAccessKey(client, parameters) {
|
|
|
896
933
|
}
|
|
897
934
|
}
|
|
898
935
|
/** @internal */
|
|
899
|
-
function toReceipt(
|
|
900
|
-
const { status, transactionHash } = receipt;
|
|
901
|
-
if (status !== 'success') {
|
|
902
|
-
throw new Error(`Transaction reverted: ${transactionHash}`);
|
|
903
|
-
}
|
|
936
|
+
function toReceipt(reference, fundingCurrency) {
|
|
904
937
|
return {
|
|
938
|
+
...(fundingCurrency ? { fundingCurrency } : {}),
|
|
905
939
|
method: 'tempo',
|
|
906
940
|
status: 'success',
|
|
907
941
|
timestamp: new Date().toISOString(),
|
|
908
|
-
reference
|
|
942
|
+
reference,
|
|
909
943
|
};
|
|
910
944
|
}
|
|
911
945
|
function assertChallengeBoundMemo(matchedLogs, parameters) {
|