@elizaos/plugin-x402 2.0.0-alpha.1 → 2.0.0-alpha.6

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.
@@ -36,9 +36,30 @@ var checkPaymentHistoryAction = {
36
36
  schema: { type: "number" }
37
37
  }
38
38
  ],
39
- validate: async (runtime) => {
40
- const service = runtime.getService("x402_payment");
41
- return !!service && service.isActive();
39
+ validate: async (runtime, message, state, options) => {
40
+ const __avTextRaw = typeof message?.content?.text === "string" ? message.content.text : "";
41
+ const __avText = __avTextRaw.toLowerCase();
42
+ const __avKeywords = ["check", "payment", "history"];
43
+ const __avKeywordOk = __avKeywords.length > 0 && __avKeywords.some((kw) => kw.length > 0 && __avText.includes(kw));
44
+ const __avRegex = new RegExp("\\b(?:check|payment|history)\\b", "i");
45
+ const __avRegexOk = __avRegex.test(__avText);
46
+ const __avSource = String(message?.content?.source ?? message?.source ?? "");
47
+ const __avExpectedSource = "";
48
+ const __avSourceOk = __avExpectedSource ? __avSource === __avExpectedSource : Boolean(__avSource || state || runtime?.agentId || runtime?.getService);
49
+ const __avOptions = options && typeof options === "object" ? options : {};
50
+ const __avInputOk = __avText.trim().length > 0 || Object.keys(__avOptions).length > 0 || Boolean(message?.content && typeof message.content === "object");
51
+ if (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {
52
+ return false;
53
+ }
54
+ const __avLegacyValidate = async (runtime2) => {
55
+ const service = runtime2.getService("x402_payment");
56
+ return !!service && service.isActive();
57
+ };
58
+ try {
59
+ return Boolean(await __avLegacyValidate(runtime, message, state, options));
60
+ } catch {
61
+ return false;
62
+ }
42
63
  },
43
64
  handler: async (runtime, _message, _state, options, callback) => {
44
65
  const service = runtime.getService("x402_payment");
@@ -189,9 +210,30 @@ var setPaymentPolicyAction = {
189
210
  schema: { type: "string" }
190
211
  }
191
212
  ],
192
- validate: async (runtime) => {
193
- const service = runtime.getService("x402_payment");
194
- return !!service && service.isActive();
213
+ validate: async (runtime, message, state, options) => {
214
+ const __avTextRaw = typeof message?.content?.text === "string" ? message.content.text : "";
215
+ const __avText = __avTextRaw.toLowerCase();
216
+ const __avKeywords = ["set", "payment", "policy"];
217
+ const __avKeywordOk = __avKeywords.length > 0 && __avKeywords.some((kw) => kw.length > 0 && __avText.includes(kw));
218
+ const __avRegex = new RegExp("\\b(?:set|payment|policy)\\b", "i");
219
+ const __avRegexOk = __avRegex.test(__avText);
220
+ const __avSource = String(message?.content?.source ?? message?.source ?? "");
221
+ const __avExpectedSource = "";
222
+ const __avSourceOk = __avExpectedSource ? __avSource === __avExpectedSource : Boolean(__avSource || state || runtime?.agentId || runtime?.getService);
223
+ const __avOptions = options && typeof options === "object" ? options : {};
224
+ const __avInputOk = __avText.trim().length > 0 || Object.keys(__avOptions).length > 0 || Boolean(message?.content && typeof message.content === "object");
225
+ if (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {
226
+ return false;
227
+ }
228
+ const __avLegacyValidate = async (runtime2) => {
229
+ const service = runtime2.getService("x402_payment");
230
+ return !!service && service.isActive();
231
+ };
232
+ try {
233
+ return Boolean(await __avLegacyValidate(runtime, message, state, options));
234
+ } catch {
235
+ return false;
236
+ }
195
237
  },
196
238
  handler: async (runtime, _message, _state, options, callback) => {
197
239
  const service = runtime.getService("x402_payment");
@@ -390,9 +432,30 @@ var payForServiceAction = {
390
432
  schema: { type: "string" }
391
433
  }
392
434
  ],
393
- validate: async (runtime) => {
394
- const service = runtime.getService("x402_payment");
395
- return !!service && service.canMakePayments();
435
+ validate: async (runtime, message, state, options) => {
436
+ const __avTextRaw = typeof message?.content?.text === "string" ? message.content.text : "";
437
+ const __avText = __avTextRaw.toLowerCase();
438
+ const __avKeywords = ["pay", "for", "service"];
439
+ const __avKeywordOk = __avKeywords.length > 0 && __avKeywords.some((kw) => kw.length > 0 && __avText.includes(kw));
440
+ const __avRegex = new RegExp("\\b(?:pay|for|service)\\b", "i");
441
+ const __avRegexOk = __avRegex.test(__avText);
442
+ const __avSource = String(message?.content?.source ?? message?.source ?? "");
443
+ const __avExpectedSource = "";
444
+ const __avSourceOk = __avExpectedSource ? __avSource === __avExpectedSource : Boolean(__avSource || state || runtime?.agentId || runtime?.getService);
445
+ const __avOptions = options && typeof options === "object" ? options : {};
446
+ const __avInputOk = __avText.trim().length > 0 || Object.keys(__avOptions).length > 0 || Boolean(message?.content && typeof message.content === "object");
447
+ if (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {
448
+ return false;
449
+ }
450
+ const __avLegacyValidate = async (runtime2) => {
451
+ const service = runtime2.getService("x402_payment");
452
+ return !!service && service.canMakePayments();
453
+ };
454
+ try {
455
+ return Boolean(await __avLegacyValidate(runtime, message, state, options));
456
+ } catch {
457
+ return false;
458
+ }
396
459
  },
397
460
  handler: async (runtime, message, _state, options, callback) => {
398
461
  const service = runtime.getService("x402_payment");
@@ -537,6 +600,7 @@ function extractUrlFromMessage(message) {
537
600
  var paymentBalanceProvider = {
538
601
  name: "x402_payment_status",
539
602
  description: "Current x402 payment status including wallet, spending, and earning summary",
603
+ dynamic: true,
540
604
  get: async (runtime, _message, _state) => {
541
605
  const service = runtime.getService("x402_payment");
542
606
  if (!service || !service.isActive()) {
@@ -577,6 +641,9 @@ Payments: Inactive (no wallet configured)`,
577
641
  }
578
642
  };
579
643
 
644
+ // routes/agent-card.ts
645
+ import { logger as logger4 } from "@elizaos/core";
646
+
580
647
  // networks.ts
581
648
  var NETWORK_REGISTRY = {
582
649
  base: {
@@ -585,7 +652,8 @@ var NETWORK_REGISTRY = {
585
652
  name: "Base",
586
653
  usdcAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
587
654
  usdcDomainName: "USDC",
588
- usdcPermitVersion: "2"
655
+ usdcPermitVersion: "2",
656
+ rpcUrl: "https://mainnet.base.org"
589
657
  },
590
658
  "base-sepolia": {
591
659
  caip2: "eip155:84532",
@@ -593,7 +661,8 @@ var NETWORK_REGISTRY = {
593
661
  name: "Base Sepolia",
594
662
  usdcAddress: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
595
663
  usdcDomainName: "USDC",
596
- usdcPermitVersion: "2"
664
+ usdcPermitVersion: "2",
665
+ rpcUrl: "https://sepolia.base.org"
597
666
  },
598
667
  ethereum: {
599
668
  caip2: "eip155:1",
@@ -601,7 +670,8 @@ var NETWORK_REGISTRY = {
601
670
  name: "Ethereum",
602
671
  usdcAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
603
672
  usdcDomainName: "USD Coin",
604
- usdcPermitVersion: "2"
673
+ usdcPermitVersion: "2",
674
+ rpcUrl: "https://cloudflare-eth.com"
605
675
  },
606
676
  sepolia: {
607
677
  caip2: "eip155:11155111",
@@ -609,7 +679,8 @@ var NETWORK_REGISTRY = {
609
679
  name: "Sepolia",
610
680
  usdcAddress: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
611
681
  usdcDomainName: "USDC",
612
- usdcPermitVersion: "2"
682
+ usdcPermitVersion: "2",
683
+ rpcUrl: "https://rpc.sepolia.org"
613
684
  }
614
685
  };
615
686
  function resolveNetwork(key) {
@@ -643,8 +714,10 @@ async function handleAgentCard(req, res, runtime) {
643
714
  try {
644
715
  const networkInfo = resolveNetwork(networkKey);
645
716
  caip2Network = networkInfo.caip2;
646
- } catch {
717
+ } catch (err) {
647
718
  caip2Network = networkKey;
719
+ const msg = err instanceof Error ? err.message : String(err);
720
+ logger4.warn("[x402] Agent card: could not resolve network '" + networkKey + "': " + msg);
648
721
  }
649
722
  const character = runtime.character;
650
723
  const agentName = character?.name ?? "ElizaOS Agent";
@@ -706,9 +779,10 @@ var agentCardRoute = {
706
779
 
707
780
  // services/x402-service.ts
708
781
  import { Service } from "@elizaos/core";
709
- import { logger as logger4 } from "@elizaos/core";
782
+ import { logger as logger5 } from "@elizaos/core";
710
783
 
711
784
  // client/signer.ts
785
+ import { createPublicClient, http } from "viem";
712
786
  import { privateKeyToAccount } from "viem/accounts";
713
787
  var PERMIT_TYPES = {
714
788
  Permit: [
@@ -761,12 +835,35 @@ class EvmPaymentSigner {
761
835
  const v = parseInt(raw.slice(128, 130), 16);
762
836
  return { v, r, s };
763
837
  }
838
+ async queryOnChainNonce(usdcAddress, rpcUrl, chainId) {
839
+ const client = createPublicClient({
840
+ transport: http(rpcUrl)
841
+ });
842
+ const result = await client.readContract({
843
+ address: usdcAddress,
844
+ abi: [{
845
+ inputs: [{ name: "owner", type: "address" }],
846
+ name: "nonces",
847
+ outputs: [{ name: "", type: "uint256" }],
848
+ stateMutability: "view",
849
+ type: "function"
850
+ }],
851
+ functionName: "nonces",
852
+ args: [this.account.address]
853
+ });
854
+ return result;
855
+ }
764
856
  async buildPaymentHeader(requirement) {
765
857
  const networkInfo = resolveNetwork(this.network);
766
858
  const amount = BigInt(requirement.maxAmountRequired);
767
859
  const tokenName = requirement.extra?.name ?? networkInfo.usdcDomainName;
768
860
  const tokenVersion = requirement.extra?.version ?? networkInfo.usdcPermitVersion;
769
- const nonce = BigInt(requirement.extra?.nonce ?? "0");
861
+ let nonce;
862
+ if (requirement.extra?.nonce !== undefined) {
863
+ nonce = BigInt(requirement.extra.nonce);
864
+ } else {
865
+ nonce = await this.queryOnChainNonce(requirement.asset, networkInfo.rpcUrl, networkInfo.chainId);
866
+ }
770
867
  const deadline = BigInt(Math.floor(Date.now() / 1000) + requirement.maxTimeoutSeconds);
771
868
  const spender = requirement.payTo;
772
869
  const domain = {
@@ -840,43 +937,43 @@ function selectPaymentOption(accepts, signerNetworkId) {
840
937
  return matching ?? null;
841
938
  }
842
939
  function createFetchWithPayment(options) {
843
- const { signer, policyEngine, circuitBreaker, storage, logger: logger4 } = options;
940
+ const { signer, policyEngine, circuitBreaker, storage, logger: logger5 } = options;
844
941
  return async function fetchWithPayment(input, init) {
845
942
  const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
846
- logger4.debug(`[x402] Making request to ${url}`);
943
+ logger5.debug(`[x402] Making request to ${url}`);
847
944
  const initialResponse = await fetch(input, init);
848
945
  if (initialResponse.status !== 402) {
849
946
  return initialResponse;
850
947
  }
851
- logger4.info(`[x402] Received 402 Payment Required from ${url}`);
948
+ logger5.info(`[x402] Received 402 Payment Required from ${url}`);
852
949
  const paymentRequired = parsePaymentRequired(initialResponse);
853
950
  if (!paymentRequired) {
854
- logger4.error("[x402] Could not parse payment requirement header from 402 response");
951
+ logger5.error("[x402] Could not parse payment requirement header from 402 response");
855
952
  return initialResponse;
856
953
  }
857
954
  if (!paymentRequired.accepts || paymentRequired.accepts.length === 0) {
858
- logger4.error("[x402] No payment options in 402 response");
955
+ logger5.error("[x402] No payment options in 402 response");
859
956
  return initialResponse;
860
957
  }
861
958
  const requirement = selectPaymentOption(paymentRequired.accepts, signer.networkId);
862
959
  if (!requirement) {
863
- logger4.error("[x402] No compatible payment option found");
960
+ logger5.error("[x402] No compatible payment option found");
864
961
  return initialResponse;
865
962
  }
866
963
  const amount = BigInt(requirement.maxAmountRequired);
867
- logger4.info(`[x402] Payment required: ${amount} to ${requirement.payTo} on ${requirement.network}`);
964
+ logger5.info(`[x402] Payment required: ${amount} to ${requirement.payTo} on ${requirement.network}`);
868
965
  const policyResult = await policyEngine.evaluateOutgoing({
869
966
  amount,
870
967
  recipient: requirement.payTo,
871
968
  resource: url
872
969
  });
873
970
  if (!policyResult.allowed) {
874
- logger4.warn(`[x402] Payment blocked by policy: ${policyResult.reason}`);
971
+ logger5.warn(`[x402] Payment blocked by policy: ${policyResult.reason}`);
875
972
  return initialResponse;
876
973
  }
877
974
  const breakerResult = circuitBreaker.check(amount);
878
975
  if (!breakerResult.allowed) {
879
- logger4.warn(`[x402] Payment blocked by circuit breaker: ${breakerResult.reason}`);
976
+ logger5.warn(`[x402] Payment blocked by circuit breaker: ${breakerResult.reason}`);
880
977
  return initialResponse;
881
978
  }
882
979
  let paymentHeader;
@@ -884,11 +981,11 @@ function createFetchWithPayment(options) {
884
981
  paymentHeader = await signer.buildPaymentHeader(requirement);
885
982
  } catch (err) {
886
983
  const message = err instanceof Error ? err.message : String(err);
887
- logger4.error(`[x402] Failed to sign payment: ${message}`);
984
+ logger5.error(`[x402] Failed to sign payment: ${message}`);
888
985
  circuitBreaker.recordFailure();
889
986
  return initialResponse;
890
987
  }
891
- logger4.info("[x402] Retrying request with X-PAYMENT header");
988
+ logger5.info("[x402] Retrying request with X-PAYMENT header");
892
989
  const retryHeaders = new Headers(init?.headers);
893
990
  retryHeaders.set("X-PAYMENT", paymentHeader);
894
991
  const retryInit = {
@@ -900,7 +997,7 @@ function createFetchWithPayment(options) {
900
997
  retryResponse = await fetch(input, retryInit);
901
998
  } catch (err) {
902
999
  const message = err instanceof Error ? err.message : String(err);
903
- logger4.error(`[x402] Retry request failed: ${message}`);
1000
+ logger5.error(`[x402] Retry request failed: ${message}`);
904
1001
  circuitBreaker.recordFailure();
905
1002
  return initialResponse;
906
1003
  }
@@ -924,14 +1021,14 @@ function createFetchWithPayment(options) {
924
1021
  await storage.recordPayment(record);
925
1022
  } catch (err) {
926
1023
  const message = err instanceof Error ? err.message : String(err);
927
- logger4.error(`[x402] Failed to record payment: ${message}`);
1024
+ logger5.error(`[x402] Failed to record payment: ${message}`);
928
1025
  }
929
1026
  if (retryResponse.ok) {
930
1027
  circuitBreaker.recordSuccess(amount);
931
- logger4.info(`[x402] Payment successful: ${amount} to ${requirement.payTo}`);
1028
+ logger5.info(`[x402] Payment successful: ${amount} to ${requirement.payTo}`);
932
1029
  } else {
933
1030
  circuitBreaker.recordFailure();
934
- logger4.warn(`[x402] Payment request returned ${retryResponse.status} after payment`);
1031
+ logger5.warn(`[x402] Payment request returned ${retryResponse.status} after payment`);
935
1032
  }
936
1033
  return retryResponse;
937
1034
  };
@@ -1284,7 +1381,7 @@ class SqlitePaymentStorage {
1284
1381
  let metadata = {};
1285
1382
  try {
1286
1383
  metadata = JSON.parse(row.metadata);
1287
- } catch {}
1384
+ } catch (_metadataParseError) {}
1288
1385
  return {
1289
1386
  id: row.id,
1290
1387
  direction: row.direction,
@@ -1358,23 +1455,23 @@ class X402Service extends Service {
1358
1455
  enabled
1359
1456
  };
1360
1457
  if (!enabled) {
1361
- logger4.info("[x402] Service inactive — no private key configured or explicitly disabled");
1458
+ logger5.info("[x402] Service inactive — no private key configured or explicitly disabled");
1362
1459
  return;
1363
1460
  }
1364
1461
  try {
1365
1462
  resolveNetwork(network);
1366
1463
  } catch (err) {
1367
1464
  const message = err instanceof Error ? err.message : String(err);
1368
- logger4.error(`[x402] Invalid network configuration: ${message}`);
1465
+ logger5.error(`[x402] Invalid network configuration: ${message}`);
1369
1466
  this.serviceConfig.enabled = false;
1370
1467
  return;
1371
1468
  }
1372
1469
  try {
1373
1470
  this.signer = new EvmPaymentSigner(privateKey, network);
1374
- logger4.info(`[x402] Wallet initialized: ${this.signer.address} on ${network}`);
1471
+ logger5.info(`[x402] Wallet initialized: ${this.signer.address} on ${network}`);
1375
1472
  } catch (err) {
1376
1473
  const message = err instanceof Error ? err.message : String(err);
1377
- logger4.error(`[x402] Failed to initialize signer: ${message}`);
1474
+ logger5.error(`[x402] Failed to initialize signer: ${message}`);
1378
1475
  this.serviceConfig.enabled = false;
1379
1476
  return;
1380
1477
  }
@@ -1383,14 +1480,14 @@ class X402Service extends Service {
1383
1480
  if (dbPath) {
1384
1481
  try {
1385
1482
  this.storage = new SqlitePaymentStorage(dbPath);
1386
- logger4.info(`[x402] Using SQLite storage at ${dbPath}`);
1483
+ logger5.info(`[x402] Using SQLite storage at ${dbPath}`);
1387
1484
  } catch (err) {
1388
1485
  const message = err instanceof Error ? err.message : String(err);
1389
- logger4.warn(`[x402] Failed to initialize SQLite storage: ${message}. Falling back to memory storage.`);
1486
+ logger5.warn(`[x402] Failed to initialize SQLite storage: ${message}. Falling back to memory storage.`);
1390
1487
  this.storage = new MemoryPaymentStorage;
1391
1488
  }
1392
1489
  } else {
1393
- logger4.info("[x402] Using in-memory storage (set X402_DB_PATH for persistence)");
1490
+ logger5.info("[x402] Using in-memory storage (set X402_DB_PATH for persistence)");
1394
1491
  }
1395
1492
  const policy = {
1396
1493
  outgoing: {
@@ -1414,12 +1511,12 @@ class X402Service extends Service {
1414
1511
  policyEngine: this.policyEngine,
1415
1512
  circuitBreaker: this.circuitBreaker,
1416
1513
  storage: this.storage,
1417
- logger: logger4
1514
+ logger: logger5
1418
1515
  });
1419
- logger4.info(`[x402] Service active — max per-txn: $${this.serviceConfig.maxPaymentUsd}, max daily: $${this.serviceConfig.maxTotalUsd}`);
1516
+ logger5.info(`[x402] Service active — max per-txn: $${this.serviceConfig.maxPaymentUsd}, max daily: $${this.serviceConfig.maxTotalUsd}`);
1420
1517
  }
1421
1518
  async stop() {
1422
- logger4.info("[x402] Service stopping");
1519
+ logger5.info("[x402] Service stopping");
1423
1520
  this.signer = null;
1424
1521
  this.fetchWithPayment = null;
1425
1522
  }
@@ -1456,7 +1553,7 @@ class X402Service extends Service {
1456
1553
  updatePolicy(policy) {
1457
1554
  if (this.policyEngine) {
1458
1555
  this.policyEngine.updatePolicy(policy);
1459
- logger4.info("[x402] Payment policy updated");
1556
+ logger5.info("[x402] Payment policy updated");
1460
1557
  }
1461
1558
  }
1462
1559
  getWalletAddress() {
@@ -1479,7 +1576,7 @@ class X402Service extends Service {
1479
1576
  }
1480
1577
  resetCircuitBreaker() {
1481
1578
  this.circuitBreaker.reset();
1482
- logger4.info("[x402] Circuit breaker reset");
1579
+ logger5.info("[x402] Circuit breaker reset");
1483
1580
  }
1484
1581
  }
1485
1582
  // middleware/facilitator-client.ts
@@ -1816,7 +1913,7 @@ class PostgresPaymentStorage {
1816
1913
  let metadata = {};
1817
1914
  try {
1818
1915
  metadata = typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata;
1819
- } catch {}
1916
+ } catch (_metadataParseError) {}
1820
1917
  return {
1821
1918
  id: row.id,
1822
1919
  direction: row.direction,
@@ -1913,7 +2010,6 @@ var x402Plugin = {
1913
2010
  X402_MAX_TOTAL_USD: null,
1914
2011
  X402_ENABLED: null
1915
2012
  },
1916
- init: async (_config, _runtime) => {},
1917
2013
  services: [X402Service],
1918
2014
  actions: [payForServiceAction, checkPaymentHistoryAction, setPaymentPolicyAction],
1919
2015
  providers: [paymentBalanceProvider],
@@ -1971,4 +2067,4 @@ export {
1971
2067
  CircuitBreaker
1972
2068
  };
1973
2069
 
1974
- //# debugId=8D689045A014B14A64756E2164756E21
2070
+ //# debugId=2EC8B924AB46A7D764756E2164756E21