@scriptmasterlabs/mcp-x402 2.0.0

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.
Files changed (304) hide show
  1. package/.env.example +35 -0
  2. package/.github/workflows/ci.yml +59 -0
  3. package/.github/workflows/keepalive.yml +31 -0
  4. package/.well-known/agentcard.json +34 -0
  5. package/CONTRIBUTING.md +76 -0
  6. package/Dockerfile +19 -0
  7. package/LICENSE +21 -0
  8. package/README.md +304 -0
  9. package/agents.json +67 -0
  10. package/dist/lib/chains/base.d.ts +10 -0
  11. package/dist/lib/chains/base.d.ts.map +1 -0
  12. package/dist/lib/chains/base.js +73 -0
  13. package/dist/lib/chains/base.js.map +1 -0
  14. package/dist/lib/chains/solana.d.ts +10 -0
  15. package/dist/lib/chains/solana.d.ts.map +1 -0
  16. package/dist/lib/chains/solana.js +49 -0
  17. package/dist/lib/chains/solana.js.map +1 -0
  18. package/dist/lib/chains/xrpl.d.ts +10 -0
  19. package/dist/lib/chains/xrpl.d.ts.map +1 -0
  20. package/dist/lib/chains/xrpl.js +55 -0
  21. package/dist/lib/chains/xrpl.js.map +1 -0
  22. package/dist/lib/credit/bureau.d.ts +10 -0
  23. package/dist/lib/credit/bureau.d.ts.map +1 -0
  24. package/dist/lib/credit/bureau.js +58 -0
  25. package/dist/lib/credit/bureau.js.map +1 -0
  26. package/dist/lib/sml-api/agentcard.d.ts +17 -0
  27. package/dist/lib/sml-api/agentcard.d.ts.map +1 -0
  28. package/dist/lib/sml-api/agentcard.js +30 -0
  29. package/dist/lib/sml-api/agentcard.js.map +1 -0
  30. package/dist/lib/sml-api/backtest.d.ts +22 -0
  31. package/dist/lib/sml-api/backtest.d.ts.map +1 -0
  32. package/dist/lib/sml-api/backtest.js +28 -0
  33. package/dist/lib/sml-api/backtest.js.map +1 -0
  34. package/dist/lib/sml-api/brokers.d.ts +40 -0
  35. package/dist/lib/sml-api/brokers.d.ts.map +1 -0
  36. package/dist/lib/sml-api/brokers.js +128 -0
  37. package/dist/lib/sml-api/brokers.js.map +1 -0
  38. package/dist/lib/sml-api/copytrader.d.ts +11 -0
  39. package/dist/lib/sml-api/copytrader.d.ts.map +1 -0
  40. package/dist/lib/sml-api/copytrader.js +30 -0
  41. package/dist/lib/sml-api/copytrader.js.map +1 -0
  42. package/dist/lib/sml-api/crawl.d.ts +20 -0
  43. package/dist/lib/sml-api/crawl.d.ts.map +1 -0
  44. package/dist/lib/sml-api/crawl.js +32 -0
  45. package/dist/lib/sml-api/crawl.js.map +1 -0
  46. package/dist/lib/sml-api/echo.d.ts +10 -0
  47. package/dist/lib/sml-api/echo.d.ts.map +1 -0
  48. package/dist/lib/sml-api/echo.js +23 -0
  49. package/dist/lib/sml-api/echo.js.map +1 -0
  50. package/dist/lib/sml-api/forge.d.ts +11 -0
  51. package/dist/lib/sml-api/forge.d.ts.map +1 -0
  52. package/dist/lib/sml-api/forge.js +29 -0
  53. package/dist/lib/sml-api/forge.js.map +1 -0
  54. package/dist/lib/sml-api/ftd.d.ts +18 -0
  55. package/dist/lib/sml-api/ftd.d.ts.map +1 -0
  56. package/dist/lib/sml-api/ftd.js +43 -0
  57. package/dist/lib/sml-api/ftd.js.map +1 -0
  58. package/dist/lib/sml-api/ghost.d.ts +13 -0
  59. package/dist/lib/sml-api/ghost.d.ts.map +1 -0
  60. package/dist/lib/sml-api/ghost.js +29 -0
  61. package/dist/lib/sml-api/ghost.js.map +1 -0
  62. package/dist/lib/sml-api/launchpad.d.ts +20 -0
  63. package/dist/lib/sml-api/launchpad.d.ts.map +1 -0
  64. package/dist/lib/sml-api/launchpad.js +31 -0
  65. package/dist/lib/sml-api/launchpad.js.map +1 -0
  66. package/dist/lib/sml-api/leviathan.d.ts +22 -0
  67. package/dist/lib/sml-api/leviathan.d.ts.map +1 -0
  68. package/dist/lib/sml-api/leviathan.js +33 -0
  69. package/dist/lib/sml-api/leviathan.js.map +1 -0
  70. package/dist/lib/sml-api/nexus.d.ts +18 -0
  71. package/dist/lib/sml-api/nexus.d.ts.map +1 -0
  72. package/dist/lib/sml-api/nexus.js +40 -0
  73. package/dist/lib/sml-api/nexus.js.map +1 -0
  74. package/dist/lib/sml-api/proof402.d.ts +6 -0
  75. package/dist/lib/sml-api/proof402.d.ts.map +1 -0
  76. package/dist/lib/sml-api/proof402.js +30 -0
  77. package/dist/lib/sml-api/proof402.js.map +1 -0
  78. package/dist/lib/sml-api/rails.d.ts +12 -0
  79. package/dist/lib/sml-api/rails.d.ts.map +1 -0
  80. package/dist/lib/sml-api/rails.js +29 -0
  81. package/dist/lib/sml-api/rails.js.map +1 -0
  82. package/dist/lib/sml-api/shadow.d.ts +15 -0
  83. package/dist/lib/sml-api/shadow.d.ts.map +1 -0
  84. package/dist/lib/sml-api/shadow.js +27 -0
  85. package/dist/lib/sml-api/shadow.js.map +1 -0
  86. package/dist/lib/sml-api/squeezeos.d.ts +21 -0
  87. package/dist/lib/sml-api/squeezeos.d.ts.map +1 -0
  88. package/dist/lib/sml-api/squeezeos.js +97 -0
  89. package/dist/lib/sml-api/squeezeos.js.map +1 -0
  90. package/dist/lib/sml-api/xdeo.d.ts +13 -0
  91. package/dist/lib/sml-api/xdeo.d.ts.map +1 -0
  92. package/dist/lib/sml-api/xdeo.js +34 -0
  93. package/dist/lib/sml-api/xdeo.js.map +1 -0
  94. package/dist/lib/sml-api/xmit.d.ts +13 -0
  95. package/dist/lib/sml-api/xmit.d.ts.map +1 -0
  96. package/dist/lib/sml-api/xmit.js +34 -0
  97. package/dist/lib/sml-api/xmit.js.map +1 -0
  98. package/dist/server/health.d.ts +16 -0
  99. package/dist/server/health.d.ts.map +1 -0
  100. package/dist/server/health.js +39 -0
  101. package/dist/server/health.js.map +1 -0
  102. package/dist/server/index.d.ts +3 -0
  103. package/dist/server/index.d.ts.map +1 -0
  104. package/dist/server/index.js +193 -0
  105. package/dist/server/index.js.map +1 -0
  106. package/dist/server/payments/ap2.d.ts +17 -0
  107. package/dist/server/payments/ap2.d.ts.map +1 -0
  108. package/dist/server/payments/ap2.js +75 -0
  109. package/dist/server/payments/ap2.js.map +1 -0
  110. package/dist/server/payments/receipt.d.ts +28 -0
  111. package/dist/server/payments/receipt.d.ts.map +1 -0
  112. package/dist/server/payments/receipt.js +60 -0
  113. package/dist/server/payments/receipt.js.map +1 -0
  114. package/dist/server/payments/router.d.ts +23 -0
  115. package/dist/server/payments/router.d.ts.map +1 -0
  116. package/dist/server/payments/router.js +69 -0
  117. package/dist/server/payments/router.js.map +1 -0
  118. package/dist/server/payments/wallet.d.ts +18 -0
  119. package/dist/server/payments/wallet.d.ts.map +1 -0
  120. package/dist/server/payments/wallet.js +107 -0
  121. package/dist/server/payments/wallet.js.map +1 -0
  122. package/dist/server/payments/x402.d.ts +29 -0
  123. package/dist/server/payments/x402.d.ts.map +1 -0
  124. package/dist/server/payments/x402.js +122 -0
  125. package/dist/server/payments/x402.js.map +1 -0
  126. package/dist/server/registry/catalog.d.ts +12 -0
  127. package/dist/server/registry/catalog.d.ts.map +1 -0
  128. package/dist/server/registry/catalog.js +55 -0
  129. package/dist/server/registry/catalog.js.map +1 -0
  130. package/dist/server/registry/discovery.d.ts +16 -0
  131. package/dist/server/registry/discovery.d.ts.map +1 -0
  132. package/dist/server/registry/discovery.js +33 -0
  133. package/dist/server/registry/discovery.js.map +1 -0
  134. package/dist/server/registry/pricing.d.ts +10 -0
  135. package/dist/server/registry/pricing.d.ts.map +1 -0
  136. package/dist/server/registry/pricing.js +66 -0
  137. package/dist/server/registry/pricing.js.map +1 -0
  138. package/dist/server/security/acl.d.ts +28 -0
  139. package/dist/server/security/acl.d.ts.map +1 -0
  140. package/dist/server/security/acl.js +36 -0
  141. package/dist/server/security/acl.js.map +1 -0
  142. package/dist/server/security/audit.d.ts +15 -0
  143. package/dist/server/security/audit.d.ts.map +1 -0
  144. package/dist/server/security/audit.js +77 -0
  145. package/dist/server/security/audit.js.map +1 -0
  146. package/dist/server/security/rate-limit.d.ts +12 -0
  147. package/dist/server/security/rate-limit.d.ts.map +1 -0
  148. package/dist/server/security/rate-limit.js +72 -0
  149. package/dist/server/security/rate-limit.js.map +1 -0
  150. package/dist/server/security/sandbox.d.ts +7 -0
  151. package/dist/server/security/sandbox.d.ts.map +1 -0
  152. package/dist/server/security/sandbox.js +42 -0
  153. package/dist/server/security/sandbox.js.map +1 -0
  154. package/dist/server/tools/agentcard.d.ts +3 -0
  155. package/dist/server/tools/agentcard.d.ts.map +1 -0
  156. package/dist/server/tools/agentcard.js +118 -0
  157. package/dist/server/tools/agentcard.js.map +1 -0
  158. package/dist/server/tools/backtest.d.ts +3 -0
  159. package/dist/server/tools/backtest.d.ts.map +1 -0
  160. package/dist/server/tools/backtest.js +112 -0
  161. package/dist/server/tools/backtest.js.map +1 -0
  162. package/dist/server/tools/brokers.d.ts +3 -0
  163. package/dist/server/tools/brokers.d.ts.map +1 -0
  164. package/dist/server/tools/brokers.js +223 -0
  165. package/dist/server/tools/brokers.js.map +1 -0
  166. package/dist/server/tools/copytrader.d.ts +3 -0
  167. package/dist/server/tools/copytrader.d.ts.map +1 -0
  168. package/dist/server/tools/copytrader.js +90 -0
  169. package/dist/server/tools/copytrader.js.map +1 -0
  170. package/dist/server/tools/crawl.d.ts +3 -0
  171. package/dist/server/tools/crawl.d.ts.map +1 -0
  172. package/dist/server/tools/crawl.js +60 -0
  173. package/dist/server/tools/crawl.js.map +1 -0
  174. package/dist/server/tools/discovery.d.ts +3 -0
  175. package/dist/server/tools/discovery.d.ts.map +1 -0
  176. package/dist/server/tools/discovery.js +188 -0
  177. package/dist/server/tools/discovery.js.map +1 -0
  178. package/dist/server/tools/echo.d.ts +3 -0
  179. package/dist/server/tools/echo.d.ts.map +1 -0
  180. package/dist/server/tools/echo.js +48 -0
  181. package/dist/server/tools/echo.js.map +1 -0
  182. package/dist/server/tools/forge.d.ts +3 -0
  183. package/dist/server/tools/forge.d.ts.map +1 -0
  184. package/dist/server/tools/forge.js +77 -0
  185. package/dist/server/tools/forge.js.map +1 -0
  186. package/dist/server/tools/ftd.d.ts +3 -0
  187. package/dist/server/tools/ftd.d.ts.map +1 -0
  188. package/dist/server/tools/ftd.js +70 -0
  189. package/dist/server/tools/ftd.js.map +1 -0
  190. package/dist/server/tools/ghost.d.ts +3 -0
  191. package/dist/server/tools/ghost.d.ts.map +1 -0
  192. package/dist/server/tools/ghost.js +83 -0
  193. package/dist/server/tools/ghost.js.map +1 -0
  194. package/dist/server/tools/index.d.ts +3 -0
  195. package/dist/server/tools/index.d.ts.map +1 -0
  196. package/dist/server/tools/index.js +44 -0
  197. package/dist/server/tools/index.js.map +1 -0
  198. package/dist/server/tools/launchpad.d.ts +3 -0
  199. package/dist/server/tools/launchpad.d.ts.map +1 -0
  200. package/dist/server/tools/launchpad.js +151 -0
  201. package/dist/server/tools/launchpad.js.map +1 -0
  202. package/dist/server/tools/leviathan.d.ts +3 -0
  203. package/dist/server/tools/leviathan.d.ts.map +1 -0
  204. package/dist/server/tools/leviathan.js +73 -0
  205. package/dist/server/tools/leviathan.js.map +1 -0
  206. package/dist/server/tools/nexus.d.ts +3 -0
  207. package/dist/server/tools/nexus.d.ts.map +1 -0
  208. package/dist/server/tools/nexus.js +65 -0
  209. package/dist/server/tools/nexus.js.map +1 -0
  210. package/dist/server/tools/proof402.d.ts +3 -0
  211. package/dist/server/tools/proof402.d.ts.map +1 -0
  212. package/dist/server/tools/proof402.js +74 -0
  213. package/dist/server/tools/proof402.js.map +1 -0
  214. package/dist/server/tools/rails.d.ts +3 -0
  215. package/dist/server/tools/rails.d.ts.map +1 -0
  216. package/dist/server/tools/rails.js +82 -0
  217. package/dist/server/tools/rails.js.map +1 -0
  218. package/dist/server/tools/shadow.d.ts +3 -0
  219. package/dist/server/tools/shadow.d.ts.map +1 -0
  220. package/dist/server/tools/shadow.js +114 -0
  221. package/dist/server/tools/shadow.js.map +1 -0
  222. package/dist/server/tools/squeezeos.d.ts +3 -0
  223. package/dist/server/tools/squeezeos.d.ts.map +1 -0
  224. package/dist/server/tools/squeezeos.js +231 -0
  225. package/dist/server/tools/squeezeos.js.map +1 -0
  226. package/dist/server/tools/xdeo.d.ts +3 -0
  227. package/dist/server/tools/xdeo.d.ts.map +1 -0
  228. package/dist/server/tools/xdeo.js +58 -0
  229. package/dist/server/tools/xdeo.js.map +1 -0
  230. package/dist/server/tools/xmit.d.ts +3 -0
  231. package/dist/server/tools/xmit.d.ts.map +1 -0
  232. package/dist/server/tools/xmit.js +59 -0
  233. package/dist/server/tools/xmit.js.map +1 -0
  234. package/docker-compose.yml +50 -0
  235. package/llms.txt +70 -0
  236. package/package.json +77 -0
  237. package/render.yaml +39 -0
  238. package/sdk/mcp-x402-sdk/package.json +18 -0
  239. package/sdk/mcp-x402-sdk/src/index.ts +118 -0
  240. package/sdk/mcp-x402-sdk/tsconfig.json +14 -0
  241. package/server.json +60 -0
  242. package/services/backtest_service.py +176 -0
  243. package/src/lib/chains/base.ts +77 -0
  244. package/src/lib/chains/solana.ts +59 -0
  245. package/src/lib/chains/xrpl.ts +63 -0
  246. package/src/lib/credit/bureau.ts +65 -0
  247. package/src/lib/sml-api/agentcard.ts +40 -0
  248. package/src/lib/sml-api/backtest.ts +47 -0
  249. package/src/lib/sml-api/brokers.ts +160 -0
  250. package/src/lib/sml-api/copytrader.ts +33 -0
  251. package/src/lib/sml-api/crawl.ts +44 -0
  252. package/src/lib/sml-api/echo.ts +28 -0
  253. package/src/lib/sml-api/forge.ts +33 -0
  254. package/src/lib/sml-api/ftd.ts +53 -0
  255. package/src/lib/sml-api/ghost.ts +35 -0
  256. package/src/lib/sml-api/launchpad.ts +43 -0
  257. package/src/lib/sml-api/leviathan.ts +49 -0
  258. package/src/lib/sml-api/nexus.ts +50 -0
  259. package/src/lib/sml-api/proof402.ts +27 -0
  260. package/src/lib/sml-api/rails.ts +34 -0
  261. package/src/lib/sml-api/shadow.ts +35 -0
  262. package/src/lib/sml-api/squeezeos.ts +95 -0
  263. package/src/lib/sml-api/xdeo.ts +40 -0
  264. package/src/lib/sml-api/xmit.ts +40 -0
  265. package/src/server/health.ts +52 -0
  266. package/src/server/index.ts +206 -0
  267. package/src/server/payments/ap2.ts +99 -0
  268. package/src/server/payments/receipt.ts +85 -0
  269. package/src/server/payments/router.ts +110 -0
  270. package/src/server/payments/wallet.ts +123 -0
  271. package/src/server/payments/x402.ts +162 -0
  272. package/src/server/registry/catalog.ts +61 -0
  273. package/src/server/registry/discovery.ts +39 -0
  274. package/src/server/registry/pricing.ts +76 -0
  275. package/src/server/security/acl.ts +42 -0
  276. package/src/server/security/audit.ts +94 -0
  277. package/src/server/security/rate-limit.ts +84 -0
  278. package/src/server/security/sandbox.ts +40 -0
  279. package/src/server/tools/agentcard.ts +134 -0
  280. package/src/server/tools/backtest.ts +119 -0
  281. package/src/server/tools/brokers.ts +250 -0
  282. package/src/server/tools/copytrader.ts +104 -0
  283. package/src/server/tools/crawl.ts +70 -0
  284. package/src/server/tools/discovery.ts +202 -0
  285. package/src/server/tools/echo.ts +58 -0
  286. package/src/server/tools/forge.ts +87 -0
  287. package/src/server/tools/ftd.ts +88 -0
  288. package/src/server/tools/ghost.ts +93 -0
  289. package/src/server/tools/index.ts +42 -0
  290. package/src/server/tools/launchpad.ts +173 -0
  291. package/src/server/tools/leviathan.ts +81 -0
  292. package/src/server/tools/nexus.ts +76 -0
  293. package/src/server/tools/proof402.ts +87 -0
  294. package/src/server/tools/rails.ts +92 -0
  295. package/src/server/tools/shadow.ts +128 -0
  296. package/src/server/tools/squeezeos.ts +312 -0
  297. package/src/server/tools/xdeo.ts +67 -0
  298. package/src/server/tools/xmit.ts +68 -0
  299. package/tests/integration/e2e.test.ts +51 -0
  300. package/tests/unit/payments.test.ts +49 -0
  301. package/tests/unit/security.test.ts +92 -0
  302. package/tests/unit/tools.test.ts +42 -0
  303. package/tsconfig.json +21 -0
  304. package/vitest.config.ts +20 -0
