@dvmkit/sdk 0.0.0 → 0.1.0-rc.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.
Files changed (41) hide show
  1. package/NOTICE +2 -0
  2. package/README.md +38 -2
  3. package/dist/chunk-27V2ILSR.js +291 -0
  4. package/dist/chunk-5GFED3GJ.js +955 -0
  5. package/dist/chunk-6JZIX5WW.js +1155 -0
  6. package/dist/chunk-7IH5SG2A.js +1038 -0
  7. package/dist/chunk-AT6V3SY7.js +102 -0
  8. package/dist/chunk-DCNT4PJS.js +733 -0
  9. package/dist/chunk-DMNLFNTW.js +135 -0
  10. package/dist/chunk-FROTD5XQ.js +70 -0
  11. package/dist/chunk-H25M54MI.js +149 -0
  12. package/dist/chunk-KQAJVVZT.js +712 -0
  13. package/dist/chunk-KXWROQGK.js +74 -0
  14. package/dist/chunk-L4OYF4DQ.js +67 -0
  15. package/dist/chunk-NTK5DJ6R.js +1256 -0
  16. package/dist/chunk-RPXHKMYE.js +3808 -0
  17. package/dist/chunk-S3XAHZQY.js +63 -0
  18. package/dist/chunk-YG7G4DPZ.js +25 -0
  19. package/dist/credit-ledger-EDMEZSA2.js +28 -0
  20. package/dist/index.d.ts +144 -0
  21. package/dist/index.js +303 -0
  22. package/dist/job-store-C5n6bhap.d.ts +5090 -0
  23. package/dist/memory-credit-ledger-7TTZDSRS.js +9 -0
  24. package/dist/mpp-secret-state-WNAQQ6K4.js +127 -0
  25. package/dist/mpp-setup-MOBWGTWJ.js +30 -0
  26. package/dist/postgres-consumed-credential-store-VHBT4KEA.js +72 -0
  27. package/dist/postgres-job-store-J5F4GUWU.js +7 -0
  28. package/dist/postgres-kv-store-JFBDP5IP.js +7 -0
  29. package/dist/postgres-replay-store-UJXRT6VO.js +7 -0
  30. package/dist/pricing-4CEB34RM.js +48 -0
  31. package/dist/processed-payment-store-HAA4SFNK.js +11 -0
  32. package/dist/revenue-reporter-M35KP6V7.js +435 -0
  33. package/dist/server/index.d.ts +4108 -0
  34. package/dist/server/index.js +22538 -0
  35. package/dist/ssrf-BdHsrrIb.d.ts +325 -0
  36. package/dist/tempo-charge-store-6GJEMNUU.js +130 -0
  37. package/dist/tempo-session-store-FTEEGZXA.js +467 -0
  38. package/dist/testing/index.d.ts +135 -0
  39. package/dist/testing/index.js +151 -0
  40. package/dist/x402-35VLYFKZ.js +1272 -0
  41. package/package.json +89 -6
