@dvmkit/sdk 0.5.0-rc.1 → 0.7.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 (54) hide show
  1. package/README.md +100 -0
  2. package/dist/{chunk-JH3SX3LY.js → chunk-6XF3RVVX.js} +1 -1
  3. package/dist/chunk-AFV77S4C.js +258 -0
  4. package/dist/{chunk-RLTSCKRI.js → chunk-BPYMEMQX.js} +50 -3
  5. package/dist/chunk-BQJIFRAZ.js +36 -0
  6. package/dist/{chunk-IWQHUSMO.js → chunk-CXCQLOOK.js} +143 -2
  7. package/dist/{chunk-PB4FYZET.js → chunk-EQLA4FFY.js} +54 -4
  8. package/dist/{chunk-LE74UEV7.js → chunk-EZXAJ6EI.js} +64 -53
  9. package/dist/{chunk-FI4CST2D.js → chunk-FWGWLPXF.js} +4 -6
  10. package/dist/{chunk-EYTHFFFU.js → chunk-GY66THYI.js} +179 -56
  11. package/dist/{chunk-TZ2WKCDD.js → chunk-HCLVYZUZ.js} +2 -2
  12. package/dist/{chunk-O5AT6S5T.js → chunk-IV6Q7NRW.js} +25 -195
  13. package/dist/{chunk-UOCZXO2Z.js → chunk-JEVLNAO3.js} +10 -5
  14. package/dist/{chunk-TUA7PYH7.js → chunk-KMED3H3A.js} +134 -130
  15. package/dist/{chunk-Z4KLECAC.js → chunk-N5RZ5HVB.js} +1 -1
  16. package/dist/{chunk-PAIM66YA.js → chunk-NFMOPYBA.js} +47 -37
  17. package/dist/{chunk-B2IROPQF.js → chunk-O2OTZ5GP.js} +40 -15
  18. package/dist/{chunk-FYRR2UEZ.js → chunk-OML32F6I.js} +1 -1
  19. package/dist/{chunk-6DOGTAHA.js → chunk-QR2EF2NI.js} +9 -43
  20. package/dist/{chunk-7ZUKL4IV.js → chunk-RA2WIX7G.js} +5 -5
  21. package/dist/{chunk-A4YFWZ7R.js → chunk-RCM3P2HE.js} +1 -1
  22. package/dist/{chunk-PLG4XER6.js → chunk-S3OY533W.js} +1 -1
  23. package/dist/{chunk-GFY5FRX3.js → chunk-SZVXOQ75.js} +5 -12
  24. package/dist/{chunk-MZRJR2CW.js → chunk-WBOKPC37.js} +22 -3
  25. package/dist/content-retention-DHyLdA6b.d.ts +78 -0
  26. package/dist/{credit-ledger-MI2H7NEC.js → credit-ledger-MCUXYKSJ.js} +2 -3
  27. package/dist/{credit-menu-CAfqe8IY.d.ts → credit-menu-DjnkeEUU.d.ts} +98 -5
  28. package/dist/{fx-D9nsN3n4.d.ts → fx-zMWFzfrU.d.ts} +1 -1
  29. package/dist/index.d.ts +5 -5
  30. package/dist/index.js +3 -3
  31. package/dist/internal/caller.d.ts +1369 -860
  32. package/dist/internal/caller.js +1079 -991
  33. package/dist/internal/index.js +1 -1
  34. package/dist/internal/server.d.ts +8 -99
  35. package/dist/internal/server.js +19 -18
  36. package/dist/{job-store-Bx-VFRjg.d.ts → job-store-DdLXBpol.d.ts} +1 -1
  37. package/dist/{lightning-backend-BAx2Y8pP.d.ts → lightning-backend-Bw-qtAbt.d.ts} +4 -2
  38. package/dist/{memory-credit-ledger-W2BAUYJP.js → memory-credit-ledger-SBSMGMDX.js} +3 -4
  39. package/dist/{postgres-consumed-credential-store-CUQJKRSL.js → postgres-consumed-credential-store-BUVCAPLS.js} +13 -0
  40. package/dist/{postgres-job-store-VLXLWXUQ.js → postgres-job-store-VHLQBJZC.js} +3 -1
  41. package/dist/server/index.d.ts +28 -10
  42. package/dist/server/index.js +34 -24
  43. package/dist/{step-cache-QG_h5-3G.d.ts → step-cache-2MkvenJn.d.ts} +3 -3
  44. package/dist/tempo-lifecycle-GJ77YKVY.js +15 -0
  45. package/dist/{tempo-wallet-B5XXV3YH.js → tempo-wallet-X2GGAZ6X.js} +4 -4
  46. package/dist/testing/index.d.ts +9 -2
  47. package/dist/testing/index.js +6 -3
  48. package/dist/{usd-EwRqCPF-.d.ts → usd-DI3d3gTK.d.ts} +1 -1
  49. package/dist/{x402-R6HPBY5D.js → x402-FT4S4JS4.js} +8 -10
  50. package/package.json +5 -3
  51. package/recipes/postgres/financial-export-v1.sql +137 -0
  52. package/dist/chunk-CZULM42S.js +0 -143
  53. package/dist/chunk-LIRAHP7B.js +0 -53
  54. package/dist/tempo-lifecycle-ZBBLFBMH.js +0 -15