@@ -0,0 +1,162 @@
1
+ import { z } from 'zod';
2
+ import { AuditLogger } from '../security/audit.js';
3
+ import { AP2Client } from './ap2.js';
4
+ import { WalletManager } from './wallet.js';
5
+ import { ChainRouter } from './router.js';
6
+ import { ReceiptStore } from './receipt.js';
7
+ import { CreditBureau } from '../../lib/credit/bureau.js';
8
+ import { PriceRegistry } from '../registry/pricing.js';
9
+
10
+ export const PaymentConfigSchema = z.object({
11
+ price: z.string().regex(/^\d+(\.\d+)?$/),
12
+ currency: z.enum(['USDC', 'RLUSD']),
13
+ toolName: z.string(),
14
+ walletAddress: z.string().optional(),
15
+ });
16
+
17
+ export type PaymentConfig = z.infer<typeof PaymentConfigSchema>;
18
+
19
+ export interface PaymentResult {
20
+ receiptId: string;
21
+ txHash: string;
22
+ chain: string;
23
+ amountPaid: string;
24
+ currency: string;
25
+ timestamp: number;
26
+ walletAddress: string;
27
+ }
28
+
29
+ const AUTO_APPROVE_THRESHOLD = parseFloat(
30
+ process.env['AUTO_APPROVE_THRESHOLD_USD'] ?? '1.0',
31
+ );
32
+
33
+ const DAILY_SPEND_CAP = parseFloat(
34
+ process.env['DAILY_SPEND_CAP_USD'] ?? '50.0',
35
+ );
36
+
37
+ const dailySpend = new Map<string, { amount: number; date: string }>();
38
+
39
+ function getTodayKey(): string {
40
+ return new Date().toISOString().slice(0, 10);
41
+ }
42
+
43
+ function getDailySpend(wallet: string): number {
44
+ const today = getTodayKey();
45
+ const entry = dailySpend.get(wallet);
46
+ if (!entry || entry.date !== today) return 0;
47
+ return entry.amount;
48
+ }
49
+
50
+ function addDailySpend(wallet: string, amount: number): void {
51
+ const today = getTodayKey();
52
+ const current = getDailySpend(wallet);
53
+ dailySpend.set(wallet, { amount: current + amount, date: today });
54
+ }
55
+
56
+ export async function executeX402Payment(
57
+ config: PaymentConfig,
58
+ ): Promise<PaymentResult> {
59
+ const audit = AuditLogger.getInstance();
60
+ const wallet = await WalletManager.getInstance().getOrCreateWallet();
61
+ const walletAddress = wallet.address;
62
+
63
+ // Enforce daily spend cap (N9)
64
+ const priceNum = parseFloat(config.price);
65
+ const currentSpend = getDailySpend(walletAddress);
66
+ if (currentSpend + priceNum > DAILY_SPEND_CAP) {
67
+ audit.warn('spend_cap_exceeded', {
68
+ wallet: walletAddress,
69
+ current: currentSpend,
70
+ requested: priceNum,
71
+ cap: DAILY_SPEND_CAP,
72
+ });
73
+ throw new Error(
74
+ `Daily spend cap of $${DAILY_SPEND_CAP} exceeded. Current: $${currentSpend.toFixed(4)}`,
75
+ );
76
+ }
77
+
78
+ // Verify price cache freshness (N12)
79
+ const cachedPrice = await PriceRegistry.getInstance().getPrice(config.toolName);
80
+ if (!cachedPrice) {
81
+ throw new Error('Price data stale or unavailable. Rejecting payment.');
82
+ }
83
+ if (cachedPrice !== config.price) {
84
+ throw new Error(
85
+ `Price mismatch: expected ${cachedPrice}, got ${config.price}. Cache may be stale.`,
86
+ );
87
+ }
88
+
89
+ // Credit Bureau check (N8)
90
+ const score = await CreditBureau.getInstance().getScore(walletAddress);
91
+ const autoApprove = priceNum <= AUTO_APPROVE_THRESHOLD && score >= 300;
92
+
93
+ audit.info('payment_attempt', {
94
+ tool: config.toolName,
95
+ price: config.price,
96
+ currency: config.currency,
97
+ wallet: walletAddress,
98
+ bureauScore: score,
99
+ autoApprove,
100
+ });
101
+
102
+ if (!autoApprove && score < 300) {
103
+ throw new Error(
104
+ `Credit Bureau score ${score} below minimum 300. Payment requires manual approval.`,
105
+ );
106
+ }
107
+
108
+ // AP2 mandate verification (N6)
109
+ const ap2 = AP2Client.getInstance();
110
+ const mandateValid = await ap2.verifyMandate(walletAddress, {
111
+ maxAmount: config.price,
112
+ currency: config.currency,
113
+ toolName: config.toolName,
114
+ });
115
+
116
+ if (!mandateValid) {
117
+ audit.warn('ap2_mandate_rejected', { wallet: walletAddress, tool: config.toolName });
118
+ throw new Error(
119
+ 'AP2 mandate verification failed. Agent not authorized for this payment.',
120
+ );
121
+ }
122
+
123
+ // Route payment to cheapest/fastest chain (N13)
124
+ const router = ChainRouter.getInstance();
125
+ const txResult = await router.route({
126
+ amount: config.price,
127
+ currency: config.currency,
128
+ from: walletAddress,
129
+ to: process.env['SML_PAYMENT_RECEIVER'] ?? '',
130
+ timeoutMs: 500,
131
+ });
132
+
133
+ addDailySpend(walletAddress, priceNum);
134
+
135
+ // Generate 402Proof receipt (N7)
136
+ const receipt = await ReceiptStore.getInstance().create({
137
+ txHash: txResult.txHash,
138
+ chain: txResult.chain,
139
+ amount: config.price,
140
+ currency: config.currency,
141
+ tool: config.toolName,
142
+ wallet: walletAddress,
143
+ });
144
+
145
+ audit.info('payment_success', {
146
+ receiptId: receipt.id,
147
+ txHash: txResult.txHash,
148
+ chain: txResult.chain,
149
+ tool: config.toolName,
150
+ wallet: walletAddress,
151
+ });
152
+
153
+ return {
154
+ receiptId: receipt.id,
155
+ txHash: txResult.txHash,
156
+ chain: txResult.chain,
157
+ amountPaid: config.price,
158
+ currency: config.currency,
159
+ timestamp: Date.now(),
160
+ walletAddress,
161
+ };
162
+ }
@@ -0,0 +1,61 @@
1
+ export interface ToolMeta {
2
+ name: string;
3
+ description: string;
4
+ price: string;
5
+ currency: 'USDC' | 'RLUSD';
6
+ freeTier?: string;
7
+ ap2Required: boolean;
8
+ cacheTtl?: number;
9
+ }
10
+
11
+ export const CATALOG: ToolMeta[] = [
12
+ {
13
+ name: 'leviathan_signal',
14
+ description: 'Institutional-grade squeeze signals. Multi-engine verdict for any ticker.',
15
+ price: '0.05',
16
+ currency: 'USDC',
17
+ ap2Required: true,
18
+ },
19
+ {
20
+ name: 'xmit_edgar_decode',
21
+ description: 'Parse SEC DEF 14A / 13F / 13D filings. Raw text never leaves SML servers.',
22
+ price: '0.02',
23
+ currency: 'USDC',
24
+ ap2Required: true,
25
+ },
26
+ {
27
+ name: 'xdeo_earnings_estimate',
28
+ description: 'Decentralized earnings oracle. +2 bureau_score on success.',
29
+ price: '0.02',
30
+ currency: 'USDC',
31
+ ap2Required: true,
32
+ },
33
+ {
34
+ name: 'ftd_threshold_scan',
35
+ description: 'SEC Reg SHO FTD data. Alerts free; full data 0.05 USDC. 15-min cache.',
36
+ price: '0.05',
37
+ currency: 'USDC',
38
+ ap2Required: false,
39
+ freeTier: 'alerts_only',
40
+ cacheTtl: 900,
41
+ },
42
+ {
43
+ name: 'nexus_agent_hire',
44
+ description: 'Agent marketplace. Query free; hire charges 5% commission.',
45
+ price: '0.00',
46
+ currency: 'USDC',
47
+ ap2Required: false,
48
+ freeTier: 'query_only',
49
+ },
50
+ {
51
+ name: 'crawl_paid_fetch',
52
+ description: 'Pay-per-fetch scraping. Humans bypass free.',
53
+ price: '0.005',
54
+ currency: 'USDC',
55
+ ap2Required: false,
56
+ },
57
+ ];
58
+
59
+ export function getToolMeta(name: string): ToolMeta | undefined {
60
+ return CATALOG.find((t) => t.name === name);
61
+ }
@@ -0,0 +1,39 @@
1
+ import { readFileSync } from 'fs';
2
+ import { join } from 'path';
3
+
4
+ interface AgentsJson {
5
+ schema_version: string;
6
+ name: string;
7
+ tools: unknown[];
8
+ }
9
+
10
+ export class Discovery {
11
+ private static instance: Discovery;
12
+ private agentsJson: AgentsJson | null = null;
13
+ private llmsTxt: string | null = null;
14
+
15
+ private constructor() {}
16
+
17
+ static getInstance(): Discovery {
18
+ if (!Discovery.instance) {
19
+ Discovery.instance = new Discovery();
20
+ }
21
+ return Discovery.instance;
22
+ }
23
+
24
+ getAgentsJson(): AgentsJson {
25
+ if (!this.agentsJson) {
26
+ const path = join(process.cwd(), 'agents.json');
27
+ this.agentsJson = JSON.parse(readFileSync(path, 'utf8')) as AgentsJson;
28
+ }
29
+ return this.agentsJson;
30
+ }
31
+
32
+ getLlmsTxt(): string {
33
+ if (!this.llmsTxt) {
34
+ const path = join(process.cwd(), 'llms.txt');
35
+ this.llmsTxt = readFileSync(path, 'utf8');
36
+ }
37
+ return this.llmsTxt;
38
+ }
39
+ }
@@ -0,0 +1,76 @@
1
+ const PRICE_CACHE_TTL = parseInt(process.env['PRICE_CACHE_TTL_MS'] ?? '60000', 10);
2
+
3
+ const BASE_PRICES: Record<string, string> = {
4
+ leviathan_signal: '0.05',
5
+ xmit_edgar_decode: '0.02',
6
+ xdeo_earnings_estimate: '0.02',
7
+ ftd_threshold_scan: '0.05',
8
+ nexus_agent_hire: '0.00', // commission-based
9
+ crawl_paid_fetch: '0.005',
10
+ };
11
+
12
+ interface CachedPrice {
13
+ price: string;
14
+ fetchedAt: number;
15
+ }
16
+
17
+ export class PriceRegistry {
18
+ private static instance: PriceRegistry;
19
+ private readonly cache = new Map<string, CachedPrice>();
20
+ private readonly baseUrl: string;
21
+
22
+ private constructor() {
23
+ this.baseUrl = process.env['SML_API_BASE'] ?? 'https://api.scriptmasterlabs.com';
24
+ }
25
+
26
+ static getInstance(): PriceRegistry {
27
+ if (!PriceRegistry.instance) {
28
+ PriceRegistry.instance = new PriceRegistry();
29
+ }
30
+ return PriceRegistry.instance;
31
+ }
32
+
33
+ async getPrice(toolName: string): Promise<string | null> {
34
+ const cached = this.cache.get(toolName);
35
+ const now = Date.now();
36
+
37
+ if (cached && now - cached.fetchedAt < PRICE_CACHE_TTL) {
38
+ return cached.price;
39
+ }
40
+
41
+ // Fetch live price from SML pricing API
42
+ try {
43
+ const res = await fetch(`${this.baseUrl}/pricing/v1/tool/${toolName}`, {
44
+ signal: AbortSignal.timeout(3000),
45
+ });
46
+
47
+ if (res.ok) {
48
+ const body = (await res.json()) as { price: string };
49
+ this.cache.set(toolName, { price: body.price, fetchedAt: now });
50
+ return body.price;
51
+ }
52
+ } catch {
53
+ // Fall through to hardcoded baseline
54
+ }
55
+
56
+ // Use hardcoded baseline if API unavailable
57
+ const fallback = BASE_PRICES[toolName];
58
+ if (fallback !== undefined) {
59
+ // Cache fallback for 30s (half normal TTL) to retry sooner
60
+ this.cache.set(toolName, { price: fallback, fetchedAt: now - PRICE_CACHE_TTL / 2 });
61
+ return fallback;
62
+ }
63
+
64
+ // Price unknown and cache stale (N12) — reject
65
+ return null;
66
+ }
67
+
68
+ seedDefaults(): void {
69
+ const now = Date.now();
70
+ for (const [tool, price] of Object.entries(BASE_PRICES)) {
71
+ if (!this.cache.has(tool)) {
72
+ this.cache.set(tool, { price, fetchedAt: now });
73
+ }
74
+ }
75
+ }
76
+ }
@@ -0,0 +1,42 @@
1
+ import { z } from 'zod';
2
+
3
+ export const ToolACLSchema = z.object({
4
+ toolName: z.string(),
5
+ walletAddress: z.string().optional(),
6
+ creditScore: z.number().optional(),
7
+ paidTier: z.boolean().default(false),
8
+ });
9
+
10
+ export type ToolACL = z.infer<typeof ToolACLSchema>;
11
+
12
+ const FREE_TOOLS = new Set(['ftd_threshold_scan_alerts', 'nexus_agent_hire_query']);
13
+
14
+ export class ACL {
15
+ private static instance: ACL;
16
+
17
+ private constructor() {}
18
+
19
+ static getInstance(): ACL {
20
+ if (!ACL.instance) {
21
+ ACL.instance = new ACL();
22
+ }
23
+ return ACL.instance;
24
+ }
25
+
26
+ isFree(toolName: string): boolean {
27
+ return FREE_TOOLS.has(toolName);
28
+ }
29
+
30
+ requiresPayment(toolName: string): boolean {
31
+ return !this.isFree(toolName);
32
+ }
33
+
34
+ requiresAP2(toolName: string): boolean {
35
+ // leviathan, xmit, xdeo require AP2 per spec
36
+ return ['leviathan_signal', 'xmit_edgar_decode', 'xdeo_earnings_estimate'].includes(toolName);
37
+ }
38
+
39
+ minCreditScore(_toolName: string): number {
40
+ return 300;
41
+ }
42
+ }
@@ -0,0 +1,94 @@
1
+ import { createHash, createHmac } from 'crypto';
2
+ import { appendFileSync } from 'fs';
3
+
4
+ type LogLevel = 'info' | 'warn' | 'error';
5
+
6
+ interface LogEntry {
7
+ seq: number;
8
+ ts: number;
9
+ level: LogLevel;
10
+ event: string;
11
+ data: Record<string, unknown>;
12
+ prev_hash: string;
13
+ hash: string;
14
+ }
15
+
16
+ // Append-only SHA-256 chained audit log (N5)
17
+ // Each entry includes the hash of the previous entry — tampering breaks the chain.
18
+ export class AuditLogger {
19
+ private static instance: AuditLogger;
20
+ private seq = 0;
21
+ private prevHash = '0000000000000000000000000000000000000000000000000000000000000000';
22
+ private readonly logPath: string;
23
+ private readonly hmacSecret: string;
24
+
25
+ private constructor() {
26
+ this.logPath = process.env['AUDIT_LOG_PATH'] ?? './audit.log';
27
+ this.hmacSecret = process.env['AUDIT_HMAC_SECRET'] ?? 'mcp-x402-audit-secret';
28
+ }
29
+
30
+ static getInstance(): AuditLogger {
31
+ if (!AuditLogger.instance) {
32
+ AuditLogger.instance = new AuditLogger();
33
+ }
34
+ return AuditLogger.instance;
35
+ }
36
+
37
+ private log(level: LogLevel, event: string, data: Record<string, unknown>): void {
38
+ const seq = ++this.seq;
39
+ const ts = Date.now();
40
+
41
+ // Redact PII (N3): hash wallet addresses, never log raw filing content
42
+ const safeData = this.redact(data);
43
+
44
+ const payload = JSON.stringify({ seq, ts, level, event, data: safeData, prev_hash: this.prevHash });
45
+ const hash = createHmac('sha256', this.hmacSecret).update(payload).digest('hex');
46
+
47
+ const entry: LogEntry = {
48
+ seq,
49
+ ts,
50
+ level,
51
+ event,
52
+ data: safeData,
53
+ prev_hash: this.prevHash,
54
+ hash,
55
+ };
56
+
57
+ this.prevHash = hash;
58
+
59
+ try {
60
+ appendFileSync(this.logPath, JSON.stringify(entry) + '\n', 'utf8');
61
+ } catch {
62
+ // If log write fails, emit to stderr but don't crash
63
+ process.stderr.write(`[audit-fail] ${JSON.stringify(entry)}\n`);
64
+ }
65
+ }
66
+
67
+ private redact(data: Record<string, unknown>): Record<string, unknown> {
68
+ const out: Record<string, unknown> = {};
69
+ for (const [k, v] of Object.entries(data)) {
70
+ if (k === 'wallet' || k === 'address') {
71
+ // Hash wallet addresses (N3)
72
+ out[k] = createHash('sha256').update(String(v)).digest('hex').slice(0, 16) + '...';
73
+ } else if (k === 'content' || k === 'raw_text' || k === 'filing') {
74
+ // Never log raw filing data (N3)
75
+ out[k] = '[REDACTED]';
76
+ } else {
77
+ out[k] = v;
78
+ }
79
+ }
80
+ return out;
81
+ }
82
+
83
+ info(event: string, data: Record<string, unknown> = {}): void {
84
+ this.log('info', event, data);
85
+ }
86
+
87
+ warn(event: string, data: Record<string, unknown> = {}): void {
88
+ this.log('warn', event, data);
89
+ }
90
+
91
+ error(event: string, data: Record<string, unknown> = {}): void {
92
+ this.log('error', event, data);
93
+ }
94
+ }
@@ -0,0 +1,84 @@
1
+ interface BucketState {
2
+ minute: { count: number; resetAt: number };
3
+ day: { count: number; resetAt: number };
4
+ }
5
+
6
+ const PER_TOOL_MINUTE_LIMIT = 100;
7
+ const PER_WALLET_DAY_LIMIT = 1000;
8
+ const IP_MINUTE_LIMIT = 200;
9
+
10
+ function nowMs(): number {
11
+ return Date.now();
12
+ }
13
+
14
+ export class RateLimiter {
15
+ private static instance: RateLimiter;
16
+ private readonly toolBuckets = new Map<string, BucketState>();
17
+ private readonly walletBuckets = new Map<string, BucketState>();
18
+ private readonly ipBuckets = new Map<string, { count: number; resetAt: number }>();
19
+
20
+ private constructor() {}
21
+
22
+ static getInstance(): RateLimiter {
23
+ if (!RateLimiter.instance) {
24
+ RateLimiter.instance = new RateLimiter();
25
+ }
26
+ return RateLimiter.instance;
27
+ }
28
+
29
+ checkTool(toolName: string): boolean {
30
+ const now = nowMs();
31
+ let bucket = this.toolBuckets.get(toolName);
32
+
33
+ if (!bucket) {
34
+ bucket = {
35
+ minute: { count: 0, resetAt: now + 60_000 },
36
+ day: { count: 0, resetAt: now + 86_400_000 },
37
+ };
38
+ this.toolBuckets.set(toolName, bucket);
39
+ }
40
+
41
+ if (now > bucket.minute.resetAt) {
42
+ bucket.minute = { count: 0, resetAt: now + 60_000 };
43
+ }
44
+
45
+ if (bucket.minute.count >= PER_TOOL_MINUTE_LIMIT) return false;
46
+ bucket.minute.count++;
47
+ return true;
48
+ }
49
+
50
+ checkWallet(wallet: string): boolean {
51
+ const now = nowMs();
52
+ let bucket = this.walletBuckets.get(wallet);
53
+
54
+ if (!bucket) {
55
+ bucket = {
56
+ minute: { count: 0, resetAt: now + 60_000 },
57
+ day: { count: 0, resetAt: now + 86_400_000 },
58
+ };
59
+ this.walletBuckets.set(wallet, bucket);
60
+ }
61
+
62
+ if (now > bucket.day.resetAt) {
63
+ bucket.day = { count: 0, resetAt: now + 86_400_000 };
64
+ }
65
+
66
+ if (bucket.day.count >= PER_WALLET_DAY_LIMIT) return false;
67
+ bucket.day.count++;
68
+ return true;
69
+ }
70
+
71
+ checkIp(ip: string): boolean {
72
+ const now = nowMs();
73
+ let entry = this.ipBuckets.get(ip);
74
+
75
+ if (!entry || now > entry.resetAt) {
76
+ entry = { count: 0, resetAt: now + 60_000 };
77
+ this.ipBuckets.set(ip, entry);
78
+ }
79
+
80
+ if (entry.count >= IP_MINUTE_LIMIT) return false;
81
+ entry.count++;
82
+ return true;
83
+ }
84
+ }
@@ -0,0 +1,40 @@
1
+ import { z } from 'zod';
2
+
3
+ // Sandboxed input validation layer — all tool inputs pass through here before execution.
4
+ // No eval(), no dynamic require(), no raw SQL (N4 enforcement at schema layer).
5
+ export class Sandbox {
6
+ static validate<T>(schema: z.ZodType<T>, input: unknown): T {
7
+ const result = schema.safeParse(input);
8
+ if (!result.success) {
9
+ const issues = result.error.issues
10
+ .map((i) => `${i.path.join('.')}: ${i.message}`)
11
+ .join('; ');
12
+ throw new Error(`Input validation failed: ${issues}`);
13
+ }
14
+ return result.data;
15
+ }
16
+
17
+ // Ensure URL is http/https only — no file://, data://, javascript:
18
+ static validateUrl(raw: string): URL {
19
+ let url: URL;
20
+ try {
21
+ url = new URL(raw);
22
+ } catch {
23
+ throw new Error(`Invalid URL: ${raw}`);
24
+ }
25
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') {
26
+ throw new Error(`Disallowed URL protocol: ${url.protocol}`);
27
+ }
28
+ return url;
29
+ }
30
+
31
+ // Strip any response content that looks like a prompt injection attempt
32
+ static sanitizeApiResponse(text: string): string {
33
+ // Remove common injection markers
34
+ return text
35
+ .replace(/<\/?system>/gi, '')
36
+ .replace(/\[INST\]/gi, '')
37
+ .replace(/\[\/?INST\]/gi, '')
38
+ .slice(0, 50_000); // Hard cap on returned content size
39
+ }
40
+ }