@@ -0,0 +1,733 @@
1
+ import {
2
+ callerLoggers
3
+ } from "./chunk-KXWROQGK.js";
4
+
5
+ // src/lib/x402/constants.ts
6
+ var X402_DEFAULT_FACILITATOR = "https://x402.org/facilitator";
7
+
8
+ // src/lib/errors.ts
9
+ var DvmError = class extends Error {
10
+ constructor(code, message, hint, data, display, proseSources) {
11
+ super(message);
12
+ this.code = code;
13
+ this.hint = hint;
14
+ this.data = data;
15
+ this.display = display;
16
+ this.proseSources = proseSources;
17
+ this.name = "DvmError";
18
+ }
19
+ code;
20
+ hint;
21
+ data;
22
+ display;
23
+ proseSources;
24
+ };
25
+
26
+ // src/lib/x402/networks.ts
27
+ var X402_NETWORKS = [
28
+ {
29
+ caip2: "eip155:8453",
30
+ slug: "base",
31
+ label: "Base",
32
+ usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
33
+ domainName: "USD Coin",
34
+ domainVersion: "2",
35
+ viemChain: "base",
36
+ nativeGas: "ETH",
37
+ confusable: "USDbC"
38
+ },
39
+ {
40
+ caip2: "eip155:84532",
41
+ slug: "base-sepolia",
42
+ label: "Base Sepolia",
43
+ usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
44
+ domainName: "USDC",
45
+ domainVersion: "2",
46
+ viemChain: "baseSepolia",
47
+ nativeGas: "ETH",
48
+ confusable: null
49
+ },
50
+ {
51
+ caip2: "eip155:43114",
52
+ slug: "avalanche",
53
+ label: "Avalanche",
54
+ usdc: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
55
+ domainName: "USD Coin",
56
+ domainVersion: "2",
57
+ viemChain: "avalanche",
58
+ nativeGas: "AVAX",
59
+ confusable: "USDC.e"
60
+ },
61
+ {
62
+ caip2: "eip155:43113",
63
+ slug: "avalanche-fuji",
64
+ label: "Avalanche Fuji",
65
+ usdc: "0x5425890298aed601595a70AB815c96711a31Bc65",
66
+ domainName: "USD Coin",
67
+ domainVersion: "2",
68
+ viemChain: "avalancheFuji",
69
+ nativeGas: "AVAX",
70
+ confusable: null
71
+ },
72
+ {
73
+ caip2: "eip155:137",
74
+ slug: "polygon",
75
+ label: "Polygon",
76
+ usdc: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
77
+ domainName: "USD Coin (PoS)",
78
+ domainVersion: "2",
79
+ viemChain: "polygon",
80
+ nativeGas: "POL",
81
+ confusable: "USDC.e"
82
+ },
83
+ {
84
+ caip2: "eip155:80002",
85
+ slug: "polygon-amoy",
86
+ label: "Polygon Amoy",
87
+ usdc: "0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582",
88
+ domainName: "USDC",
89
+ domainVersion: "2",
90
+ viemChain: "polygonAmoy",
91
+ nativeGas: "POL",
92
+ confusable: null
93
+ }
94
+ ];
95
+ function x402NetworkByCaip2(caip2) {
96
+ return BY_CAIP2.get(caip2);
97
+ }
98
+ function x402NetworkBySlug(slug) {
99
+ return BY_SLUG.get(slug);
100
+ }
101
+ function x402NetworkLabel(network) {
102
+ const spec = x402NetworkByCaip2(network);
103
+ return spec ? `${spec.label} (${network})` : network;
104
+ }
105
+ function x402NetworkListForCopy() {
106
+ return X402_NETWORKS.map((n) => `${n.caip2} (${n.label})`).join(", ");
107
+ }
108
+ function assertSupportedX402Network(network) {
109
+ if (x402NetworkByCaip2(network)) return;
110
+ throw new DvmError(
111
+ "unsupported_network",
112
+ `Unsupported x402 network: ${network}.`,
113
+ `${x402SupportedNetworksHint()} If a wallet is connected, run 'dvm wallet x402-network <caip2>'. For a first connection, pass --network to 'dvm wallet x402-connect'.`
114
+ );
115
+ }
116
+ function x402SupportedNetworksHint() {
117
+ return `This rail settles native USDC on: ${x402NetworkListForCopy()}.`;
118
+ }
119
+ var BY_CAIP2 = new Map(X402_NETWORKS.map((n) => [n.caip2, n]));
120
+ var BY_SLUG = new Map(X402_NETWORKS.map((n) => [n.slug, n]));
121
+
122
+ // src/lib/x402/facilitator.ts
123
+ import { createPrivateKey, randomBytes } from "crypto";
124
+ import { HTTPFacilitatorClient } from "@x402/core/server";
125
+ import { importJWK, importPKCS8, SignJWT } from "jose";
126
+ var AUTH_HEADER_TTL_MS = 6e4;
127
+ var AUTH_TOKEN_TTL_SECONDS = 120;
128
+ var FACILITATOR_PATHS = {
129
+ verify: "POST",
130
+ settle: "POST",
131
+ supported: "GET"
132
+ };
133
+ function createX402FacilitatorAuthHeaders(config) {
134
+ const auth = config.facilitatorAuth;
135
+ if (!auth) return void 0;
136
+ const facilitator = new URL(config.facilitator ?? X402_DEFAULT_FACILITATOR);
137
+ const basePath = facilitator.pathname.replace(/\/+$/, "");
138
+ let cached;
139
+ return async () => {
140
+ const nowMs = Date.now();
141
+ if (cached && nowMs < cached.refreshAtMs) return cached.headers;
142
+ const entries = await Promise.all(
143
+ Object.entries(FACILITATOR_PATHS).map(async ([path, method]) => {
144
+ const requestPath = `${basePath}/${path}`;
145
+ const jwt = await generateCdpJwt(
146
+ auth.keyId,
147
+ auth.keySecret,
148
+ `${method} ${facilitator.host}${requestPath}`
149
+ );
150
+ return [path, { Authorization: `Bearer ${jwt}` }];
151
+ })
152
+ );
153
+ const headers = Object.fromEntries(entries);
154
+ cached = { headers, refreshAtMs: nowMs + AUTH_HEADER_TTL_MS };
155
+ return headers;
156
+ };
157
+ }
158
+ function createX402FacilitatorClient(config, opts = {}) {
159
+ return new HTTPFacilitatorClient({
160
+ url: config.facilitator ?? X402_DEFAULT_FACILITATOR,
161
+ ...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
162
+ ...config.facilitatorAuth ? { createAuthHeaders: createX402FacilitatorAuthHeaders(config) } : {}
163
+ });
164
+ }
165
+ async function generateCdpJwt(keyId, keySecret, uri) {
166
+ const now = Math.floor(Date.now() / 1e3);
167
+ const nonce = randomBytes(16).toString("hex");
168
+ const claims = { sub: keyId, iss: "cdp", uris: [uri] };
169
+ try {
170
+ const ecKey = await importPKCS8(normalizeEs256KeySecret(keySecret), "ES256");
171
+ return await new SignJWT(claims).setProtectedHeader({ alg: "ES256", kid: keyId, typ: "JWT", nonce }).setIssuedAt(now).setNotBefore(now).setExpirationTime(now + AUTH_TOKEN_TTL_SECONDS).sign(ecKey);
172
+ } catch {
173
+ }
174
+ const decoded = Buffer.from(keySecret, "base64");
175
+ if (decoded.length !== 64) {
176
+ throw new Error("Invalid CDP key secret: expected an ES256 PEM or base64 Ed25519 key");
177
+ }
178
+ const jwk = {
179
+ kty: "OKP",
180
+ crv: "Ed25519",
181
+ d: decoded.subarray(0, 32).toString("base64url"),
182
+ x: decoded.subarray(32).toString("base64url")
183
+ };
184
+ const edKey = await importJWK(jwk, "EdDSA");
185
+ return new SignJWT(claims).setProtectedHeader({ alg: "EdDSA", kid: keyId, typ: "JWT", nonce }).setIssuedAt(now).setNotBefore(now).setExpirationTime(now + AUTH_TOKEN_TTL_SECONDS).sign(edKey);
186
+ }
187
+ function normalizeEs256KeySecret(keySecret) {
188
+ if (!keySecret.includes("-----BEGIN EC PRIVATE KEY-----")) return keySecret;
189
+ return createPrivateKey(keySecret).export({ format: "pem", type: "pkcs8" });
190
+ }
191
+
192
+ // src/lib/x402/spec.ts
193
+ var X402_VERSION = 2;
194
+ var X402_V1_VERSION = 1;
195
+ var X402_DEFAULT_NETWORK = "eip155:8453";
196
+ var X402_EXACT_SCHEME = "exact";
197
+ var X402_BATCH_SETTLEMENT_SCHEME = "batch-settlement";
198
+ function buildPaymentRequirements(opts) {
199
+ const { x402Config, requiredUsdcMicro, resource, description, mimeType } = opts;
200
+ const version = opts.version ?? X402_VERSION;
201
+ const network = version === X402_V1_VERSION ? caip2ToX402Network(x402Config.network) : x402Config.network;
202
+ const usdcContract = usdcContractByCaip2(x402Config.network);
203
+ const configuredAsset = x402Config.asset;
204
+ const asset = version === X402_VERSION && configuredAsset?.toUpperCase() === "USDC" ? usdcContract ?? configuredAsset : configuredAsset ?? usdcContract ?? "USDC";
205
+ const extra = paymentRequirementExtra(x402Config.network, asset, usdcContract, version);
206
+ if (version === X402_VERSION) {
207
+ return {
208
+ scheme: "exact",
209
+ network,
210
+ amount: requiredUsdcMicro.toString(),
211
+ payTo: x402Config.payTo,
212
+ asset,
213
+ maxTimeoutSeconds: 60,
214
+ extra
215
+ };
216
+ }
217
+ return {
218
+ scheme: "exact",
219
+ network,
220
+ maxAmountRequired: requiredUsdcMicro.toString(),
221
+ resource,
222
+ description: description ?? x402Config.description ?? "",
223
+ mimeType: mimeType ?? "application/json",
224
+ payTo: x402Config.payTo,
225
+ asset,
226
+ maxTimeoutSeconds: 60,
227
+ ...Object.keys(extra).length > 0 ? { extra } : {}
228
+ };
229
+ }
230
+ function buildPaymentRequiredV2(opts) {
231
+ const description = opts.description ?? opts.x402Config.description ?? "";
232
+ const mimeType = opts.mimeType ?? "application/json";
233
+ return {
234
+ x402Version: X402_VERSION,
235
+ ...opts.error ? { error: opts.error } : {},
236
+ resource: {
237
+ url: opts.resource,
238
+ ...description ? { description } : {},
239
+ ...mimeType ? { mimeType } : {}
240
+ },
241
+ accepts: [buildPaymentRequirements({ ...opts, version: X402_VERSION })],
242
+ extensions: {}
243
+ };
244
+ }
245
+ function paymentRequiredV2FromV1(requirements, error) {
246
+ const first = requirements.at(0);
247
+ if (!first) throw new Error("Cannot build PAYMENT-REQUIRED without requirements");
248
+ return {
249
+ x402Version: X402_VERSION,
250
+ ...error ? { error } : {},
251
+ resource: {
252
+ url: first.resource,
253
+ ...first.description ? { description: first.description } : {},
254
+ ...first.mimeType ? { mimeType: first.mimeType } : {}
255
+ },
256
+ accepts: requirements.map((requirement) => {
257
+ const network = x402NetworkBySlug(requirement.network)?.caip2 ?? requirement.network;
258
+ const usdcContract = usdcContractByCaip2(network);
259
+ const asset = requirement.asset.toUpperCase() === "USDC" ? usdcContract ?? requirement.asset : requirement.asset;
260
+ return {
261
+ scheme: "exact",
262
+ network,
263
+ amount: requirement.maxAmountRequired,
264
+ asset,
265
+ payTo: requirement.payTo,
266
+ maxTimeoutSeconds: requirement.maxTimeoutSeconds,
267
+ extra: {
268
+ ...requirement.extra,
269
+ ...paymentRequirementExtra(network, asset, usdcContract, X402_VERSION),
270
+ assetTransferMethod: "eip3009"
271
+ }
272
+ };
273
+ }),
274
+ extensions: {}
275
+ };
276
+ }
277
+ function caip2ToX402Network(network) {
278
+ return x402NetworkByCaip2(network)?.slug ?? network;
279
+ }
280
+ function x402NetworkToCaip2(network) {
281
+ return x402NetworkBySlug(network)?.caip2 ?? network;
282
+ }
283
+ function chainIdFromCaip2(network) {
284
+ const parts = network.split(":");
285
+ return Number.parseInt(parts[1] ?? "8453", 10);
286
+ }
287
+ function usdcContractFor(networkSlug) {
288
+ return x402NetworkBySlug(networkSlug)?.usdc;
289
+ }
290
+ function usdcContractByCaip2(caip2) {
291
+ return x402NetworkByCaip2(caip2)?.usdc;
292
+ }
293
+ function usdcDomainNameFor(networkSlug) {
294
+ return x402NetworkBySlug(networkSlug)?.domainName;
295
+ }
296
+ function usdcDomainVersionFor(networkSlug) {
297
+ return x402NetworkBySlug(networkSlug)?.domainVersion;
298
+ }
299
+ function decodePayment(header) {
300
+ const decoded = Buffer.from(header, "base64").toString("utf-8");
301
+ const parsed = JSON.parse(decoded);
302
+ if (!parsed || typeof parsed !== "object") throw new Error("Invalid payment header shape");
303
+ const version = parsed.x402Version;
304
+ const scheme = version === X402_V1_VERSION ? parsed.scheme : version === X402_VERSION && isRecord(parsed.accepted) ? parsed.accepted.scheme : void 0;
305
+ if (typeof scheme !== "string") throw new Error("Invalid payment scheme");
306
+ const decoder = PAYMENT_SCHEME_DECODERS[scheme];
307
+ if (!decoder) throw new Error(`Unsupported x402 payment scheme: ${scheme}`);
308
+ return decoder(parsed);
309
+ }
310
+ function encodePayment(payload) {
311
+ return Buffer.from(JSON.stringify(payload), "utf-8").toString("base64");
312
+ }
313
+ function encodePaymentRequiredHeader(required) {
314
+ return Buffer.from(JSON.stringify(required), "utf-8").toString("base64");
315
+ }
316
+ function decodePaymentRequiredHeader(header) {
317
+ const parsed = JSON.parse(Buffer.from(header, "base64").toString("utf-8"));
318
+ if (!isRecord(parsed) || parsed.x402Version !== X402_VERSION || !isRecord(parsed.resource)) {
319
+ throw new Error("Invalid PAYMENT-REQUIRED header shape");
320
+ }
321
+ if (typeof parsed.resource.url !== "string" || !Array.isArray(parsed.accepts)) {
322
+ throw new Error("Invalid PAYMENT-REQUIRED header shape");
323
+ }
324
+ for (const requirement of parsed.accepts) validateV2Requirements(requirement);
325
+ return parsed;
326
+ }
327
+ function exactEvmAuthorization(payload) {
328
+ return payload.payload.authorization;
329
+ }
330
+ function encodeSettleResponseHeader(response) {
331
+ return Buffer.from(JSON.stringify(response), "utf-8").toString("base64");
332
+ }
333
+ async function verifyWithFacilitator(payload, requirements, config = {}, createAuthHeaders = createX402FacilitatorAuthHeaders(config)) {
334
+ const facilitator = config.facilitator ?? X402_DEFAULT_FACILITATOR;
335
+ const url = facilitator.replace(/\/$/, "") + "/verify";
336
+ return callerLoggers.x402.span(
337
+ "x402.verify_with_facilitator",
338
+ { facilitator_url: url },
339
+ async () => {
340
+ const authHeaders = (await createAuthHeaders?.())?.verify ?? {};
341
+ const res = await fetch(url, {
342
+ method: "POST",
343
+ headers: { "Content-Type": "application/json", ...authHeaders },
344
+ body: JSON.stringify({
345
+ x402Version: payload.x402Version,
346
+ paymentPayload: payload,
347
+ paymentRequirements: requirements
348
+ })
349
+ });
350
+ if (!res.ok) {
351
+ const rejection = await readFacilitatorRejection(res);
352
+ if (rejection?.isValid === false && typeof rejection.invalidReason === "string" && rejection.invalidReason.length > 0) {
353
+ return rejection;
354
+ }
355
+ return { isValid: false, invalidReason: `facilitator_status_${res.status}` };
356
+ }
357
+ const data = await res.json();
358
+ callerLoggers.x402.info("x402.verify_with_facilitator.result", { is_valid: data.isValid });
359
+ return data;
360
+ }
361
+ );
362
+ }
363
+ async function settleWithFacilitator(payload, requirements, config = {}, createAuthHeaders = createX402FacilitatorAuthHeaders(config)) {
364
+ const facilitator = config.facilitator ?? X402_DEFAULT_FACILITATOR;
365
+ const url = facilitator.replace(/\/$/, "") + "/settle";
366
+ return callerLoggers.x402.span(
367
+ "x402.settle_with_facilitator",
368
+ { facilitator_url: url },
369
+ async () => {
370
+ const authHeaders = (await createAuthHeaders?.())?.settle ?? {};
371
+ const res = await fetch(url, {
372
+ method: "POST",
373
+ headers: { "Content-Type": "application/json", ...authHeaders },
374
+ body: JSON.stringify({
375
+ x402Version: payload.x402Version,
376
+ paymentPayload: payload,
377
+ paymentRequirements: requirements
378
+ })
379
+ });
380
+ if (!res.ok) {
381
+ const rejection = await readFacilitatorRejection(res);
382
+ if (rejection?.success === false && typeof rejection.errorReason === "string" && rejection.errorReason.length > 0) {
383
+ return rejection;
384
+ }
385
+ return { success: false, errorReason: `facilitator_status_${res.status}` };
386
+ }
387
+ const data = await res.json();
388
+ callerLoggers.x402.info("x402.settle_with_facilitator.result", {
389
+ success: data.success,
390
+ tx_hash: data.transaction
391
+ });
392
+ return data;
393
+ }
394
+ );
395
+ }
396
+ async function readFacilitatorRejection(res) {
397
+ if (res.status !== 400) return null;
398
+ try {
399
+ const body = await res.json();
400
+ return isRecord(body) ? body : null;
401
+ } catch {
402
+ return null;
403
+ }
404
+ }
405
+ var PAYMENT_SCHEME_DECODERS = {
406
+ exact: decodeExactPayment
407
+ };
408
+ function decodeExactPayment(parsed) {
409
+ validateExactEvmPayload(parsed.payload);
410
+ if (parsed.x402Version === X402_V1_VERSION) {
411
+ if (parsed.scheme !== "exact" || typeof parsed.network !== "string") {
412
+ throw new Error("Invalid x402 v1 exact payload");
413
+ }
414
+ return parsed;
415
+ }
416
+ if (parsed.x402Version === X402_VERSION) {
417
+ validateV2Requirements(parsed.accepted);
418
+ if (parsed.resource !== void 0 && !isResourceInfo(parsed.resource)) {
419
+ throw new Error("Invalid x402 v2 resource");
420
+ }
421
+ if (parsed.extensions !== void 0 && !isRecord(parsed.extensions)) {
422
+ throw new Error("Invalid x402 v2 extensions");
423
+ }
424
+ return parsed;
425
+ }
426
+ throw new Error(`Unsupported x402 version: ${String(parsed.x402Version)}`);
427
+ }
428
+ function validateV2Requirements(value) {
429
+ if (!isRecord(value) || typeof value.scheme !== "string" || typeof value.network !== "string" || typeof value.amount !== "string" || typeof value.asset !== "string" || typeof value.payTo !== "string" || typeof value.maxTimeoutSeconds !== "number" || !isOptionalRecord(value.extra)) {
430
+ throw new Error("Invalid x402 v2 payment requirements");
431
+ }
432
+ }
433
+ function validateExactEvmPayload(value) {
434
+ if (!isRecord(value) || typeof value.signature !== "string" || !isRecord(value.authorization)) {
435
+ throw new Error("Invalid exact EVM payload");
436
+ }
437
+ const authorization = value.authorization;
438
+ for (const field of ["from", "to", "value", "validAfter", "validBefore", "nonce"]) {
439
+ if (typeof authorization[field] !== "string") throw new Error("Invalid exact authorization");
440
+ }
441
+ }
442
+ function paymentRequirementExtra(caip2, asset, usdcContract, version) {
443
+ if (asset.toLowerCase() !== usdcContract?.toLowerCase()) return {};
444
+ const network = x402NetworkByCaip2(caip2);
445
+ if (!network) return {};
446
+ return {
447
+ ...version === X402_VERSION ? { assetTransferMethod: "eip3009" } : {},
448
+ name: network.domainName,
449
+ version: network.domainVersion
450
+ };
451
+ }
452
+ function isResourceInfo(value) {
453
+ return isRecord(value) && typeof value.url === "string";
454
+ }
455
+ function isRecord(value) {
456
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
457
+ }
458
+ function isOptionalRecord(value) {
459
+ return value === void 0 || value === null || isRecord(value);
460
+ }
461
+
462
+ // src/lib/credit-implicit.ts
463
+ var IMPLICIT_CREDIT_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
464
+
465
+ // src/lib/currency.ts
466
+ import { existsSync, readFileSync, writeFileSync } from "fs";
467
+ import { dirname } from "path";
468
+
469
+ // src/defaults.ts
470
+ import { homedir } from "os";
471
+ import { join } from "path";
472
+ var CONFIG_DIR = process.env.DVM_CONFIG_DIR ?? join(homedir(), ".dvm");
473
+ var CONFIG_FILE = join(CONFIG_DIR, "config.json");
474
+ var CONFIG_BACKUP_FILE = join(CONFIG_DIR, "config.json.bak");
475
+ var CONFIG_AUDIT_FILE = join(CONFIG_DIR, "config-audit.jsonl");
476
+ var CONFIG_PINNED_FILE = join(CONFIG_DIR, "config.json.pinned");
477
+ var JOBS_FILE = join(CONFIG_DIR, "jobs.json");
478
+ var REQUEST_INTENTS_DIR = join(CONFIG_DIR, "request-intents");
479
+ var FAVORITES_FILE = join(CONFIG_DIR, "favorites.json");
480
+ var MESSAGES_DIR = join(CONFIG_DIR, "messages");
481
+ var RECEIPTS_FILE = join(CONFIG_DIR, "receipts.jsonl");
482
+ var CREDITS_FILE = join(CONFIG_DIR, "credits.json");
483
+ var X402_CHANNELS_FILE = join(CONFIG_DIR, "x402-channels.json");
484
+ var TEMPO_CHANNELS_FILE = join(CONFIG_DIR, "tempo-channels.json");
485
+ var FEEDBACK_NUDGES_FILE = join(CONFIG_DIR, "feedback-nudges.json");
486
+ var RECOVERY_MNEMONIC_FILE = join(CONFIG_DIR, "recovery.mnemonic");
487
+ var WALLET_FILE = join(CONFIG_DIR, "wallet.json");
488
+ var AGENT_MNEMONIC_FILE = join(CONFIG_DIR, "wallet.mnemonic");
489
+ var IDENTITIES_FILE = join(CONFIG_DIR, "identities.json");
490
+ var PAIRINGS_DIR = join(CONFIG_DIR, "pairings");
491
+ var BUDGET_FILE = join(CONFIG_DIR, "budget.json");
492
+ var BTC_RATE_FILE = join(CONFIG_DIR, "btc-rate.json");
493
+ var ADMIN_DIR = join(CONFIG_DIR, "admin");
494
+ var PROXY_STATE_FILE = join(ADMIN_DIR, "proxy.json");
495
+ var BUILDER_CONFIG_DIR = process.env.DVMKIT_CONFIG_DIR ?? join(homedir(), ".dvmkit");
496
+ var BUILDER_FILE = join(BUILDER_CONFIG_DIR, "builder.json");
497
+ var BUILDER_MNEMONIC_FILE = join(BUILDER_CONFIG_DIR, "recovery.mnemonic");
498
+ var BUILDER_KEY_FILE = join(BUILDER_CONFIG_DIR, "builder.key");
499
+ var MELT_PENDING_LOG_FILE = join(BUILDER_CONFIG_DIR, "melt-pending.log");
500
+ var DRAIN_PARK_LOG_FILE = join(BUILDER_CONFIG_DIR, "drain-park.log");
501
+ var DRAIN_HOLD_STATE_FILE = join(BUILDER_CONFIG_DIR, "drain-hold.json");
502
+ var MELT_SUCCESS_FILE = join(BUILDER_CONFIG_DIR, "melt-success.json");
503
+
504
+ // src/lib/config-dir.ts
505
+ import { chmodSync, mkdirSync, statSync } from "fs";
506
+ var CONFIG_DIR_MODE = 448;
507
+ function ensureConfigDir() {
508
+ ensureConfigSubdir(CONFIG_DIR);
509
+ }
510
+ function ensureConfigSubdir(path) {
511
+ mkdirSync(path, { recursive: true, mode: CONFIG_DIR_MODE });
512
+ }
513
+
514
+ // src/lib/fx-coingecko.ts
515
+ var COINGECKO_BTC_USD_URL = "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd";
516
+ var DEFAULT_TIMEOUT_MS = 5e3;
517
+ var DEFAULT_USER_AGENT = "dvmkit-platform/1.0 (+https://dvmkit.com)";
518
+ async function fetchCoinGeckoBtcUsd(opts) {
519
+ const body = await fetchCoinGeckoBody(opts, COINGECKO_BTC_USD_URL);
520
+ const btc = body.bitcoin?.usd;
521
+ if (!isPositiveNumber(btc)) {
522
+ throw new Error("coingecko response missing bitcoin.usd");
523
+ }
524
+ return { btc_usd: btc, as_of: (/* @__PURE__ */ new Date()).toISOString() };
525
+ }
526
+ async function fetchCoinGeckoBody(opts, defaultUrl) {
527
+ const url = opts?.url ?? defaultUrl;
528
+ const fetchImpl = opts?.fetchImpl ?? fetch;
529
+ const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
530
+ const userAgent = opts?.userAgent ?? DEFAULT_USER_AGENT;
531
+ const res = await fetchImpl(url, {
532
+ headers: { "User-Agent": userAgent, Accept: "application/json" },
533
+ signal: AbortSignal.timeout(timeoutMs)
534
+ });
535
+ if (!res.ok) {
536
+ throw new Error(`coingecko returned HTTP ${res.status.toString()}`);
537
+ }
538
+ return await res.json();
539
+ }
540
+ function isPositiveNumber(v) {
541
+ return typeof v === "number" && Number.isFinite(v) && v > 0;
542
+ }
543
+
544
+ // src/lib/currency.ts
545
+ function msatsToUsd(msats, rate) {
546
+ return msats / 1e11 * rate;
547
+ }
548
+ function usdToMsats(usd, rate) {
549
+ return Math.round(usd / rate * 1e11);
550
+ }
551
+ function msatsToUsdc(msats, btcUsdRate) {
552
+ const usd = msatsToUsd(msats, btcUsdRate);
553
+ return Math.ceil(usd * 1e6);
554
+ }
555
+ function usdcToMsats(usdc, btcUsdRate) {
556
+ return usdToMsats(usdc, btcUsdRate);
557
+ }
558
+ var COST_ANCHOR_BANDS = {
559
+ usd: [0.01, 0.1, 1, 10],
560
+ eur: [0.01, 0.1, 1, 10],
561
+ gbp: [0.01, 0.1, 1, 10],
562
+ jpy: [1, 10, 100, 1500]
563
+ };
564
+ function costAnchor(amount, currency = "usd") {
565
+ const bands = COST_ANCHOR_BANDS[currency] ?? COST_ANCHOR_BANDS.usd;
566
+ if (amount < bands[0]) return "fraction of a cent";
567
+ if (amount <= bands[1]) return "comparable to an API call";
568
+ if (amount <= bands[2]) return "comparable to a ChatGPT query";
569
+ if (amount <= bands[3]) return "comparable to a coffee";
570
+ return "significant purchase \u2014 review carefully";
571
+ }
572
+ function formatUsd(usd) {
573
+ if (usd === 0) return "0.00";
574
+ if (usd >= 0.01) return usd.toFixed(2);
575
+ const digits = Math.max(2, -Math.floor(Math.log10(usd)) + 1);
576
+ return usd.toFixed(digits);
577
+ }
578
+ async function fetchBtcUsdRate() {
579
+ if (memRate && Date.now() - memTime < CACHE_TTL) {
580
+ return memRate;
581
+ }
582
+ const disk = readDiskCache();
583
+ if (disk) {
584
+ memRate = disk;
585
+ memTime = Date.now();
586
+ return disk;
587
+ }
588
+ const rate = await fetchFromApi();
589
+ memRate = rate;
590
+ memTime = Date.now();
591
+ writeDiskCache(rate);
592
+ return rate;
593
+ }
594
+ function getLastKnownBtcRate() {
595
+ if (memRate && memTime > 0) return { rate: memRate, cachedAt: memTime };
596
+ return readDiskCacheStale();
597
+ }
598
+ function formatStaleness(cachedAt, now = Date.now()) {
599
+ const ageMs = Math.max(0, now - cachedAt);
600
+ const seconds = Math.floor(ageMs / 1e3);
601
+ if (seconds < 60) return `${seconds}s ago`;
602
+ const minutes = Math.floor(seconds / 60);
603
+ if (minutes < 60) return `${minutes} min ago`;
604
+ const hours = Math.floor(minutes / 60);
605
+ if (hours < 24) return `${hours} hour${hours === 1 ? "" : "s"} ago`;
606
+ const days = Math.floor(hours / 24);
607
+ return `${days} day${days === 1 ? "" : "s"} ago`;
608
+ }
609
+ var CACHE_TTL = 5 * 60 * 1e3;
610
+ var CACHE_FILE = BTC_RATE_FILE;
611
+ var memRate = null;
612
+ var memTime = 0;
613
+ function readDiskCache() {
614
+ try {
615
+ if (!existsSync(CACHE_FILE)) return null;
616
+ const data = JSON.parse(readFileSync(CACHE_FILE, "utf-8"));
617
+ if (Date.now() - data.timestamp >= CACHE_TTL) return null;
618
+ if (!isPlausibleBtcUsdRate(data.rate)) return null;
619
+ return data.rate;
620
+ } catch {
621
+ return null;
622
+ }
623
+ }
624
+ function readDiskCacheStale() {
625
+ try {
626
+ if (!existsSync(CACHE_FILE)) return null;
627
+ const data = JSON.parse(readFileSync(CACHE_FILE, "utf-8"));
628
+ if (!isPlausibleBtcUsdRate(data.rate)) return null;
629
+ return { rate: data.rate, cachedAt: data.timestamp };
630
+ } catch {
631
+ return null;
632
+ }
633
+ }
634
+ function writeDiskCache(rate) {
635
+ try {
636
+ ensureConfigSubdir(dirname(CACHE_FILE));
637
+ writeFileSync(CACHE_FILE, JSON.stringify({ rate, timestamp: Date.now() }));
638
+ } catch {
639
+ }
640
+ }
641
+ var MAX_PLAUSIBLE_BTC_USD = 1e7;
642
+ function isPlausibleBtcUsdRate(rate) {
643
+ return typeof rate === "number" && Number.isFinite(rate) && rate > 0 && rate < MAX_PLAUSIBLE_BTC_USD;
644
+ }
645
+ async function fetchFromApi() {
646
+ const spot = await fetchCoinGeckoBtcUsd();
647
+ const rate = spot.btc_usd;
648
+ if (!isPlausibleBtcUsdRate(rate)) {
649
+ throw new Error(`implausible BTC/USD rate from upstream: ${String(rate)}`);
650
+ }
651
+ return rate;
652
+ }
653
+
654
+ // src/lib/payment-rails/rpc.ts
655
+ function resolveRpcOverride(envValue, bundled) {
656
+ const override = envValue?.trim() ?? "";
657
+ return override.length > 0 ? { url: override, source: "override" } : { url: bundled, source: "bundled" };
658
+ }
659
+ function resolveRpcHttpTransportOptions(source, envValue) {
660
+ if (source !== "override" || !envValue?.trim()) return void 0;
661
+ let parsed;
662
+ try {
663
+ parsed = JSON.parse(envValue);
664
+ } catch {
665
+ throw new Error("RPC headers must be a JSON object of non-empty string values.");
666
+ }
667
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
668
+ throw new Error("RPC headers must be a JSON object of non-empty string values.");
669
+ const headers = {};
670
+ for (const [name, value] of Object.entries(parsed)) {
671
+ if (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name) || typeof value !== "string" || value.length === 0 || /[\r\n]/.test(value))
672
+ throw new Error("RPC headers must be a JSON object of non-empty string values.");
673
+ headers[name] = value;
674
+ }
675
+ if (Object.keys(headers).length === 0)
676
+ throw new Error("RPC headers must be a JSON object of non-empty string values.");
677
+ return { fetchOptions: { headers } };
678
+ }
679
+
680
+ export {
681
+ X402_DEFAULT_FACILITATOR,
682
+ createX402FacilitatorAuthHeaders,
683
+ createX402FacilitatorClient,
684
+ DvmError,
685
+ X402_NETWORKS,
686
+ x402NetworkByCaip2,
687
+ x402NetworkLabel,
688
+ x402NetworkListForCopy,
689
+ assertSupportedX402Network,
690
+ x402SupportedNetworksHint,
691
+ X402_VERSION,
692
+ X402_V1_VERSION,
693
+ X402_DEFAULT_NETWORK,
694
+ X402_EXACT_SCHEME,
695
+ X402_BATCH_SETTLEMENT_SCHEME,
696
+ buildPaymentRequirements,
697
+ buildPaymentRequiredV2,
698
+ paymentRequiredV2FromV1,
699
+ caip2ToX402Network,
700
+ x402NetworkToCaip2,
701
+ chainIdFromCaip2,
702
+ usdcContractFor,
703
+ usdcContractByCaip2,
704
+ usdcDomainNameFor,
705
+ usdcDomainVersionFor,
706
+ decodePayment,
707
+ encodePayment,
708
+ encodePaymentRequiredHeader,
709
+ decodePaymentRequiredHeader,
710
+ exactEvmAuthorization,
711
+ encodeSettleResponseHeader,
712
+ verifyWithFacilitator,
713
+ settleWithFacilitator,
714
+ IMPLICIT_CREDIT_TTL_MS,
715
+ CONFIG_FILE,
716
+ CONFIG_BACKUP_FILE,
717
+ CONFIG_AUDIT_FILE,
718
+ CONFIG_PINNED_FILE,
719
+ X402_CHANNELS_FILE,
720
+ WALLET_FILE,
721
+ AGENT_MNEMONIC_FILE,
722
+ ensureConfigDir,
723
+ msatsToUsd,
724
+ msatsToUsdc,
725
+ usdcToMsats,
726
+ costAnchor,
727
+ formatUsd,
728
+ fetchBtcUsdRate,
729
+ getLastKnownBtcRate,
730
+ formatStaleness,
731
+ resolveRpcOverride,
732
+ resolveRpcHttpTransportOptions
733
+ };