@@ -8,7 +8,7 @@ import {
8
8
  verifyDrainReceipt,
9
9
  verifyFundingReceipt,
10
10
  verifyReceipt
11
- } from "./chunk-PAIM66YA.js";
11
+ } from "./chunk-NFMOPYBA.js";
12
12
  import {
13
13
  AGENT_MNEMONIC_FILE,
14
14
  BUILDER_CONFIG_DIR,
@@ -16,7 +16,7 @@ import {
16
16
  BUILDER_KEY_FILE,
17
17
  WALLET_FILE,
18
18
  ensureConfigDir
19
- } from "./chunk-CZULM42S.js";
19
+ } from "./chunk-CXCQLOOK.js";
20
20
  import {
21
21
  canonicaliseForSigning
22
22
  } from "./chunk-OFVV37XC.js";
@@ -25,7 +25,7 @@ import {
25
25
  } from "./chunk-66HGCPBU.js";
26
26
  import {
27
27
  DvmError
28
- } from "./chunk-LIRAHP7B.js";
28
+ } from "./chunk-BPYMEMQX.js";
29
29
  import {
30
30
  diagnosticConsole
31
31
  } from "./chunk-ETXDTUJO.js";
@@ -40,6 +40,9 @@ function isX402SettlementReconciliationReason(value) {
40
40
  return value === "settlement_not_on_chain" || value === "chain_unreachable" || value === "settlement_unbookmarked" || value === "facilitator_channel_state_unavailable";
41
41
  }
42
42
 
43
+ // src/lib/payment-rails/tempo-wire.ts
44
+ var TEMPO_SESSION_UPDATED_REASON = "Tempo session updated; attach the next voucher to fund this credit.";
45
+
43
46
  // src/lib/mints.ts
