@agentcash/router 1.1.10 → 1.2.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/dist/index.cjs +196 -14
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +196 -14
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -625,8 +625,10 @@ function resolveMaxPrice(pricing) {
|
|
|
625
625
|
}
|
|
626
626
|
|
|
627
627
|
// src/orchestrate.ts
|
|
628
|
-
var
|
|
629
|
-
var
|
|
628
|
+
var import_mppx2 = require("mppx");
|
|
629
|
+
var import_actions = require("viem/actions");
|
|
630
|
+
var import_tempo = require("viem/tempo");
|
|
631
|
+
var import_viem2 = require("viem");
|
|
630
632
|
|
|
631
633
|
// src/protocols/x402.ts
|
|
632
634
|
init_x402_facilitators();
|
|
@@ -898,6 +900,22 @@ async function buildSIWXExtension() {
|
|
|
898
900
|
return declareSIWxExtension();
|
|
899
901
|
}
|
|
900
902
|
|
|
903
|
+
// src/auth/mpp-siwx.ts
|
|
904
|
+
var import_mppx = require("mppx");
|
|
905
|
+
var import_viem = require("viem");
|
|
906
|
+
async function verifyMppSiwx(request, mppx) {
|
|
907
|
+
const result = await mppx.charge({ amount: "0" })(request);
|
|
908
|
+
if (result.status === 402) {
|
|
909
|
+
return { valid: false, challenge: result.challenge };
|
|
910
|
+
}
|
|
911
|
+
const credential = import_mppx.Credential.fromRequest(request);
|
|
912
|
+
const rawSource = credential?.source ?? "";
|
|
913
|
+
const didParts = rawSource.split(":");
|
|
914
|
+
const lastPart = didParts[didParts.length - 1];
|
|
915
|
+
const wallet = normalizeWalletAddress((0, import_viem.isAddress)(lastPart) ? (0, import_viem.getAddress)(lastPart) : rawSource);
|
|
916
|
+
return { valid: true, wallet, withReceipt: result.withReceipt };
|
|
917
|
+
}
|
|
918
|
+
|
|
901
919
|
// src/auth/api-key.ts
|
|
902
920
|
async function verifyApiKey(request, resolver) {
|
|
903
921
|
const apiKey = request.headers.get("X-API-Key") ?? extractBearerToken(request.headers.get("Authorization"));
|
|
@@ -1063,6 +1081,33 @@ function createRequestHandler(routeEntry, handler, deps) {
|
|
|
1063
1081
|
}
|
|
1064
1082
|
}
|
|
1065
1083
|
const siwxHeader = request.headers.get("SIGN-IN-WITH-X");
|
|
1084
|
+
if (!siwxHeader && protocol === "mpp" && routeEntry.authMode === "siwx" && deps.mppx) {
|
|
1085
|
+
let mppSiwxResult;
|
|
1086
|
+
try {
|
|
1087
|
+
mppSiwxResult = await verifyMppSiwx(request, deps.mppx);
|
|
1088
|
+
} catch (err) {
|
|
1089
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1090
|
+
firePluginHook(deps.plugin, "onAlert", pluginCtx, {
|
|
1091
|
+
level: "critical",
|
|
1092
|
+
message: `MPP SIWX verification failed: ${message}`,
|
|
1093
|
+
route: routeEntry.key
|
|
1094
|
+
});
|
|
1095
|
+
return fail(500, `MPP SIWX verification failed: ${message}`, meta, pluginCtx);
|
|
1096
|
+
}
|
|
1097
|
+
if (mppSiwxResult.valid) {
|
|
1098
|
+
pluginCtx.setVerifiedWallet(mppSiwxResult.wallet);
|
|
1099
|
+
firePluginHook(deps.plugin, "onAuthVerified", pluginCtx, {
|
|
1100
|
+
authMode: "siwx",
|
|
1101
|
+
wallet: mppSiwxResult.wallet,
|
|
1102
|
+
route: routeEntry.key
|
|
1103
|
+
});
|
|
1104
|
+
const authResponse = await handleAuth(mppSiwxResult.wallet, void 0);
|
|
1105
|
+
if (authResponse.status < 400) {
|
|
1106
|
+
return mppSiwxResult.withReceipt(authResponse);
|
|
1107
|
+
}
|
|
1108
|
+
return authResponse;
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1066
1111
|
if (!siwxHeader && routeEntry.authMode === "siwx") {
|
|
1067
1112
|
const url = new URL(request.url);
|
|
1068
1113
|
const nonce = crypto.randomUUID().replace(/-/g, "");
|
|
@@ -1118,6 +1163,16 @@ function createRequestHandler(routeEntry, handler, deps) {
|
|
|
1118
1163
|
headers: { "Content-Type": "application/json", "Cache-Control": "no-store" }
|
|
1119
1164
|
});
|
|
1120
1165
|
if (encoded) response.headers.set("PAYMENT-REQUIRED", encoded);
|
|
1166
|
+
if (deps.mppx) {
|
|
1167
|
+
try {
|
|
1168
|
+
const mppChallenge = await deps.mppx.charge({ amount: "0" })(request);
|
|
1169
|
+
if (mppChallenge.status === 402) {
|
|
1170
|
+
const wwwAuth = mppChallenge.challenge.headers.get("WWW-Authenticate");
|
|
1171
|
+
if (wwwAuth) response.headers.set("WWW-Authenticate", wwwAuth);
|
|
1172
|
+
}
|
|
1173
|
+
} catch {
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1121
1176
|
firePluginResponse(deps, pluginCtx, meta, response);
|
|
1122
1177
|
return response;
|
|
1123
1178
|
}
|
|
@@ -1311,6 +1366,122 @@ function createRequestHandler(routeEntry, handler, deps) {
|
|
|
1311
1366
|
console.error(`[router] ${routeEntry.key}: ${reason}`);
|
|
1312
1367
|
return fail(500, reason, meta, pluginCtx, body.data);
|
|
1313
1368
|
}
|
|
1369
|
+
const mppCredential = import_mppx2.Credential.fromRequest(request);
|
|
1370
|
+
const rawSource = mppCredential?.source ?? "";
|
|
1371
|
+
const didParts = rawSource.split(":");
|
|
1372
|
+
const lastPart = didParts[didParts.length - 1];
|
|
1373
|
+
const wallet = normalizeWalletAddress((0, import_viem2.isAddress)(lastPart) ? (0, import_viem2.getAddress)(lastPart) : rawSource);
|
|
1374
|
+
const payloadType = mppCredential?.payload?.type;
|
|
1375
|
+
if (payloadType === "transaction" && deps.tempoClient) {
|
|
1376
|
+
try {
|
|
1377
|
+
const serializedTx = mppCredential.payload.signature;
|
|
1378
|
+
const transaction = import_tempo.Transaction.deserialize(serializedTx);
|
|
1379
|
+
await (0, import_actions.call)(deps.tempoClient, {
|
|
1380
|
+
...transaction,
|
|
1381
|
+
account: transaction.from,
|
|
1382
|
+
calls: transaction.calls ?? []
|
|
1383
|
+
});
|
|
1384
|
+
} catch (err) {
|
|
1385
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1386
|
+
console.warn(`[router] ${routeEntry.key}: MPP simulation failed \u2014 ${message}`);
|
|
1387
|
+
firePluginHook(deps.plugin, "onAlert", pluginCtx, {
|
|
1388
|
+
level: "warn",
|
|
1389
|
+
message: `MPP simulation failed: ${message}`,
|
|
1390
|
+
route: routeEntry.key
|
|
1391
|
+
});
|
|
1392
|
+
return await build402(request, routeEntry, deps, meta, pluginCtx, body.data);
|
|
1393
|
+
}
|
|
1394
|
+
pluginCtx.setVerifiedWallet(wallet);
|
|
1395
|
+
firePluginHook(deps.plugin, "onPaymentVerified", pluginCtx, {
|
|
1396
|
+
protocol: "mpp",
|
|
1397
|
+
payer: wallet,
|
|
1398
|
+
amount: price,
|
|
1399
|
+
network: "tempo:4217"
|
|
1400
|
+
});
|
|
1401
|
+
const { response: response2, rawResult: rawResult2 } = await invoke(
|
|
1402
|
+
request,
|
|
1403
|
+
meta,
|
|
1404
|
+
pluginCtx,
|
|
1405
|
+
wallet,
|
|
1406
|
+
account,
|
|
1407
|
+
body.data
|
|
1408
|
+
);
|
|
1409
|
+
if (response2.status < 400) {
|
|
1410
|
+
let mppResult2;
|
|
1411
|
+
try {
|
|
1412
|
+
mppResult2 = await deps.mppx.charge({ amount: price })(request);
|
|
1413
|
+
} catch (err) {
|
|
1414
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1415
|
+
console.error(
|
|
1416
|
+
`[router] ${routeEntry.key}: MPP broadcast failed after handler: ${message}`
|
|
1417
|
+
);
|
|
1418
|
+
firePluginHook(deps.plugin, "onAlert", pluginCtx, {
|
|
1419
|
+
level: "critical",
|
|
1420
|
+
message: `MPP broadcast failed after handler: ${message}`,
|
|
1421
|
+
route: routeEntry.key
|
|
1422
|
+
});
|
|
1423
|
+
return fail(
|
|
1424
|
+
500,
|
|
1425
|
+
`MPP payment processing failed: ${message}`,
|
|
1426
|
+
meta,
|
|
1427
|
+
pluginCtx,
|
|
1428
|
+
body.data
|
|
1429
|
+
);
|
|
1430
|
+
}
|
|
1431
|
+
if (mppResult2.status === 402) {
|
|
1432
|
+
let rejectReason = "";
|
|
1433
|
+
try {
|
|
1434
|
+
const problemBody = await mppResult2.challenge.clone().text();
|
|
1435
|
+
if (problemBody) {
|
|
1436
|
+
const problem = JSON.parse(problemBody);
|
|
1437
|
+
rejectReason = problem.detail || problem.title || "";
|
|
1438
|
+
}
|
|
1439
|
+
} catch {
|
|
1440
|
+
}
|
|
1441
|
+
const detail = rejectReason || "transaction reverted on-chain after handler execution";
|
|
1442
|
+
console.error(
|
|
1443
|
+
`[router] ${routeEntry.key}: MPP payment failed after handler \u2014 ${detail}`
|
|
1444
|
+
);
|
|
1445
|
+
firePluginHook(deps.plugin, "onAlert", pluginCtx, {
|
|
1446
|
+
level: "critical",
|
|
1447
|
+
message: `MPP payment failed after handler: ${detail}`,
|
|
1448
|
+
route: routeEntry.key
|
|
1449
|
+
});
|
|
1450
|
+
return fail(500, `MPP payment failed: ${detail}`, meta, pluginCtx, body.data);
|
|
1451
|
+
}
|
|
1452
|
+
const receiptResponse = mppResult2.withReceipt(response2);
|
|
1453
|
+
receiptResponse.headers.set("Cache-Control", "private");
|
|
1454
|
+
let txHash2 = "";
|
|
1455
|
+
const receiptHeader2 = receiptResponse.headers.get("Payment-Receipt");
|
|
1456
|
+
if (receiptHeader2) {
|
|
1457
|
+
try {
|
|
1458
|
+
txHash2 = import_mppx2.Receipt.deserialize(receiptHeader2).reference;
|
|
1459
|
+
} catch {
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
if (routeEntry.siwxEnabled) {
|
|
1463
|
+
try {
|
|
1464
|
+
await deps.entitlementStore.grant(routeEntry.key, wallet);
|
|
1465
|
+
} catch (error) {
|
|
1466
|
+
firePluginHook(deps.plugin, "onAlert", pluginCtx, {
|
|
1467
|
+
level: "warn",
|
|
1468
|
+
message: `Entitlement grant failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
1469
|
+
route: routeEntry.key
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
firePluginHook(deps.plugin, "onPaymentSettled", pluginCtx, {
|
|
1474
|
+
protocol: "mpp",
|
|
1475
|
+
payer: wallet,
|
|
1476
|
+
transaction: txHash2,
|
|
1477
|
+
network: "tempo:4217"
|
|
1478
|
+
});
|
|
1479
|
+
finalize(receiptResponse, rawResult2, meta, pluginCtx, body.data);
|
|
1480
|
+
return receiptResponse;
|
|
1481
|
+
}
|
|
1482
|
+
finalize(response2, rawResult2, meta, pluginCtx, body.data);
|
|
1483
|
+
return response2;
|
|
1484
|
+
}
|
|
1314
1485
|
let mppResult;
|
|
1315
1486
|
try {
|
|
1316
1487
|
mppResult = await deps.mppx.charge({ amount: price })(request);
|
|
@@ -1343,11 +1514,16 @@ function createRequestHandler(routeEntry, handler, deps) {
|
|
|
1343
1514
|
});
|
|
1344
1515
|
return await build402(request, routeEntry, deps, meta, pluginCtx, body.data);
|
|
1345
1516
|
}
|
|
1346
|
-
|
|
1347
|
-
const
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1517
|
+
let txHash = "";
|
|
1518
|
+
const receiptHeader = mppResult.withReceipt(new Response()).headers.get(
|
|
1519
|
+
"Payment-Receipt"
|
|
1520
|
+
);
|
|
1521
|
+
if (receiptHeader) {
|
|
1522
|
+
try {
|
|
1523
|
+
txHash = import_mppx2.Receipt.deserialize(receiptHeader).reference;
|
|
1524
|
+
} catch {
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1351
1527
|
pluginCtx.setVerifiedWallet(wallet);
|
|
1352
1528
|
firePluginHook(deps.plugin, "onPaymentVerified", pluginCtx, {
|
|
1353
1529
|
protocol: "mpp",
|
|
@@ -1377,6 +1553,12 @@ function createRequestHandler(routeEntry, handler, deps) {
|
|
|
1377
1553
|
}
|
|
1378
1554
|
const receiptResponse = mppResult.withReceipt(response);
|
|
1379
1555
|
receiptResponse.headers.set("Cache-Control", "private");
|
|
1556
|
+
firePluginHook(deps.plugin, "onPaymentSettled", pluginCtx, {
|
|
1557
|
+
protocol: "mpp",
|
|
1558
|
+
payer: wallet,
|
|
1559
|
+
transaction: txHash,
|
|
1560
|
+
network: "tempo:4217"
|
|
1561
|
+
});
|
|
1380
1562
|
finalize(receiptResponse, rawResult, meta, pluginCtx, body.data);
|
|
1381
1563
|
return receiptResponse;
|
|
1382
1564
|
}
|
|
@@ -1426,7 +1608,7 @@ async function resolveDynamicPrice(bodyData, routeEntry, deps, pluginCtx, meta)
|
|
|
1426
1608
|
if (routeEntry.maxPrice) {
|
|
1427
1609
|
const calculated = parseFloat(price);
|
|
1428
1610
|
const max = parseFloat(routeEntry.maxPrice);
|
|
1429
|
-
if (calculated > max) {
|
|
1611
|
+
if (!Number.isFinite(calculated) || calculated > max) {
|
|
1430
1612
|
firePluginHook(deps.plugin, "onAlert", pluginCtx, {
|
|
1431
1613
|
level: "warn",
|
|
1432
1614
|
message: `Price ${price} exceeds maxPrice ${routeEntry.maxPrice}, capping`,
|
|
@@ -2238,7 +2420,8 @@ function createRouter(config) {
|
|
|
2238
2420
|
network,
|
|
2239
2421
|
x402FacilitatorsByNetwork: void 0,
|
|
2240
2422
|
x402Accepts,
|
|
2241
|
-
mppx: null
|
|
2423
|
+
mppx: null,
|
|
2424
|
+
tempoClient: null
|
|
2242
2425
|
};
|
|
2243
2426
|
deps.initPromise = (async () => {
|
|
2244
2427
|
if (x402ConfigError) {
|
|
@@ -2261,11 +2444,10 @@ function createRouter(config) {
|
|
|
2261
2444
|
try {
|
|
2262
2445
|
const { Mppx, tempo } = await import("mppx/server");
|
|
2263
2446
|
const rpcUrl = config.mpp.rpcUrl ?? process.env.TEMPO_RPC_URL;
|
|
2264
|
-
const
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
};
|
|
2447
|
+
const { createClient, http } = await import("viem");
|
|
2448
|
+
const { tempo: tempoChain } = await import("viem/chains");
|
|
2449
|
+
deps.tempoClient = createClient({ chain: tempoChain, transport: http(rpcUrl) });
|
|
2450
|
+
const getClient = async () => deps.tempoClient;
|
|
2269
2451
|
let feePayerAccount;
|
|
2270
2452
|
if (config.mpp.feePayerKey) {
|
|
2271
2453
|
const { Account } = await import("viem/tempo");
|
package/dist/index.d.cts
CHANGED
|
@@ -2,6 +2,7 @@ import { FacilitatorConfig } from '@x402/core/http';
|
|
|
2
2
|
import { NextRequest, NextResponse } from 'next/server';
|
|
3
3
|
import { ZodType } from 'zod';
|
|
4
4
|
import { PaymentRequirements, PaymentRequired, SettleResponse, Network } from '@x402/core/types';
|
|
5
|
+
import * as viem from 'viem';
|
|
5
6
|
export { S as SIWX_ERROR_MESSAGES, a as SiwxErrorCode } from './siwx-BMlja_nt.cjs';
|
|
6
7
|
|
|
7
8
|
interface EntitlementStore {
|
|
@@ -423,6 +424,7 @@ interface OrchestrateDeps {
|
|
|
423
424
|
withReceipt: (response: Response) => Response;
|
|
424
425
|
}>;
|
|
425
426
|
} | null;
|
|
427
|
+
tempoClient?: viem.Client | null;
|
|
426
428
|
}
|
|
427
429
|
|
|
428
430
|
type True = true;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { FacilitatorConfig } from '@x402/core/http';
|
|
|
2
2
|
import { NextRequest, NextResponse } from 'next/server';
|
|
3
3
|
import { ZodType } from 'zod';
|
|
4
4
|
import { PaymentRequirements, PaymentRequired, SettleResponse, Network } from '@x402/core/types';
|
|
5
|
+
import * as viem from 'viem';
|
|
5
6
|
export { S as SIWX_ERROR_MESSAGES, a as SiwxErrorCode } from './siwx-BMlja_nt.js';
|
|
6
7
|
|
|
7
8
|
interface EntitlementStore {
|
|
@@ -423,6 +424,7 @@ interface OrchestrateDeps {
|
|
|
423
424
|
withReceipt: (response: Response) => Response;
|
|
424
425
|
}>;
|
|
425
426
|
} | null;
|
|
427
|
+
tempoClient?: viem.Client | null;
|
|
426
428
|
}
|
|
427
429
|
|
|
428
430
|
type True = true;
|
package/dist/index.js
CHANGED
|
@@ -586,8 +586,10 @@ function resolveMaxPrice(pricing) {
|
|
|
586
586
|
}
|
|
587
587
|
|
|
588
588
|
// src/orchestrate.ts
|
|
589
|
-
import { Credential } from "mppx";
|
|
590
|
-
import {
|
|
589
|
+
import { Credential as Credential2, Receipt } from "mppx";
|
|
590
|
+
import { call as viemCall } from "viem/actions";
|
|
591
|
+
import { Transaction as TempoTransaction } from "viem/tempo";
|
|
592
|
+
import { isAddress as isAddress2, getAddress as getAddress2 } from "viem";
|
|
591
593
|
|
|
592
594
|
// src/protocols/x402.ts
|
|
593
595
|
init_x402_facilitators();
|
|
@@ -859,6 +861,22 @@ async function buildSIWXExtension() {
|
|
|
859
861
|
return declareSIWxExtension();
|
|
860
862
|
}
|
|
861
863
|
|
|
864
|
+
// src/auth/mpp-siwx.ts
|
|
865
|
+
import { Credential } from "mppx";
|
|
866
|
+
import { isAddress, getAddress } from "viem";
|
|
867
|
+
async function verifyMppSiwx(request, mppx) {
|
|
868
|
+
const result = await mppx.charge({ amount: "0" })(request);
|
|
869
|
+
if (result.status === 402) {
|
|
870
|
+
return { valid: false, challenge: result.challenge };
|
|
871
|
+
}
|
|
872
|
+
const credential = Credential.fromRequest(request);
|
|
873
|
+
const rawSource = credential?.source ?? "";
|
|
874
|
+
const didParts = rawSource.split(":");
|
|
875
|
+
const lastPart = didParts[didParts.length - 1];
|
|
876
|
+
const wallet = normalizeWalletAddress(isAddress(lastPart) ? getAddress(lastPart) : rawSource);
|
|
877
|
+
return { valid: true, wallet, withReceipt: result.withReceipt };
|
|
878
|
+
}
|
|
879
|
+
|
|
862
880
|
// src/auth/api-key.ts
|
|
863
881
|
async function verifyApiKey(request, resolver) {
|
|
864
882
|
const apiKey = request.headers.get("X-API-Key") ?? extractBearerToken(request.headers.get("Authorization"));
|
|
@@ -1024,6 +1042,33 @@ function createRequestHandler(routeEntry, handler, deps) {
|
|
|
1024
1042
|
}
|
|
1025
1043
|
}
|
|
1026
1044
|
const siwxHeader = request.headers.get("SIGN-IN-WITH-X");
|
|
1045
|
+
if (!siwxHeader && protocol === "mpp" && routeEntry.authMode === "siwx" && deps.mppx) {
|
|
1046
|
+
let mppSiwxResult;
|
|
1047
|
+
try {
|
|
1048
|
+
mppSiwxResult = await verifyMppSiwx(request, deps.mppx);
|
|
1049
|
+
} catch (err) {
|
|
1050
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1051
|
+
firePluginHook(deps.plugin, "onAlert", pluginCtx, {
|
|
1052
|
+
level: "critical",
|
|
1053
|
+
message: `MPP SIWX verification failed: ${message}`,
|
|
1054
|
+
route: routeEntry.key
|
|
1055
|
+
});
|
|
1056
|
+
return fail(500, `MPP SIWX verification failed: ${message}`, meta, pluginCtx);
|
|
1057
|
+
}
|
|
1058
|
+
if (mppSiwxResult.valid) {
|
|
1059
|
+
pluginCtx.setVerifiedWallet(mppSiwxResult.wallet);
|
|
1060
|
+
firePluginHook(deps.plugin, "onAuthVerified", pluginCtx, {
|
|
1061
|
+
authMode: "siwx",
|
|
1062
|
+
wallet: mppSiwxResult.wallet,
|
|
1063
|
+
route: routeEntry.key
|
|
1064
|
+
});
|
|
1065
|
+
const authResponse = await handleAuth(mppSiwxResult.wallet, void 0);
|
|
1066
|
+
if (authResponse.status < 400) {
|
|
1067
|
+
return mppSiwxResult.withReceipt(authResponse);
|
|
1068
|
+
}
|
|
1069
|
+
return authResponse;
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1027
1072
|
if (!siwxHeader && routeEntry.authMode === "siwx") {
|
|
1028
1073
|
const url = new URL(request.url);
|
|
1029
1074
|
const nonce = crypto.randomUUID().replace(/-/g, "");
|
|
@@ -1079,6 +1124,16 @@ function createRequestHandler(routeEntry, handler, deps) {
|
|
|
1079
1124
|
headers: { "Content-Type": "application/json", "Cache-Control": "no-store" }
|
|
1080
1125
|
});
|
|
1081
1126
|
if (encoded) response.headers.set("PAYMENT-REQUIRED", encoded);
|
|
1127
|
+
if (deps.mppx) {
|
|
1128
|
+
try {
|
|
1129
|
+
const mppChallenge = await deps.mppx.charge({ amount: "0" })(request);
|
|
1130
|
+
if (mppChallenge.status === 402) {
|
|
1131
|
+
const wwwAuth = mppChallenge.challenge.headers.get("WWW-Authenticate");
|
|
1132
|
+
if (wwwAuth) response.headers.set("WWW-Authenticate", wwwAuth);
|
|
1133
|
+
}
|
|
1134
|
+
} catch {
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1082
1137
|
firePluginResponse(deps, pluginCtx, meta, response);
|
|
1083
1138
|
return response;
|
|
1084
1139
|
}
|
|
@@ -1272,6 +1327,122 @@ function createRequestHandler(routeEntry, handler, deps) {
|
|
|
1272
1327
|
console.error(`[router] ${routeEntry.key}: ${reason}`);
|
|
1273
1328
|
return fail(500, reason, meta, pluginCtx, body.data);
|
|
1274
1329
|
}
|
|
1330
|
+
const mppCredential = Credential2.fromRequest(request);
|
|
1331
|
+
const rawSource = mppCredential?.source ?? "";
|
|
1332
|
+
const didParts = rawSource.split(":");
|
|
1333
|
+
const lastPart = didParts[didParts.length - 1];
|
|
1334
|
+
const wallet = normalizeWalletAddress(isAddress2(lastPart) ? getAddress2(lastPart) : rawSource);
|
|
1335
|
+
const payloadType = mppCredential?.payload?.type;
|
|
1336
|
+
if (payloadType === "transaction" && deps.tempoClient) {
|
|
1337
|
+
try {
|
|
1338
|
+
const serializedTx = mppCredential.payload.signature;
|
|
1339
|
+
const transaction = TempoTransaction.deserialize(serializedTx);
|
|
1340
|
+
await viemCall(deps.tempoClient, {
|
|
1341
|
+
...transaction,
|
|
1342
|
+
account: transaction.from,
|
|
1343
|
+
calls: transaction.calls ?? []
|
|
1344
|
+
});
|
|
1345
|
+
} catch (err) {
|
|
1346
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1347
|
+
console.warn(`[router] ${routeEntry.key}: MPP simulation failed \u2014 ${message}`);
|
|
1348
|
+
firePluginHook(deps.plugin, "onAlert", pluginCtx, {
|
|
1349
|
+
level: "warn",
|
|
1350
|
+
message: `MPP simulation failed: ${message}`,
|
|
1351
|
+
route: routeEntry.key
|
|
1352
|
+
});
|
|
1353
|
+
return await build402(request, routeEntry, deps, meta, pluginCtx, body.data);
|
|
1354
|
+
}
|
|
1355
|
+
pluginCtx.setVerifiedWallet(wallet);
|
|
1356
|
+
firePluginHook(deps.plugin, "onPaymentVerified", pluginCtx, {
|
|
1357
|
+
protocol: "mpp",
|
|
1358
|
+
payer: wallet,
|
|
1359
|
+
amount: price,
|
|
1360
|
+
network: "tempo:4217"
|
|
1361
|
+
});
|
|
1362
|
+
const { response: response2, rawResult: rawResult2 } = await invoke(
|
|
1363
|
+
request,
|
|
1364
|
+
meta,
|
|
1365
|
+
pluginCtx,
|
|
1366
|
+
wallet,
|
|
1367
|
+
account,
|
|
1368
|
+
body.data
|
|
1369
|
+
);
|
|
1370
|
+
if (response2.status < 400) {
|
|
1371
|
+
let mppResult2;
|
|
1372
|
+
try {
|
|
1373
|
+
mppResult2 = await deps.mppx.charge({ amount: price })(request);
|
|
1374
|
+
} catch (err) {
|
|
1375
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1376
|
+
console.error(
|
|
1377
|
+
`[router] ${routeEntry.key}: MPP broadcast failed after handler: ${message}`
|
|
1378
|
+
);
|
|
1379
|
+
firePluginHook(deps.plugin, "onAlert", pluginCtx, {
|
|
1380
|
+
level: "critical",
|
|
1381
|
+
message: `MPP broadcast failed after handler: ${message}`,
|
|
1382
|
+
route: routeEntry.key
|
|
1383
|
+
});
|
|
1384
|
+
return fail(
|
|
1385
|
+
500,
|
|
1386
|
+
`MPP payment processing failed: ${message}`,
|
|
1387
|
+
meta,
|
|
1388
|
+
pluginCtx,
|
|
1389
|
+
body.data
|
|
1390
|
+
);
|
|
1391
|
+
}
|
|
1392
|
+
if (mppResult2.status === 402) {
|
|
1393
|
+
let rejectReason = "";
|
|
1394
|
+
try {
|
|
1395
|
+
const problemBody = await mppResult2.challenge.clone().text();
|
|
1396
|
+
if (problemBody) {
|
|
1397
|
+
const problem = JSON.parse(problemBody);
|
|
1398
|
+
rejectReason = problem.detail || problem.title || "";
|
|
1399
|
+
}
|
|
1400
|
+
} catch {
|
|
1401
|
+
}
|
|
1402
|
+
const detail = rejectReason || "transaction reverted on-chain after handler execution";
|
|
1403
|
+
console.error(
|
|
1404
|
+
`[router] ${routeEntry.key}: MPP payment failed after handler \u2014 ${detail}`
|
|
1405
|
+
);
|
|
1406
|
+
firePluginHook(deps.plugin, "onAlert", pluginCtx, {
|
|
1407
|
+
level: "critical",
|
|
1408
|
+
message: `MPP payment failed after handler: ${detail}`,
|
|
1409
|
+
route: routeEntry.key
|
|
1410
|
+
});
|
|
1411
|
+
return fail(500, `MPP payment failed: ${detail}`, meta, pluginCtx, body.data);
|
|
1412
|
+
}
|
|
1413
|
+
const receiptResponse = mppResult2.withReceipt(response2);
|
|
1414
|
+
receiptResponse.headers.set("Cache-Control", "private");
|
|
1415
|
+
let txHash2 = "";
|
|
1416
|
+
const receiptHeader2 = receiptResponse.headers.get("Payment-Receipt");
|
|
1417
|
+
if (receiptHeader2) {
|
|
1418
|
+
try {
|
|
1419
|
+
txHash2 = Receipt.deserialize(receiptHeader2).reference;
|
|
1420
|
+
} catch {
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
if (routeEntry.siwxEnabled) {
|
|
1424
|
+
try {
|
|
1425
|
+
await deps.entitlementStore.grant(routeEntry.key, wallet);
|
|
1426
|
+
} catch (error) {
|
|
1427
|
+
firePluginHook(deps.plugin, "onAlert", pluginCtx, {
|
|
1428
|
+
level: "warn",
|
|
1429
|
+
message: `Entitlement grant failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
1430
|
+
route: routeEntry.key
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
firePluginHook(deps.plugin, "onPaymentSettled", pluginCtx, {
|
|
1435
|
+
protocol: "mpp",
|
|
1436
|
+
payer: wallet,
|
|
1437
|
+
transaction: txHash2,
|
|
1438
|
+
network: "tempo:4217"
|
|
1439
|
+
});
|
|
1440
|
+
finalize(receiptResponse, rawResult2, meta, pluginCtx, body.data);
|
|
1441
|
+
return receiptResponse;
|
|
1442
|
+
}
|
|
1443
|
+
finalize(response2, rawResult2, meta, pluginCtx, body.data);
|
|
1444
|
+
return response2;
|
|
1445
|
+
}
|
|
1275
1446
|
let mppResult;
|
|
1276
1447
|
try {
|
|
1277
1448
|
mppResult = await deps.mppx.charge({ amount: price })(request);
|
|
@@ -1304,11 +1475,16 @@ function createRequestHandler(routeEntry, handler, deps) {
|
|
|
1304
1475
|
});
|
|
1305
1476
|
return await build402(request, routeEntry, deps, meta, pluginCtx, body.data);
|
|
1306
1477
|
}
|
|
1307
|
-
|
|
1308
|
-
const
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1478
|
+
let txHash = "";
|
|
1479
|
+
const receiptHeader = mppResult.withReceipt(new Response()).headers.get(
|
|
1480
|
+
"Payment-Receipt"
|
|
1481
|
+
);
|
|
1482
|
+
if (receiptHeader) {
|
|
1483
|
+
try {
|
|
1484
|
+
txHash = Receipt.deserialize(receiptHeader).reference;
|
|
1485
|
+
} catch {
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1312
1488
|
pluginCtx.setVerifiedWallet(wallet);
|
|
1313
1489
|
firePluginHook(deps.plugin, "onPaymentVerified", pluginCtx, {
|
|
1314
1490
|
protocol: "mpp",
|
|
@@ -1338,6 +1514,12 @@ function createRequestHandler(routeEntry, handler, deps) {
|
|
|
1338
1514
|
}
|
|
1339
1515
|
const receiptResponse = mppResult.withReceipt(response);
|
|
1340
1516
|
receiptResponse.headers.set("Cache-Control", "private");
|
|
1517
|
+
firePluginHook(deps.plugin, "onPaymentSettled", pluginCtx, {
|
|
1518
|
+
protocol: "mpp",
|
|
1519
|
+
payer: wallet,
|
|
1520
|
+
transaction: txHash,
|
|
1521
|
+
network: "tempo:4217"
|
|
1522
|
+
});
|
|
1341
1523
|
finalize(receiptResponse, rawResult, meta, pluginCtx, body.data);
|
|
1342
1524
|
return receiptResponse;
|
|
1343
1525
|
}
|
|
@@ -1387,7 +1569,7 @@ async function resolveDynamicPrice(bodyData, routeEntry, deps, pluginCtx, meta)
|
|
|
1387
1569
|
if (routeEntry.maxPrice) {
|
|
1388
1570
|
const calculated = parseFloat(price);
|
|
1389
1571
|
const max = parseFloat(routeEntry.maxPrice);
|
|
1390
|
-
if (calculated > max) {
|
|
1572
|
+
if (!Number.isFinite(calculated) || calculated > max) {
|
|
1391
1573
|
firePluginHook(deps.plugin, "onAlert", pluginCtx, {
|
|
1392
1574
|
level: "warn",
|
|
1393
1575
|
message: `Price ${price} exceeds maxPrice ${routeEntry.maxPrice}, capping`,
|
|
@@ -2199,7 +2381,8 @@ function createRouter(config) {
|
|
|
2199
2381
|
network,
|
|
2200
2382
|
x402FacilitatorsByNetwork: void 0,
|
|
2201
2383
|
x402Accepts,
|
|
2202
|
-
mppx: null
|
|
2384
|
+
mppx: null,
|
|
2385
|
+
tempoClient: null
|
|
2203
2386
|
};
|
|
2204
2387
|
deps.initPromise = (async () => {
|
|
2205
2388
|
if (x402ConfigError) {
|
|
@@ -2222,11 +2405,10 @@ function createRouter(config) {
|
|
|
2222
2405
|
try {
|
|
2223
2406
|
const { Mppx, tempo } = await import("mppx/server");
|
|
2224
2407
|
const rpcUrl = config.mpp.rpcUrl ?? process.env.TEMPO_RPC_URL;
|
|
2225
|
-
const
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
};
|
|
2408
|
+
const { createClient, http } = await import("viem");
|
|
2409
|
+
const { tempo: tempoChain } = await import("viem/chains");
|
|
2410
|
+
deps.tempoClient = createClient({ chain: tempoChain, transport: http(rpcUrl) });
|
|
2411
|
+
const getClient = async () => deps.tempoClient;
|
|
2230
2412
|
let feePayerAccount;
|
|
2231
2413
|
if (config.mpp.feePayerKey) {
|
|
2232
2414
|
const { Account } = await import("viem/tempo");
|