@agentcash/router 1.2.0 → 1.2.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/dist/index.cjs +179 -27
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +180 -28
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -626,6 +626,8 @@ function resolveMaxPrice(pricing) {
|
|
|
626
626
|
|
|
627
627
|
// src/orchestrate.ts
|
|
628
628
|
var import_mppx2 = require("mppx");
|
|
629
|
+
var import_actions = require("viem/actions");
|
|
630
|
+
var import_tempo = require("viem/tempo");
|
|
629
631
|
var import_viem2 = require("viem");
|
|
630
632
|
|
|
631
633
|
// src/protocols/x402.ts
|
|
@@ -739,30 +741,53 @@ async function buildChallengeRequirements(server, request, price, accepts, resou
|
|
|
739
741
|
function needsFacilitatorEnrichment(accepts) {
|
|
740
742
|
return accepts.some((accept) => accept.scheme !== "exact") || hasSolanaAccepts(accepts);
|
|
741
743
|
}
|
|
744
|
+
async function enrichGroup(group, resource) {
|
|
745
|
+
const accepted = await enrichRequirementsWithFacilitatorAccepts(
|
|
746
|
+
group.facilitator,
|
|
747
|
+
resource,
|
|
748
|
+
group.items.map(({ requirement }) => requirement)
|
|
749
|
+
);
|
|
750
|
+
if (accepted.length !== group.items.length) {
|
|
751
|
+
throw new Error(
|
|
752
|
+
`Facilitator /accepts returned ${accepted.length} requirements for ${group.items.length} inputs on ${group.facilitator.url ?? group.facilitator.network}`
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
return accepted;
|
|
756
|
+
}
|
|
742
757
|
async function enrichChallengeRequirements(requirements, resource, facilitatorsByNetwork) {
|
|
743
758
|
const groups = collectEnrichmentGroups(requirements, facilitatorsByNetwork);
|
|
744
759
|
if (groups.length === 0) return requirements;
|
|
745
|
-
const
|
|
746
|
-
await Promise.all(
|
|
760
|
+
const results = await Promise.all(
|
|
747
761
|
groups.map(async (group) => {
|
|
748
|
-
|
|
749
|
-
group
|
|
750
|
-
|
|
751
|
-
group.
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
`Facilitator /accepts returned ${enriched.length} requirements for ${group.items.length} inputs on ${group.facilitator.url ?? group.facilitator.network}`
|
|
762
|
+
try {
|
|
763
|
+
return { success: true, group, accepted: await enrichGroup(group, resource) };
|
|
764
|
+
} catch (err) {
|
|
765
|
+
const label = group.facilitator.url ?? group.facilitator.network;
|
|
766
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
767
|
+
console.warn(
|
|
768
|
+
`[router] ${label} /accepts failed, dropping ${group.items.length} requirement(s): ${reason}`
|
|
756
769
|
);
|
|
770
|
+
return { success: false, group };
|
|
757
771
|
}
|
|
758
|
-
enriched.forEach((requirement, offset) => {
|
|
759
|
-
const index = group.items[offset]?.index;
|
|
760
|
-
if (index === void 0) return;
|
|
761
|
-
enrichedRequirements[index] = requirement;
|
|
762
|
-
});
|
|
763
772
|
})
|
|
764
773
|
);
|
|
765
|
-
|
|
774
|
+
const enriched = [...requirements];
|
|
775
|
+
results.filter((r) => r.success).forEach(({ group, accepted }) => {
|
|
776
|
+
accepted.forEach((req, offset) => {
|
|
777
|
+
const index = group.items[offset]?.index;
|
|
778
|
+
if (index !== void 0) enriched[index] = req;
|
|
779
|
+
});
|
|
780
|
+
});
|
|
781
|
+
const failedIndices = new Set(
|
|
782
|
+
results.filter((r) => !r.success).flatMap(({ group }) => group.items.map(({ index }) => index))
|
|
783
|
+
);
|
|
784
|
+
const remaining = enriched.filter((_, i) => !failedIndices.has(i));
|
|
785
|
+
if (remaining.length === 0) {
|
|
786
|
+
throw new Error(
|
|
787
|
+
"All facilitator enrichments failed; no payment requirements remain for challenge"
|
|
788
|
+
);
|
|
789
|
+
}
|
|
790
|
+
return remaining;
|
|
766
791
|
}
|
|
767
792
|
function collectEnrichmentGroups(requirements, facilitatorsByNetwork) {
|
|
768
793
|
const groups = [];
|
|
@@ -1364,6 +1389,122 @@ function createRequestHandler(routeEntry, handler, deps) {
|
|
|
1364
1389
|
console.error(`[router] ${routeEntry.key}: ${reason}`);
|
|
1365
1390
|
return fail(500, reason, meta, pluginCtx, body.data);
|
|
1366
1391
|
}
|
|
1392
|
+
const mppCredential = import_mppx2.Credential.fromRequest(request);
|
|
1393
|
+
const rawSource = mppCredential?.source ?? "";
|
|
1394
|
+
const didParts = rawSource.split(":");
|
|
1395
|
+
const lastPart = didParts[didParts.length - 1];
|
|
1396
|
+
const wallet = normalizeWalletAddress((0, import_viem2.isAddress)(lastPart) ? (0, import_viem2.getAddress)(lastPart) : rawSource);
|
|
1397
|
+
const payloadType = mppCredential?.payload?.type;
|
|
1398
|
+
if (payloadType === "transaction" && deps.tempoClient) {
|
|
1399
|
+
try {
|
|
1400
|
+
const serializedTx = mppCredential.payload.signature;
|
|
1401
|
+
const transaction = import_tempo.Transaction.deserialize(serializedTx);
|
|
1402
|
+
await (0, import_actions.call)(deps.tempoClient, {
|
|
1403
|
+
...transaction,
|
|
1404
|
+
account: transaction.from,
|
|
1405
|
+
calls: transaction.calls ?? []
|
|
1406
|
+
});
|
|
1407
|
+
} catch (err) {
|
|
1408
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1409
|
+
console.warn(`[router] ${routeEntry.key}: MPP simulation failed \u2014 ${message}`);
|
|
1410
|
+
firePluginHook(deps.plugin, "onAlert", pluginCtx, {
|
|
1411
|
+
level: "warn",
|
|
1412
|
+
message: `MPP simulation failed: ${message}`,
|
|
1413
|
+
route: routeEntry.key
|
|
1414
|
+
});
|
|
1415
|
+
return await build402(request, routeEntry, deps, meta, pluginCtx, body.data);
|
|
1416
|
+
}
|
|
1417
|
+
pluginCtx.setVerifiedWallet(wallet);
|
|
1418
|
+
firePluginHook(deps.plugin, "onPaymentVerified", pluginCtx, {
|
|
1419
|
+
protocol: "mpp",
|
|
1420
|
+
payer: wallet,
|
|
1421
|
+
amount: price,
|
|
1422
|
+
network: "tempo:4217"
|
|
1423
|
+
});
|
|
1424
|
+
const { response: response2, rawResult: rawResult2 } = await invoke(
|
|
1425
|
+
request,
|
|
1426
|
+
meta,
|
|
1427
|
+
pluginCtx,
|
|
1428
|
+
wallet,
|
|
1429
|
+
account,
|
|
1430
|
+
body.data
|
|
1431
|
+
);
|
|
1432
|
+
if (response2.status < 400) {
|
|
1433
|
+
let mppResult2;
|
|
1434
|
+
try {
|
|
1435
|
+
mppResult2 = await deps.mppx.charge({ amount: price })(request);
|
|
1436
|
+
} catch (err) {
|
|
1437
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1438
|
+
console.error(
|
|
1439
|
+
`[router] ${routeEntry.key}: MPP broadcast failed after handler: ${message}`
|
|
1440
|
+
);
|
|
1441
|
+
firePluginHook(deps.plugin, "onAlert", pluginCtx, {
|
|
1442
|
+
level: "critical",
|
|
1443
|
+
message: `MPP broadcast failed after handler: ${message}`,
|
|
1444
|
+
route: routeEntry.key
|
|
1445
|
+
});
|
|
1446
|
+
return fail(
|
|
1447
|
+
500,
|
|
1448
|
+
`MPP payment processing failed: ${message}`,
|
|
1449
|
+
meta,
|
|
1450
|
+
pluginCtx,
|
|
1451
|
+
body.data
|
|
1452
|
+
);
|
|
1453
|
+
}
|
|
1454
|
+
if (mppResult2.status === 402) {
|
|
1455
|
+
let rejectReason = "";
|
|
1456
|
+
try {
|
|
1457
|
+
const problemBody = await mppResult2.challenge.clone().text();
|
|
1458
|
+
if (problemBody) {
|
|
1459
|
+
const problem = JSON.parse(problemBody);
|
|
1460
|
+
rejectReason = problem.detail || problem.title || "";
|
|
1461
|
+
}
|
|
1462
|
+
} catch {
|
|
1463
|
+
}
|
|
1464
|
+
const detail = rejectReason || "transaction reverted on-chain after handler execution";
|
|
1465
|
+
console.error(
|
|
1466
|
+
`[router] ${routeEntry.key}: MPP payment failed after handler \u2014 ${detail}`
|
|
1467
|
+
);
|
|
1468
|
+
firePluginHook(deps.plugin, "onAlert", pluginCtx, {
|
|
1469
|
+
level: "critical",
|
|
1470
|
+
message: `MPP payment failed after handler: ${detail}`,
|
|
1471
|
+
route: routeEntry.key
|
|
1472
|
+
});
|
|
1473
|
+
return fail(500, `MPP payment failed: ${detail}`, meta, pluginCtx, body.data);
|
|
1474
|
+
}
|
|
1475
|
+
const receiptResponse = mppResult2.withReceipt(response2);
|
|
1476
|
+
receiptResponse.headers.set("Cache-Control", "private");
|
|
1477
|
+
let txHash2 = "";
|
|
1478
|
+
const receiptHeader2 = receiptResponse.headers.get("Payment-Receipt");
|
|
1479
|
+
if (receiptHeader2) {
|
|
1480
|
+
try {
|
|
1481
|
+
txHash2 = import_mppx2.Receipt.deserialize(receiptHeader2).reference;
|
|
1482
|
+
} catch {
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
if (routeEntry.siwxEnabled) {
|
|
1486
|
+
try {
|
|
1487
|
+
await deps.entitlementStore.grant(routeEntry.key, wallet);
|
|
1488
|
+
} catch (error) {
|
|
1489
|
+
firePluginHook(deps.plugin, "onAlert", pluginCtx, {
|
|
1490
|
+
level: "warn",
|
|
1491
|
+
message: `Entitlement grant failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
1492
|
+
route: routeEntry.key
|
|
1493
|
+
});
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
firePluginHook(deps.plugin, "onPaymentSettled", pluginCtx, {
|
|
1497
|
+
protocol: "mpp",
|
|
1498
|
+
payer: wallet,
|
|
1499
|
+
transaction: txHash2,
|
|
1500
|
+
network: "tempo:4217"
|
|
1501
|
+
});
|
|
1502
|
+
finalize(receiptResponse, rawResult2, meta, pluginCtx, body.data);
|
|
1503
|
+
return receiptResponse;
|
|
1504
|
+
}
|
|
1505
|
+
finalize(response2, rawResult2, meta, pluginCtx, body.data);
|
|
1506
|
+
return response2;
|
|
1507
|
+
}
|
|
1367
1508
|
let mppResult;
|
|
1368
1509
|
try {
|
|
1369
1510
|
mppResult = await deps.mppx.charge({ amount: price })(request);
|
|
@@ -1396,11 +1537,16 @@ function createRequestHandler(routeEntry, handler, deps) {
|
|
|
1396
1537
|
});
|
|
1397
1538
|
return await build402(request, routeEntry, deps, meta, pluginCtx, body.data);
|
|
1398
1539
|
}
|
|
1399
|
-
|
|
1400
|
-
const
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1540
|
+
let txHash = "";
|
|
1541
|
+
const receiptHeader = mppResult.withReceipt(new Response()).headers.get(
|
|
1542
|
+
"Payment-Receipt"
|
|
1543
|
+
);
|
|
1544
|
+
if (receiptHeader) {
|
|
1545
|
+
try {
|
|
1546
|
+
txHash = import_mppx2.Receipt.deserialize(receiptHeader).reference;
|
|
1547
|
+
} catch {
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1404
1550
|
pluginCtx.setVerifiedWallet(wallet);
|
|
1405
1551
|
firePluginHook(deps.plugin, "onPaymentVerified", pluginCtx, {
|
|
1406
1552
|
protocol: "mpp",
|
|
@@ -1430,6 +1576,12 @@ function createRequestHandler(routeEntry, handler, deps) {
|
|
|
1430
1576
|
}
|
|
1431
1577
|
const receiptResponse = mppResult.withReceipt(response);
|
|
1432
1578
|
receiptResponse.headers.set("Cache-Control", "private");
|
|
1579
|
+
firePluginHook(deps.plugin, "onPaymentSettled", pluginCtx, {
|
|
1580
|
+
protocol: "mpp",
|
|
1581
|
+
payer: wallet,
|
|
1582
|
+
transaction: txHash,
|
|
1583
|
+
network: "tempo:4217"
|
|
1584
|
+
});
|
|
1433
1585
|
finalize(receiptResponse, rawResult, meta, pluginCtx, body.data);
|
|
1434
1586
|
return receiptResponse;
|
|
1435
1587
|
}
|
|
@@ -2291,7 +2443,8 @@ function createRouter(config) {
|
|
|
2291
2443
|
network,
|
|
2292
2444
|
x402FacilitatorsByNetwork: void 0,
|
|
2293
2445
|
x402Accepts,
|
|
2294
|
-
mppx: null
|
|
2446
|
+
mppx: null,
|
|
2447
|
+
tempoClient: null
|
|
2295
2448
|
};
|
|
2296
2449
|
deps.initPromise = (async () => {
|
|
2297
2450
|
if (x402ConfigError) {
|
|
@@ -2314,11 +2467,10 @@ function createRouter(config) {
|
|
|
2314
2467
|
try {
|
|
2315
2468
|
const { Mppx, tempo } = await import("mppx/server");
|
|
2316
2469
|
const rpcUrl = config.mpp.rpcUrl ?? process.env.TEMPO_RPC_URL;
|
|
2317
|
-
const
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
};
|
|
2470
|
+
const { createClient, http } = await import("viem");
|
|
2471
|
+
const { tempo: tempoChain } = await import("viem/chains");
|
|
2472
|
+
deps.tempoClient = createClient({ chain: tempoChain, transport: http(rpcUrl) });
|
|
2473
|
+
const getClient = async () => deps.tempoClient;
|
|
2322
2474
|
let feePayerAccount;
|
|
2323
2475
|
if (config.mpp.feePayerKey) {
|
|
2324
2476
|
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,7 +586,9 @@ function resolveMaxPrice(pricing) {
|
|
|
586
586
|
}
|
|
587
587
|
|
|
588
588
|
// src/orchestrate.ts
|
|
589
|
-
import { Credential as Credential2 } from "mppx";
|
|
589
|
+
import { Credential as Credential2, Receipt } from "mppx";
|
|
590
|
+
import { call as viemCall } from "viem/actions";
|
|
591
|
+
import { Transaction as TempoTransaction } from "viem/tempo";
|
|
590
592
|
import { isAddress as isAddress2, getAddress as getAddress2 } from "viem";
|
|
591
593
|
|
|
592
594
|
// src/protocols/x402.ts
|
|
@@ -700,30 +702,53 @@ async function buildChallengeRequirements(server, request, price, accepts, resou
|
|
|
700
702
|
function needsFacilitatorEnrichment(accepts) {
|
|
701
703
|
return accepts.some((accept) => accept.scheme !== "exact") || hasSolanaAccepts(accepts);
|
|
702
704
|
}
|
|
705
|
+
async function enrichGroup(group, resource) {
|
|
706
|
+
const accepted = await enrichRequirementsWithFacilitatorAccepts(
|
|
707
|
+
group.facilitator,
|
|
708
|
+
resource,
|
|
709
|
+
group.items.map(({ requirement }) => requirement)
|
|
710
|
+
);
|
|
711
|
+
if (accepted.length !== group.items.length) {
|
|
712
|
+
throw new Error(
|
|
713
|
+
`Facilitator /accepts returned ${accepted.length} requirements for ${group.items.length} inputs on ${group.facilitator.url ?? group.facilitator.network}`
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
return accepted;
|
|
717
|
+
}
|
|
703
718
|
async function enrichChallengeRequirements(requirements, resource, facilitatorsByNetwork) {
|
|
704
719
|
const groups = collectEnrichmentGroups(requirements, facilitatorsByNetwork);
|
|
705
720
|
if (groups.length === 0) return requirements;
|
|
706
|
-
const
|
|
707
|
-
await Promise.all(
|
|
721
|
+
const results = await Promise.all(
|
|
708
722
|
groups.map(async (group) => {
|
|
709
|
-
|
|
710
|
-
group
|
|
711
|
-
|
|
712
|
-
group.
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
`Facilitator /accepts returned ${enriched.length} requirements for ${group.items.length} inputs on ${group.facilitator.url ?? group.facilitator.network}`
|
|
723
|
+
try {
|
|
724
|
+
return { success: true, group, accepted: await enrichGroup(group, resource) };
|
|
725
|
+
} catch (err) {
|
|
726
|
+
const label = group.facilitator.url ?? group.facilitator.network;
|
|
727
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
728
|
+
console.warn(
|
|
729
|
+
`[router] ${label} /accepts failed, dropping ${group.items.length} requirement(s): ${reason}`
|
|
717
730
|
);
|
|
731
|
+
return { success: false, group };
|
|
718
732
|
}
|
|
719
|
-
enriched.forEach((requirement, offset) => {
|
|
720
|
-
const index = group.items[offset]?.index;
|
|
721
|
-
if (index === void 0) return;
|
|
722
|
-
enrichedRequirements[index] = requirement;
|
|
723
|
-
});
|
|
724
733
|
})
|
|
725
734
|
);
|
|
726
|
-
|
|
735
|
+
const enriched = [...requirements];
|
|
736
|
+
results.filter((r) => r.success).forEach(({ group, accepted }) => {
|
|
737
|
+
accepted.forEach((req, offset) => {
|
|
738
|
+
const index = group.items[offset]?.index;
|
|
739
|
+
if (index !== void 0) enriched[index] = req;
|
|
740
|
+
});
|
|
741
|
+
});
|
|
742
|
+
const failedIndices = new Set(
|
|
743
|
+
results.filter((r) => !r.success).flatMap(({ group }) => group.items.map(({ index }) => index))
|
|
744
|
+
);
|
|
745
|
+
const remaining = enriched.filter((_, i) => !failedIndices.has(i));
|
|
746
|
+
if (remaining.length === 0) {
|
|
747
|
+
throw new Error(
|
|
748
|
+
"All facilitator enrichments failed; no payment requirements remain for challenge"
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
return remaining;
|
|
727
752
|
}
|
|
728
753
|
function collectEnrichmentGroups(requirements, facilitatorsByNetwork) {
|
|
729
754
|
const groups = [];
|
|
@@ -1325,6 +1350,122 @@ function createRequestHandler(routeEntry, handler, deps) {
|
|
|
1325
1350
|
console.error(`[router] ${routeEntry.key}: ${reason}`);
|
|
1326
1351
|
return fail(500, reason, meta, pluginCtx, body.data);
|
|
1327
1352
|
}
|
|
1353
|
+
const mppCredential = Credential2.fromRequest(request);
|
|
1354
|
+
const rawSource = mppCredential?.source ?? "";
|
|
1355
|
+
const didParts = rawSource.split(":");
|
|
1356
|
+
const lastPart = didParts[didParts.length - 1];
|
|
1357
|
+
const wallet = normalizeWalletAddress(isAddress2(lastPart) ? getAddress2(lastPart) : rawSource);
|
|
1358
|
+
const payloadType = mppCredential?.payload?.type;
|
|
1359
|
+
if (payloadType === "transaction" && deps.tempoClient) {
|
|
1360
|
+
try {
|
|
1361
|
+
const serializedTx = mppCredential.payload.signature;
|
|
1362
|
+
const transaction = TempoTransaction.deserialize(serializedTx);
|
|
1363
|
+
await viemCall(deps.tempoClient, {
|
|
1364
|
+
...transaction,
|
|
1365
|
+
account: transaction.from,
|
|
1366
|
+
calls: transaction.calls ?? []
|
|
1367
|
+
});
|
|
1368
|
+
} catch (err) {
|
|
1369
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1370
|
+
console.warn(`[router] ${routeEntry.key}: MPP simulation failed \u2014 ${message}`);
|
|
1371
|
+
firePluginHook(deps.plugin, "onAlert", pluginCtx, {
|
|
1372
|
+
level: "warn",
|
|
1373
|
+
message: `MPP simulation failed: ${message}`,
|
|
1374
|
+
route: routeEntry.key
|
|
1375
|
+
});
|
|
1376
|
+
return await build402(request, routeEntry, deps, meta, pluginCtx, body.data);
|
|
1377
|
+
}
|
|
1378
|
+
pluginCtx.setVerifiedWallet(wallet);
|
|
1379
|
+
firePluginHook(deps.plugin, "onPaymentVerified", pluginCtx, {
|
|
1380
|
+
protocol: "mpp",
|
|
1381
|
+
payer: wallet,
|
|
1382
|
+
amount: price,
|
|
1383
|
+
network: "tempo:4217"
|
|
1384
|
+
});
|
|
1385
|
+
const { response: response2, rawResult: rawResult2 } = await invoke(
|
|
1386
|
+
request,
|
|
1387
|
+
meta,
|
|
1388
|
+
pluginCtx,
|
|
1389
|
+
wallet,
|
|
1390
|
+
account,
|
|
1391
|
+
body.data
|
|
1392
|
+
);
|
|
1393
|
+
if (response2.status < 400) {
|
|
1394
|
+
let mppResult2;
|
|
1395
|
+
try {
|
|
1396
|
+
mppResult2 = await deps.mppx.charge({ amount: price })(request);
|
|
1397
|
+
} catch (err) {
|
|
1398
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1399
|
+
console.error(
|
|
1400
|
+
`[router] ${routeEntry.key}: MPP broadcast failed after handler: ${message}`
|
|
1401
|
+
);
|
|
1402
|
+
firePluginHook(deps.plugin, "onAlert", pluginCtx, {
|
|
1403
|
+
level: "critical",
|
|
1404
|
+
message: `MPP broadcast failed after handler: ${message}`,
|
|
1405
|
+
route: routeEntry.key
|
|
1406
|
+
});
|
|
1407
|
+
return fail(
|
|
1408
|
+
500,
|
|
1409
|
+
`MPP payment processing failed: ${message}`,
|
|
1410
|
+
meta,
|
|
1411
|
+
pluginCtx,
|
|
1412
|
+
body.data
|
|
1413
|
+
);
|
|
1414
|
+
}
|
|
1415
|
+
if (mppResult2.status === 402) {
|
|
1416
|
+
let rejectReason = "";
|
|
1417
|
+
try {
|
|
1418
|
+
const problemBody = await mppResult2.challenge.clone().text();
|
|
1419
|
+
if (problemBody) {
|
|
1420
|
+
const problem = JSON.parse(problemBody);
|
|
1421
|
+
rejectReason = problem.detail || problem.title || "";
|
|
1422
|
+
}
|
|
1423
|
+
} catch {
|
|
1424
|
+
}
|
|
1425
|
+
const detail = rejectReason || "transaction reverted on-chain after handler execution";
|
|
1426
|
+
console.error(
|
|
1427
|
+
`[router] ${routeEntry.key}: MPP payment failed after handler \u2014 ${detail}`
|
|
1428
|
+
);
|
|
1429
|
+
firePluginHook(deps.plugin, "onAlert", pluginCtx, {
|
|
1430
|
+
level: "critical",
|
|
1431
|
+
message: `MPP payment failed after handler: ${detail}`,
|
|
1432
|
+
route: routeEntry.key
|
|
1433
|
+
});
|
|
1434
|
+
return fail(500, `MPP payment failed: ${detail}`, meta, pluginCtx, body.data);
|
|
1435
|
+
}
|
|
1436
|
+
const receiptResponse = mppResult2.withReceipt(response2);
|
|
1437
|
+
receiptResponse.headers.set("Cache-Control", "private");
|
|
1438
|
+
let txHash2 = "";
|
|
1439
|
+
const receiptHeader2 = receiptResponse.headers.get("Payment-Receipt");
|
|
1440
|
+
if (receiptHeader2) {
|
|
1441
|
+
try {
|
|
1442
|
+
txHash2 = Receipt.deserialize(receiptHeader2).reference;
|
|
1443
|
+
} catch {
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
if (routeEntry.siwxEnabled) {
|
|
1447
|
+
try {
|
|
1448
|
+
await deps.entitlementStore.grant(routeEntry.key, wallet);
|
|
1449
|
+
} catch (error) {
|
|
1450
|
+
firePluginHook(deps.plugin, "onAlert", pluginCtx, {
|
|
1451
|
+
level: "warn",
|
|
1452
|
+
message: `Entitlement grant failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
1453
|
+
route: routeEntry.key
|
|
1454
|
+
});
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
firePluginHook(deps.plugin, "onPaymentSettled", pluginCtx, {
|
|
1458
|
+
protocol: "mpp",
|
|
1459
|
+
payer: wallet,
|
|
1460
|
+
transaction: txHash2,
|
|
1461
|
+
network: "tempo:4217"
|
|
1462
|
+
});
|
|
1463
|
+
finalize(receiptResponse, rawResult2, meta, pluginCtx, body.data);
|
|
1464
|
+
return receiptResponse;
|
|
1465
|
+
}
|
|
1466
|
+
finalize(response2, rawResult2, meta, pluginCtx, body.data);
|
|
1467
|
+
return response2;
|
|
1468
|
+
}
|
|
1328
1469
|
let mppResult;
|
|
1329
1470
|
try {
|
|
1330
1471
|
mppResult = await deps.mppx.charge({ amount: price })(request);
|
|
@@ -1357,11 +1498,16 @@ function createRequestHandler(routeEntry, handler, deps) {
|
|
|
1357
1498
|
});
|
|
1358
1499
|
return await build402(request, routeEntry, deps, meta, pluginCtx, body.data);
|
|
1359
1500
|
}
|
|
1360
|
-
|
|
1361
|
-
const
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1501
|
+
let txHash = "";
|
|
1502
|
+
const receiptHeader = mppResult.withReceipt(new Response()).headers.get(
|
|
1503
|
+
"Payment-Receipt"
|
|
1504
|
+
);
|
|
1505
|
+
if (receiptHeader) {
|
|
1506
|
+
try {
|
|
1507
|
+
txHash = Receipt.deserialize(receiptHeader).reference;
|
|
1508
|
+
} catch {
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1365
1511
|
pluginCtx.setVerifiedWallet(wallet);
|
|
1366
1512
|
firePluginHook(deps.plugin, "onPaymentVerified", pluginCtx, {
|
|
1367
1513
|
protocol: "mpp",
|
|
@@ -1391,6 +1537,12 @@ function createRequestHandler(routeEntry, handler, deps) {
|
|
|
1391
1537
|
}
|
|
1392
1538
|
const receiptResponse = mppResult.withReceipt(response);
|
|
1393
1539
|
receiptResponse.headers.set("Cache-Control", "private");
|
|
1540
|
+
firePluginHook(deps.plugin, "onPaymentSettled", pluginCtx, {
|
|
1541
|
+
protocol: "mpp",
|
|
1542
|
+
payer: wallet,
|
|
1543
|
+
transaction: txHash,
|
|
1544
|
+
network: "tempo:4217"
|
|
1545
|
+
});
|
|
1394
1546
|
finalize(receiptResponse, rawResult, meta, pluginCtx, body.data);
|
|
1395
1547
|
return receiptResponse;
|
|
1396
1548
|
}
|
|
@@ -2252,7 +2404,8 @@ function createRouter(config) {
|
|
|
2252
2404
|
network,
|
|
2253
2405
|
x402FacilitatorsByNetwork: void 0,
|
|
2254
2406
|
x402Accepts,
|
|
2255
|
-
mppx: null
|
|
2407
|
+
mppx: null,
|
|
2408
|
+
tempoClient: null
|
|
2256
2409
|
};
|
|
2257
2410
|
deps.initPromise = (async () => {
|
|
2258
2411
|
if (x402ConfigError) {
|
|
@@ -2275,11 +2428,10 @@ function createRouter(config) {
|
|
|
2275
2428
|
try {
|
|
2276
2429
|
const { Mppx, tempo } = await import("mppx/server");
|
|
2277
2430
|
const rpcUrl = config.mpp.rpcUrl ?? process.env.TEMPO_RPC_URL;
|
|
2278
|
-
const
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
};
|
|
2431
|
+
const { createClient, http } = await import("viem");
|
|
2432
|
+
const { tempo: tempoChain } = await import("viem/chains");
|
|
2433
|
+
deps.tempoClient = createClient({ chain: tempoChain, transport: http(rpcUrl) });
|
|
2434
|
+
const getClient = async () => deps.tempoClient;
|
|
2283
2435
|
let feePayerAccount;
|
|
2284
2436
|
if (config.mpp.feePayerKey) {
|
|
2285
2437
|
const { Account } = await import("viem/tempo");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentcash/router",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "Unified route builder for Next.js App Router APIs with x402, MPP, SIWX, and API key auth",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"@x402/extensions": "^2.3.0",
|
|
62
62
|
"@x402/svm": "2.3.0",
|
|
63
63
|
"eslint": "^10.0.0",
|
|
64
|
-
"mppx": "^0.4.
|
|
64
|
+
"mppx": "^0.4.8",
|
|
65
65
|
"next": "^15.0.0",
|
|
66
66
|
"prettier": "^3.8.1",
|
|
67
67
|
"react": "^19.0.0",
|