44
47
  var RECOMMENDED_MINTS = [
45
48
  {
@@ -204,6 +207,9 @@ var DEFAULT_LOCK_RETRY_INTERVAL_MS = 1e3;
204
207
  var WALLET_VERSION = 9;
205
208
  var RECENT_SPENDS_CAP = 10;
206
209
  var DEFAULT_AGENT_MINT_URL = "https://mint.coinos.io";
210
+ function loadWalletConfiguredMints() {
211
+ return loadAgentWallet()?.mints ?? [];
212
+ }
207
213
  function loadAgentWallet() {
208
214
  if (!existsSync3(WALLET_FILE)) return null;
209
215
  const raw = readFileSync3(WALLET_FILE, "utf-8");
@@ -953,6 +959,112 @@ function generateEphemeralReceiptKey() {
953
959
  return { secret: bytesToHex(secretKey), pubkey: bytesToHex(publicKey) };
954
960
  }
955
961
 
962
+ // src/lib/wallet-agent/keypair.ts
963
+ import { secp256k1 as secp256k12 } from "@noble/curves/secp256k1.js";
964
+ var PRIVKEY_HEX_RE = /^[0-9a-f]{64}$/i;
965
+ function derivePubkeyFromPrivkey(privkey) {
966
+ if (!PRIVKEY_HEX_RE.test(privkey)) {
967
+ throw new DvmError(
968
+ "invalid_privkey",
969
+ "Privkey must be 64 hex characters (32 bytes).",
970
+ "Provide a valid secp256k1 private key as a 64-char hex string."
971
+ );
972
+ }
973
+ const bytes = hexToBytes2(privkey);
974
+ if (!secp256k12.utils.isValidSecretKey(bytes)) {
975
+ throw new DvmError(
976
+ "invalid_privkey",
977
+ "Privkey is not a valid secp256k1 scalar (zero or out of curve order).",
978
+ "Generate a fresh key with: dvm wallet init"
979
+ );
980
+ }
981
+ const pub = secp256k12.getPublicKey(bytes);
982
+ return bytesToHex2(pub);
983
+ }
984
+ function bytesToHex2(bytes) {
985
+ return Buffer.from(bytes).toString("hex");
986
+ }
987
+ function hexToBytes2(hex) {
988
+ return Uint8Array.from(Buffer.from(hex, "hex"));
989
+ }
990
+
991
+ // src/lib/wallet-agent/sign.ts
992
+ import { schnorr as schnorr2 } from "@noble/curves/secp256k1.js";
993
+ var PUBKEY_COMPRESSED_HEX_RE = /^0[23][0-9a-f]{64}$/i;
994
+ var PUBKEY_XONLY_HEX_RE = /^[0-9a-f]{64}$/i;
995
+ var SIG_HEX_RE = /^[0-9a-f]{128}$/i;
996
+ function signChallenge(privkeyHex, challenge) {
997
+ if (!PRIVKEY_HEX_RE.test(privkeyHex)) {
998
+ throw new DvmError(
999
+ "invalid_privkey",
1000
+ "Privkey must be 64 hex characters (32 bytes).",
1001
+ "Provide a valid secp256k1 private key as a 64-char hex string."
1002
+ );
1003
+ }
1004
+ if (challenge.length !== 32) {
1005
+ throw new DvmError(
1006
+ "invalid_challenge",
1007
+ `Challenge must be exactly 32 bytes; got ${challenge.length}.`,
1008
+ "Pass the SHA-256 digest output (32 bytes)."
1009
+ );
1010
+ }
1011
+ const sig = schnorr2.sign(challenge, hexToBytes3(privkeyHex));
1012
+ return bytesToHex3(sig);
1013
+ }
1014
+ function verifyChallenge(pubkeyHex, challenge, sigHex) {
1015
+ if (!SIG_HEX_RE.test(sigHex)) return false;
1016
+ if (challenge.length !== 32) return false;
1017
+ let xonly;
1018
+ if (PUBKEY_COMPRESSED_HEX_RE.test(pubkeyHex)) {
1019
+ xonly = hexToBytes3(pubkeyHex.slice(2));
1020
+ } else if (PUBKEY_XONLY_HEX_RE.test(pubkeyHex)) {
1021
+ xonly = hexToBytes3(pubkeyHex);
1022
+ } else {
1023
+ return false;
1024
+ }
1025
+ try {
1026
+ return schnorr2.verify(hexToBytes3(sigHex), challenge, xonly);
1027
+ } catch {
1028
+ return false;
1029
+ }
1030
+ }
1031
+ function hexToBytes3(hex) {
1032
+ return Uint8Array.from(Buffer.from(hex, "hex"));
1033
+ }
1034
+ function bytesToHex3(bytes) {
1035
+ return Buffer.from(bytes).toString("hex");
1036
+ }
1037
+
1038
+ // src/lib/cashu/admin-auth.ts
1039
+ import { randomBytes } from "crypto";
1040
+ import { sha256 as sha2562 } from "@noble/hashes/sha2.js";
1041
+ var ADMIN_AUTH_NONCE_TTL_SECONDS = 300;
1042
+ var ADMIN_AUTH_CLOCK_SKEW_SECONDS = 30;
1043
+ var ADMIN_STATE_UNAVAILABLE = "admin_state_unavailable";
1044
+ function buildAdminChallenge(args) {
1045
+ const msg = `dvmkit-admin:${args.method}:${args.path}:${args.dvmId}:${args.nonceHex}:${args.unixSeconds}`;
1046
+ return sha2562(new TextEncoder().encode(msg));
1047
+ }
1048
+ function buildAdminAuthHeader(args) {
1049
+ const nowMs = args.now ? args.now() : Date.now();
1050
+ const unixSeconds = Math.floor(nowMs / 1e3);
1051
+ const nonceBytes = args.randomNonce ? args.randomNonce() : Uint8Array.from(randomBytes(16));
1052
+ const nonceHex = Buffer.from(nonceBytes).toString("hex");
1053
+ const challenge = buildAdminChallenge({
1054
+ method: args.method,
1055
+ path: args.path,
1056
+ dvmId: args.dvmId,
1057
+ nonceHex,
1058
+ unixSeconds
1059
+ });
1060
+ const sigHex = signChallenge(args.privkeyHex, challenge);
1061
+ return {
1062
+ header: `P2PK ${nonceHex}.${unixSeconds}.${sigHex}`,
1063
+ nonceHex,
1064
+ unixSeconds
1065
+ };
1066
+ }
1067
+
956
1068
  // src/lib/builder-identity.ts
957
1069
  import {
958
1070
  chmodSync as chmodSync3,
@@ -964,8 +1076,8 @@ import {
964
1076
  unlinkSync as unlinkSync4,
965
1077
  writeFileSync as writeFileSync5
966
1078
  } from "fs";
967
- import { schnorr as schnorr2 } from "@noble/curves/secp256k1.js";
968
- import { sha256 as sha2562 } from "@noble/hashes/sha2.js";
1079
+ import { schnorr as schnorr3 } from "@noble/curves/secp256k1.js";
1080
+ import { sha256 as sha2563 } from "@noble/hashes/sha2.js";
969
1081
 
970
1082
  // src/lib/builder-store.ts
971
1083
  import {
@@ -1070,8 +1182,8 @@ function migrateV1Store(parsed) {
1070
1182
 
1071
1183
  // src/lib/builder-identity.ts
1072
1184
  function generateIdentity() {
1073
- const { secretKey, publicKey } = schnorr2.keygen();
1074
- return { pubkey: bytesToHex2(publicKey), secret: bytesToHex2(secretKey) };
1185
+ const { secretKey, publicKey } = schnorr3.keygen();
1186
+ return { pubkey: bytesToHex4(publicKey), secret: bytesToHex4(secretKey) };
1075
1187
  }
1076
1188
  function loadIdentitySecret() {
1077
1189
  if (!existsSync5(BUILDER_KEY_FILE)) return null;
@@ -1114,7 +1226,7 @@ function writeIdentity(keypair, opts = {}) {
1114
1226
  setIdentityPubkey(keypair.pubkey);
1115
1227
  }
1116
1228
  function hashCapabilities(capabilities) {
1117
- return bytesToHex2(sha2562(canonicaliseForSigning(capabilities)));
1229
+ return bytesToHex4(sha2563(canonicaliseForSigning(capabilities)));
1118
1230
  }
1119
1231
  function buildAttestation(args) {
1120
1232
  return {
@@ -1127,14 +1239,14 @@ function buildAttestation(args) {
1127
1239
  };
1128
1240
  }
1129
1241
  function signAttestation(payload, secretHex) {
1130
- const secretBytes = hexToBytes2(secretHex);
1242
+ const secretBytes = hexToBytes4(secretHex);
1131
1243
  const canonicalBytes = canonicaliseForSigning(payload);
1132
- return bytesToHex2(schnorr2.sign(canonicalBytes, secretBytes));
1244
+ return bytesToHex4(schnorr3.sign(canonicalBytes, secretBytes));
1133
1245
  }
1134
1246
  function verifyAttestation(payload, signatureHex, pubkeyHex) {
1135
1247
  try {
1136
1248
  const canonicalBytes = canonicaliseForSigning(payload);
1137
- return schnorr2.verify(hexToBytes2(signatureHex), canonicalBytes, hexToBytes2(pubkeyHex));
1249
+ return schnorr3.verify(hexToBytes4(signatureHex), canonicalBytes, hexToBytes4(pubkeyHex));
1138
1250
  } catch {
1139
1251
  return false;
1140
1252
  }
@@ -1157,12 +1269,12 @@ function warnAndTightenLooseKeyMode() {
1157
1269
  } catch {
1158
1270
  }
1159
1271
  }
1160
- function bytesToHex2(bytes) {
1272
+ function bytesToHex4(bytes) {
1161
1273
  let s = "";
1162
1274
  for (const b of bytes) s += b.toString(16).padStart(2, "0");
1163
1275
  return s;
1164
1276
  }
1165
- function hexToBytes2(hex) {
1277
+ function hexToBytes4(hex) {
1166
1278
  if (hex.length % 2 !== 0) {
1167
1279
  throw new Error(`hex string has odd length: ${String(hex.length)}`);
1168
1280
  }
@@ -1462,112 +1574,6 @@ function parseTraceparent(header) {
1462
1574
  return { traceId, parentSpanId, flags: parseInt(flagsHex, 16) };
1463
1575
  }
1464
1576
 
1465
- // src/lib/wallet-agent/keypair.ts
1466
- import { secp256k1 as secp256k12 } from "@noble/curves/secp256k1.js";
1467
- var PRIVKEY_HEX_RE = /^[0-9a-f]{64}$/i;
1468
- function derivePubkeyFromPrivkey(privkey) {
1469
- if (!PRIVKEY_HEX_RE.test(privkey)) {
1470
- throw new DvmError(
1471
- "invalid_privkey",
1472
- "Privkey must be 64 hex characters (32 bytes).",
1473
- "Provide a valid secp256k1 private key as a 64-char hex string."
1474
- );
1475
- }
1476
- const bytes = hexToBytes3(privkey);
1477
- if (!secp256k12.utils.isValidSecretKey(bytes)) {
1478
- throw new DvmError(
1479
- "invalid_privkey",
1480
- "Privkey is not a valid secp256k1 scalar (zero or out of curve order).",
1481
- "Generate a fresh key with: dvm wallet init"
1482
- );
1483
- }
1484
- const pub = secp256k12.getPublicKey(bytes);
1485
- return bytesToHex3(pub);
1486
- }
1487
- function bytesToHex3(bytes) {
1488
- return Buffer.from(bytes).toString("hex");
1489
- }
1490
- function hexToBytes3(hex) {
1491
- return Uint8Array.from(Buffer.from(hex, "hex"));
1492
- }
1493
-
1494
- // src/lib/wallet-agent/sign.ts
1495
- import { schnorr as schnorr3 } from "@noble/curves/secp256k1.js";
1496
- var PUBKEY_COMPRESSED_HEX_RE = /^0[23][0-9a-f]{64}$/i;
1497
- var PUBKEY_XONLY_HEX_RE = /^[0-9a-f]{64}$/i;
1498
- var SIG_HEX_RE = /^[0-9a-f]{128}$/i;
1499
- function signChallenge(privkeyHex, challenge) {
1500
- if (!PRIVKEY_HEX_RE.test(privkeyHex)) {
1501
- throw new DvmError(
1502
- "invalid_privkey",
1503
- "Privkey must be 64 hex characters (32 bytes).",
1504
- "Provide a valid secp256k1 private key as a 64-char hex string."
1505
- );
1506
- }
1507
- if (challenge.length !== 32) {
1508
- throw new DvmError(
1509
- "invalid_challenge",
1510
- `Challenge must be exactly 32 bytes; got ${challenge.length}.`,
1511
- "Pass the SHA-256 digest output (32 bytes)."
1512
- );
1513
- }
1514
- const sig = schnorr3.sign(challenge, hexToBytes4(privkeyHex));
1515
- return bytesToHex4(sig);
1516
- }
1517
- function verifyChallenge(pubkeyHex, challenge, sigHex) {
1518
- if (!SIG_HEX_RE.test(sigHex)) return false;
1519
- if (challenge.length !== 32) return false;
1520
- let xonly;
1521
- if (PUBKEY_COMPRESSED_HEX_RE.test(pubkeyHex)) {
1522
- xonly = hexToBytes4(pubkeyHex.slice(2));
1523
- } else if (PUBKEY_XONLY_HEX_RE.test(pubkeyHex)) {
1524
- xonly = hexToBytes4(pubkeyHex);
1525
- } else {
1526
- return false;
1527
- }
1528
- try {
1529
- return schnorr3.verify(hexToBytes4(sigHex), challenge, xonly);
1530
- } catch {
1531
- return false;
1532
- }
1533
- }
1534
- function hexToBytes4(hex) {
1535
- return Uint8Array.from(Buffer.from(hex, "hex"));
1536
- }
1537
- function bytesToHex4(bytes) {
1538
- return Buffer.from(bytes).toString("hex");
1539
- }
1540
-
1541
- // src/lib/cashu/admin-auth.ts
1542
- import { randomBytes } from "crypto";
1543
- import { sha256 as sha2563 } from "@noble/hashes/sha2.js";
1544
- var ADMIN_AUTH_NONCE_TTL_SECONDS = 300;
1545
- var ADMIN_AUTH_CLOCK_SKEW_SECONDS = 30;
1546
- var ADMIN_STATE_UNAVAILABLE = "admin_state_unavailable";
1547
- function buildAdminChallenge(args) {
1548
- const msg = `dvmkit-admin:${args.method}:${args.path}:${args.dvmId}:${args.nonceHex}:${args.unixSeconds}`;
1549
- return sha2563(new TextEncoder().encode(msg));
1550
- }
1551
- function buildAdminAuthHeader(args) {
1552
- const nowMs = args.now ? args.now() : Date.now();
1553
- const unixSeconds = Math.floor(nowMs / 1e3);
1554
- const nonceBytes = args.randomNonce ? args.randomNonce() : Uint8Array.from(randomBytes(16));
1555
- const nonceHex = Buffer.from(nonceBytes).toString("hex");
1556
- const challenge = buildAdminChallenge({
1557
- method: args.method,
1558
- path: args.path,
1559
- dvmId: args.dvmId,
1560
- nonceHex,
1561
- unixSeconds
1562
- });
1563
- const sigHex = signChallenge(args.privkeyHex, challenge);
1564
- return {
1565
- header: `P2PK ${nonceHex}.${unixSeconds}.${sigHex}`,
1566
- nonceHex,
1567
- unixSeconds
1568
- };
1569
- }
1570
-
1571
1577
  // src/lib/monotonic-clock.ts
1572
1578
  function createMonotonicClock() {
1573
1579
  let anchorWallMs = Date.now();
@@ -1620,9 +1626,6 @@ function normaliseMintUrl(url) {
1620
1626
  }
1621
1627
  }
1622
1628
 
1623
- // src/lib/payment-rails/tempo-wire.ts
1624
- var TEMPO_SESSION_UPDATED_REASON = "Tempo session updated; attach the next voucher to fund this credit.";
1625
-
1626
1629
  export {
1627
1630
  toLockPubkey,
1628
1631
  isX402SettlementReconciliationReason,
@@ -1644,6 +1647,7 @@ export {
1644
1647
  WALLET_VERSION,
1645
1648
  RECENT_SPENDS_CAP,
1646
1649
  DEFAULT_AGENT_MINT_URL,
1650
+ loadWalletConfiguredMints,
1647
1651
  loadAgentWallet,
1648
1652
  saveAgentWallet,
1649
1653
  walletExists,
@@ -1662,6 +1666,14 @@ export {
1662
1666
  receiptPubkeyFromSecret,
1663
1667
  deriveReceiptKeypair,
1664
1668
  generateEphemeralReceiptKey,
1669
+ PRIVKEY_HEX_RE,
1670
+ derivePubkeyFromPrivkey,
1671
+ verifyChallenge,
1672
+ ADMIN_AUTH_NONCE_TTL_SECONDS,
1673
+ ADMIN_AUTH_CLOCK_SKEW_SECONDS,
1674
+ ADMIN_STATE_UNAVAILABLE,
1675
+ buildAdminChallenge,
1676
+ buildAdminAuthHeader,
1665
1677
  generateIdentity,
1666
1678
  loadIdentitySecret,
1667
1679
  writeIdentity,
@@ -1685,13 +1697,5 @@ export {
1685
1697
  receiptFailureDetail,
1686
1698
  isPublicRequestId,
1687
1699
  parseTraceparent,
1688
- PRIVKEY_HEX_RE,
1689
- derivePubkeyFromPrivkey,
1690
- verifyChallenge,
1691
- ADMIN_AUTH_NONCE_TTL_SECONDS,
1692
- ADMIN_AUTH_CLOCK_SKEW_SECONDS,
1693
- ADMIN_STATE_UNAVAILABLE,
1694
- buildAdminChallenge,
1695
- buildAdminAuthHeader,
1696
1700
  createMonotonicClock
1697
1701
  };
@@ -4,7 +4,7 @@ import {
4
4
  signedRequestHeaderMatchesDomain,
5
5
  signedRequestStatementHeader,
6
6
  testOnlySignedRequestDomain
7
- } from "./chunk-A4YFWZ7R.js";
7
+ } from "./chunk-RCM3P2HE.js";
8
8
  import {
9
9
  diagnosticConsole
10
10
  } from "./chunk-ETXDTUJO.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  BTC_RATE_FILE,
3
3
  ensureConfigSubdir
4
- } from "./chunk-CZULM42S.js";
4
+ } from "./chunk-CXCQLOOK.js";
5
5
  import {
6
6
  canonicaliseForSigning
7
7
  } from "./chunk-OFVV37XC.js";
@@ -12,6 +12,47 @@ import {
12
12
  // src/lib/credit-implicit.ts
13
13
  var IMPLICIT_CREDIT_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
14
14
 
15
+ // src/lib/payment-amounts.ts
16
+ function msatsToUsd(msats, rate) {
17
+ return msats / 1e11 * rate;
18
+ }
19
+ function usdToMsats(usd, rate) {
20
+ return Math.round(usd / rate * 1e11);
21
+ }
22
+ function msatsToUsdCents(msats, btcPriceUsd) {
23
+ const usd = msatsToUsd(msats, btcPriceUsd);
24
+ return Math.round(usd * 100);
25
+ }
26
+ function msatsToUsdc(msats, btcUsdRate) {
27
+ if (!Number.isSafeInteger(msats) || msats < 0) {
28
+ throw new RangeError("msats must be a non-negative safe integer");
29
+ }
30
+ if (!Number.isFinite(btcUsdRate) || btcUsdRate <= 0) {
31
+ throw new RangeError("BTC/USD rate must be finite and positive");
32
+ }
33
+ const [mantissa, exponent = "0"] = btcUsdRate.toString().split("e");
34
+ const decimalPlaces = mantissa.split(".")[1]?.length ?? 0;
35
+ const shift = Number(exponent) - decimalPlaces;
36
+ let numerator = BigInt(msats) * BigInt(mantissa.replace(".", ""));
37
+ let denominator = 100000n;
38
+ if (shift > 0) numerator *= 10n ** BigInt(shift);
39
+ else if (shift < 0) denominator *= 10n ** BigInt(-shift);
40
+ const micro = (numerator + denominator - 1n) / denominator;
41
+ if (micro > BigInt(Number.MAX_SAFE_INTEGER)) {
42
+ throw new RangeError("USDC micro-unit amount exceeds the safe integer range");
43
+ }
44
+ return Number(micro);
45
+ }
46
+ function usdcToMsats(usdc, btcUsdRate) {
47
+ return usdToMsats(usdc, btcUsdRate);
48
+ }
49
+ function msatsToMicro(msats, btcRate) {
50
+ return Math.round(msatsToUsd(msats, btcRate) * 1e6);
51
+ }
52
+ function microToSats(micro, btcRate) {
53
+ return Math.ceil(usdToMsats(micro / 1e6, btcRate) / 1e3);
54
+ }
55
+
15
56
  // src/lib/fx-coingecko.ts
16
57
  var COINGECKO_SIMPLE_PRICE_URL = "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,usd-coin&vs_currencies=usd";
17
58
  var COINGECKO_BTC_USD_URL = "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd";
@@ -58,39 +99,6 @@ function isPositiveNumber(v) {
58
99
  // src/lib/currency.ts
59
100
  import { existsSync, readFileSync, writeFileSync } from "fs";
60
101
  import { dirname } from "path";
61
- function msatsToUsd(msats, rate) {
62
- return msats / 1e11 * rate;
63
- }
64
- function usdToMsats(usd, rate) {
65
- return Math.round(usd / rate * 1e11);
66
- }
67
- function msatsToUsdCents(msats, btcPriceUsd) {
68
- const usd = msatsToUsd(msats, btcPriceUsd);
69
- return Math.round(usd * 100);
70
- }
71
- function msatsToUsdc(msats, btcUsdRate) {
72
- if (!Number.isSafeInteger(msats) || msats < 0) {
73
- throw new RangeError("msats must be a non-negative safe integer");
74
- }
75
- if (!Number.isFinite(btcUsdRate) || btcUsdRate <= 0) {
76
- throw new RangeError("BTC/USD rate must be finite and positive");
77
- }
78
- const [mantissa, exponent = "0"] = btcUsdRate.toString().split("e");
79
- const decimalPlaces = mantissa.split(".")[1]?.length ?? 0;
80
- const shift = Number(exponent) - decimalPlaces;
81
- let numerator = BigInt(msats) * BigInt(mantissa.replace(".", ""));
82
- let denominator = 100000n;
83
- if (shift > 0) numerator *= 10n ** BigInt(shift);
84
- else if (shift < 0) denominator *= 10n ** BigInt(-shift);
85
- const micro = (numerator + denominator - 1n) / denominator;
86
- if (micro > BigInt(Number.MAX_SAFE_INTEGER)) {
87
- throw new RangeError("USDC micro-unit amount exceeds the safe integer range");
88
- }
89
- return Number(micro);
90
- }
91
- function usdcToMsats(usdc, btcUsdRate) {
92
- return usdToMsats(usdc, btcUsdRate);
93
- }
94
102
  function formatSats(msats) {
95
103
  if (msats === 0) return "0 sats";
96
104
  if (msats < 1e3) return `${msats.toLocaleString("en-US")} msats`;
@@ -313,14 +321,16 @@ function verifyDrainReceipt(receipt) {
313
321
 
314
322
  export {
315
323
  IMPLICIT_CREDIT_TTL_MS,
316
- COINGECKO_SIMPLE_PRICE_URL,
317
- fetchCoinGeckoSpot,
318
- fetchCoinGeckoBtcUsd,
319
324
  msatsToUsd,
320
325
  usdToMsats,
321
326
  msatsToUsdCents,
322
327
  msatsToUsdc,
323
328
  usdcToMsats,
329
+ msatsToMicro,
330
+ microToSats,
331
+ COINGECKO_SIMPLE_PRICE_URL,
332
+ fetchCoinGeckoSpot,
333
+ fetchCoinGeckoBtcUsd,
324
334
  formatSats,
325
335
  formatAmount,
326
336
  formatPaymentAmount,
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  IMPLICIT_CREDIT_TTL_MS,
3
3
  isDrainReceipt
4
- } from "./chunk-PAIM66YA.js";
4
+ } from "./chunk-NFMOPYBA.js";
5
5
  import {
6
6
  CREDITS_FILE,
7
7
  ensureConfigDir
8
- } from "./chunk-CZULM42S.js";
8
+ } from "./chunk-CXCQLOOK.js";
9
9
  import {
10
10
  DvmError
11
- } from "./chunk-LIRAHP7B.js";
11
+ } from "./chunk-BPYMEMQX.js";
12
12
 
13
13
  // src/lib/payment-rails/types.ts
14
14
  var RAIL_FLAGS = ["cashu", "tempo", "x402"];
@@ -1449,33 +1449,58 @@ function isObjectRecord(value) {
1449
1449
  return value !== null && typeof value === "object" && !Array.isArray(value);
1450
1450
  }
1451
1451
  function mergeDuplicateCredits(credits) {
1452
- const merged = [];
1452
+ const groups = [];
1453
1453
  const byCredit = /* @__PURE__ */ new Map();
1454
1454
  let changed = false;
1455
1455
  for (const record of credits) {
1456
1456
  if (typeof record.creditId !== "string" || typeof record.callerPubkey !== "string") {
1457
- merged.push(record);
1457
+ groups.push([record]);
1458
1458
  continue;
1459
1459
  }
1460
1460
  const key = `${record.callerPubkey}\0${record.creditId}`;
1461
- const index = byCredit.get(key);
1462
- if (index === void 0) {
1463
- byCredit.set(key, merged.length);
1464
- merged.push(record);
1465
- continue;
1461
+ let group = byCredit.get(key);
1462
+ if (group === void 0) {
1463
+ group = [];
1464
+ byCredit.set(key, group);
1465
+ groups.push(group);
1466
1466
  }
1467
- merged[index] = mergeCreditRecords(merged[index], record);
1468
- changed = true;
1467
+ let combined = record;
1468
+ let index = group.findIndex((candidate) => compatibleCreditLocations(candidate, combined));
1469
+ while (index !== -1) {
1470
+ combined = mergeCreditRecords(group[index], combined);
1471
+ group.splice(index, 1);
1472
+ changed = true;
1473
+ index = group.findIndex((candidate) => compatibleCreditLocations(candidate, combined));
1474
+ }
1475
+ group.push(combined);
1469
1476
  }
1470
- return { credits: merged, changed };
1477
+ return { credits: groups.flat(), changed };
1478
+ }
1479
+ function compatibleCreditLocations(left, right) {
1480
+ const leftIdentity = dvmLocationIdentity(left);
1481
+ const rightIdentity = dvmLocationIdentity(right);
1482
+ if (leftIdentity && rightIdentity) {
1483
+ return matchesDvmIdentity(left, rightIdentity);
1484
+ }
1485
+ if (leftIdentity) {
1486
+ return dvmLocationOrigins(left).some((origin) => matchesDvmOrigin(right, origin, leftIdentity));
1487
+ }
1488
+ if (rightIdentity) {
1489
+ return dvmLocationOrigins(right).some((origin) => matchesDvmOrigin(left, origin, rightIdentity));
1490
+ }
1491
+ return dvmLocationOrigins(left).some((origin) => matchesDvmOrigin(right, origin, void 0));
1471
1492
  }
1472
1493
  function mergeCreditRecords(left, right) {
1473
1494
  const leftUpdated = Number.isFinite(left.updatedAt) ? left.updatedAt : 0;
1474
1495
  const rightUpdated = Number.isFinite(right.updatedAt) ? right.updatedAt : 0;
1475
1496
  const [newer, older] = rightUpdated >= leftUpdated ? [right, left] : [left, right];
1476
1497
  const record = { ...newer };
1477
- if (record.dvmId === void 0 && older.dvmId !== void 0) record.dvmId = older.dvmId;
1478
- if (record.attestedBuilderPubkey === void 0 && older.attestedBuilderPubkey !== void 0 && record.dvmId === older.dvmId) {
1498
+ const identity = dvmLocationIdentity(newer) ?? dvmLocationIdentity(older);
1499
+ if (identity) {
1500
+ record.dvmId = identity.dvmId;
1501
+ record.attestedBuilderPubkey = identity.builderPubkey;
1502
+ } else if (record.dvmId === void 0 && record.attestedBuilderPubkey === void 0) {
1503
+ record.dvmId = older.dvmId;
1479
1504
  record.attestedBuilderPubkey = older.attestedBuilderPubkey;
1480
1505
  }
1481
1506
  if (record.rail === void 0 && older.rail !== void 0) record.rail = older.rail;
@@ -15,7 +15,7 @@ import {
15
15
  lotOwedSats,
16
16
  netOwedSats,
17
17
  x402SettlementPending
18
- } from "./chunk-JH3SX3LY.js";
18
+ } from "./chunk-6XF3RVVX.js";
19
19
 
20
20
  // src/sdk/server/memory-credit-ledger.ts
21
21
  import { randomUUID } from "crypto";
@@ -10,39 +10,6 @@ import {
10
10
  projectSpanRow
11
11
  } from "./chunk-ETXDTUJO.js";
12
12
 
13
- // src/sdk/step-cache.ts
14
- var StepCache = class _StepCache {
15
- cache = /* @__PURE__ */ new Map();
16
- has(id) {
17
- return this.cache.has(id);
18
- }
19
- get(id) {
20
- return this.getRecord(id).value;
21
- }
22
- getRecord(id) {
23
- const record = this.cache.get(id);
24
- if (record === void 0) throw new Error(`StepCache: no cached result for step "${id}"`);
25
- return record;
26
- }
27
- set(id, value, costs) {
28
- this.cache.set(id, {
29
- id,
30
- value,
31
- ...costs !== void 0 && costs.length > 0 ? { costs: costs.map(({ amount, currency }) => ({ amount, currency })) } : {}
32
- });
33
- }
34
- serialize() {
35
- return [...this.cache.values()];
36
- }
37
- static deserialize(records) {
38
- const cache = new _StepCache();
39
- for (const { id, value, costs } of records) {
40
- cache.set(id, value, costs);
41
- }
42
- return cache;
43
- }
44
- };
45
-
46
13
  // src/sdk/logger.ts
47
14
  function createConsoleLogger(_jobId) {
48
15
  return {
@@ -373,15 +340,15 @@ async function withSpan(opts, fn) {
373
340
 
374
341
  // src/observability/datasets.ts
375
342
  var SPANS_DATASETS = [
376
- { name: "platform", retentionDays: 90 },
377
- { name: "cashu", retentionDays: 90 },
378
- { name: "lightning", retentionDays: 90 },
379
- { name: "stripe", retentionDays: 90 },
380
- { name: "x402", retentionDays: 90 },
381
- { name: "tempo", retentionDays: 90 },
382
- { name: "isolate", retentionDays: 90 },
383
- { name: "audit", retentionDays: 365 },
384
- { name: "cron", retentionDays: 30 }
343
+ { name: "platform", retentionDays: 730 },
344
+ { name: "cashu", retentionDays: 730 },
345
+ { name: "lightning", retentionDays: 730 },
346
+ { name: "stripe", retentionDays: 730 },
347
+ { name: "x402", retentionDays: 730 },
348
+ { name: "tempo", retentionDays: 730 },
349
+ { name: "isolate", retentionDays: 730 },
350
+ { name: "audit", retentionDays: 730 },
351
+ { name: "cron", retentionDays: 730 }
385
352
  ];
386
353
  var DATASET_NAMES = new Set(SPANS_DATASETS.map((d) => d.name));
387
354
 
@@ -485,7 +452,6 @@ function buildNoopLoggers() {
485
452
  }
486
453
 
487
454
  export {
488
- StepCache,
489
455
  createConsoleLogger,
490
456
  createStdoutLogger,
491
457
  createNoopLogger,