@dvmkit/sdk 0.1.0-rc.5 → 0.1.0-rc.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-F2L6KIMD.js → chunk-5URG56JJ.js} +5 -143
- package/dist/{chunk-ZMZTZFRC.js → chunk-7AKBC4PW.js} +754 -150
- package/dist/chunk-ANFX5HEG.js +186 -0
- package/dist/{chunk-ATQIII6K.js → chunk-CMDI3ENK.js} +342 -176
- package/dist/{chunk-AB3L6B52.js → chunk-FJDCFHW5.js} +26 -12
- package/dist/{chunk-AAJNGQMC.js → chunk-JGGI65I3.js} +1 -1
- package/dist/{chunk-4KB3LTOS.js → chunk-KSQEFVFJ.js} +532 -685
- package/dist/{chunk-XYTSDAPH.js → chunk-LDTWX7JW.js} +1 -1
- package/dist/chunk-MKI6OVW4.js +194 -0
- package/dist/{chunk-AZBXSXQT.js → chunk-P4RUVDU7.js} +1 -0
- package/dist/chunk-TSWKITGR.js +772 -0
- package/dist/{credit-menu-ClA1JyYW.d.ts → credit-menu-CYkETVM4.d.ts} +22 -3
- package/dist/{fx-ptKVFOwq.d.ts → fx-Pf4Ey4f_.d.ts} +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/internal/index.d.ts +4835 -24
- package/dist/internal/index.js +3731 -167
- package/dist/{job-store-DxFqDPYq.d.ts → job-store-BtCaLnvJ.d.ts} +23 -1
- package/dist/server/index.d.ts +5 -5
- package/dist/server/index.js +8 -21
- package/dist/tempo-lifecycle-SQL3KLEZ.js +13 -0
- package/dist/tempo-wallet-QOLEIPCV.js +31 -0
- package/dist/testing/index.d.ts +1 -1
- package/dist/{usd-Cha_j80I.d.ts → usd-D7fW2S7I.d.ts} +1 -1
- package/dist/{x402-3VQ64MCS.js → x402-FTG2GRAQ.js} +6 -4
- package/package.json +1 -1
|
@@ -17,24 +17,26 @@ import {
|
|
|
17
17
|
verifyWithFacilitator,
|
|
18
18
|
x402NetworkByCaip2,
|
|
19
19
|
x402NetworkToCaip2
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-JGGI65I3.js";
|
|
21
21
|
import {
|
|
22
|
-
AGENT_MNEMONIC_FILE,
|
|
23
|
-
BUILDER_CONFIG_DIR,
|
|
24
|
-
BUILDER_FILE,
|
|
25
|
-
BUILDER_KEY_FILE,
|
|
26
|
-
DvmError,
|
|
27
22
|
IMPLICIT_CREDIT_TTL_MS,
|
|
28
|
-
WALLET_FILE,
|
|
29
23
|
costAnchor,
|
|
30
|
-
ensureConfigDir,
|
|
31
24
|
formatStaleness,
|
|
32
25
|
formatUsd,
|
|
33
26
|
msatsToUsd,
|
|
34
27
|
msatsToUsdc,
|
|
35
|
-
resolveRpcOverride,
|
|
36
28
|
usdcToMsats
|
|
37
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-5URG56JJ.js";
|
|
30
|
+
import {
|
|
31
|
+
AGENT_MNEMONIC_FILE,
|
|
32
|
+
BUILDER_CONFIG_DIR,
|
|
33
|
+
BUILDER_FILE,
|
|
34
|
+
BUILDER_KEY_FILE,
|
|
35
|
+
DvmError,
|
|
36
|
+
WALLET_FILE,
|
|
37
|
+
ensureConfigDir,
|
|
38
|
+
resolveRpcOverride
|
|
39
|
+
} from "./chunk-MKI6OVW4.js";
|
|
38
40
|
import {
|
|
39
41
|
MemoryCreditLedger
|
|
40
42
|
} from "./chunk-EXHBXA4U.js";
|
|
@@ -50,7 +52,7 @@ import {
|
|
|
50
52
|
isZodSchema,
|
|
51
53
|
signedRequestInput,
|
|
52
54
|
zodIssuesOf
|
|
53
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-P4RUVDU7.js";
|
|
54
56
|
import {
|
|
55
57
|
JobCancelledError,
|
|
56
58
|
MemoryJobStore,
|
|
@@ -1246,8 +1248,115 @@ function backupSecretFile(file, nextContent) {
|
|
|
1246
1248
|
throw new Error(`Could not create a backup of ${file}: 100 candidate paths already exist.`);
|
|
1247
1249
|
}
|
|
1248
1250
|
|
|
1251
|
+
// src/lib/wallet-agent/agent-mnemonic-file.ts
|
|
1252
|
+
import {
|
|
1253
|
+
chmodSync,
|
|
1254
|
+
existsSync as existsSync2,
|
|
1255
|
+
readFileSync as readFileSync2,
|
|
1256
|
+
renameSync,
|
|
1257
|
+
unlinkSync,
|
|
1258
|
+
writeFileSync as writeFileSync2
|
|
1259
|
+
} from "fs";
|
|
1260
|
+
function readAgentMnemonic() {
|
|
1261
|
+
if (!existsSync2(AGENT_MNEMONIC_FILE)) return null;
|
|
1262
|
+
const raw = readFileSync2(AGENT_MNEMONIC_FILE, "utf-8");
|
|
1263
|
+
return raw.trim().toLowerCase().split(/\s+/).join(" ");
|
|
1264
|
+
}
|
|
1265
|
+
function assertAgentMnemonicReplaceable(opts = {}) {
|
|
1266
|
+
if (existsSync2(AGENT_MNEMONIC_FILE) && !opts.force) {
|
|
1267
|
+
throw new DvmError(
|
|
1268
|
+
"agent_mnemonic_exists",
|
|
1269
|
+
`An agent wallet recovery mnemonic already exists at ${AGENT_MNEMONIC_FILE}.`,
|
|
1270
|
+
"Re-run with --force to replace it. WARNING: funds locked to keys derived from the current words become unrecoverable without a backup. The replaced mnemonic is copied to a timestamped .bak sibling."
|
|
1271
|
+
);
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
function writeAgentMnemonic(mnemonic, opts = {}) {
|
|
1275
|
+
const normalised = mnemonic.trim().toLowerCase().split(/\s+/).join(" ");
|
|
1276
|
+
assertAgentMnemonicReplaceable(opts);
|
|
1277
|
+
ensureConfigDir();
|
|
1278
|
+
const backupFile = backupSecretFile(AGENT_MNEMONIC_FILE, normalised);
|
|
1279
|
+
const tmp = `${AGENT_MNEMONIC_FILE}.tmp`;
|
|
1280
|
+
writeFileSync2(tmp, normalised + "\n", { mode: 384 });
|
|
1281
|
+
try {
|
|
1282
|
+
renameSync(tmp, AGENT_MNEMONIC_FILE);
|
|
1283
|
+
} catch (err) {
|
|
1284
|
+
try {
|
|
1285
|
+
unlinkSync(tmp);
|
|
1286
|
+
} catch {
|
|
1287
|
+
}
|
|
1288
|
+
throw err;
|
|
1289
|
+
}
|
|
1290
|
+
try {
|
|
1291
|
+
chmodSync(AGENT_MNEMONIC_FILE, 384);
|
|
1292
|
+
} catch {
|
|
1293
|
+
}
|
|
1294
|
+
return { backupFile };
|
|
1295
|
+
}
|
|
1296
|
+
function deleteAgentMnemonic() {
|
|
1297
|
+
if (!existsSync2(AGENT_MNEMONIC_FILE)) return { backupFile: null };
|
|
1298
|
+
const backupFile = backupSecretFile(AGENT_MNEMONIC_FILE);
|
|
1299
|
+
unlinkSync(AGENT_MNEMONIC_FILE);
|
|
1300
|
+
return { backupFile };
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
// src/lib/wallet-agent/mnemonic.ts
|
|
1304
|
+
import { createHash as createHash3 } from "crypto";
|
|
1305
|
+
import { HDKey } from "@scure/bip32";
|
|
1306
|
+
import {
|
|
1307
|
+
generateMnemonic,
|
|
1308
|
+
mnemonicToEntropy,
|
|
1309
|
+
mnemonicToSeedSync,
|
|
1310
|
+
validateMnemonic
|
|
1311
|
+
} from "@scure/bip39";
|
|
1312
|
+
import { wordlist } from "@scure/bip39/wordlists/english.js";
|
|
1313
|
+
var MNEMONIC_PURPOSE_INDEX = 1789;
|
|
1314
|
+
var AGENT_MASTER_ROLE_INDEX = 1;
|
|
1315
|
+
var MNEMONIC_STRENGTH_BITS = 128;
|
|
1316
|
+
function agentMasterKeypairPath() {
|
|
1317
|
+
return `m/${MNEMONIC_PURPOSE_INDEX}'/${AGENT_MASTER_ROLE_INDEX}'`;
|
|
1318
|
+
}
|
|
1319
|
+
function generateAgentMnemonic() {
|
|
1320
|
+
return generateMnemonic(wordlist, MNEMONIC_STRENGTH_BITS);
|
|
1321
|
+
}
|
|
1322
|
+
function deriveAgentMasterKeypair(mnemonic) {
|
|
1323
|
+
const seed = mnemonicToSeedSync(mnemonic);
|
|
1324
|
+
const hd = HDKey.fromMasterSeed(seed);
|
|
1325
|
+
const child = hd.derive(agentMasterKeypairPath());
|
|
1326
|
+
if (!child.privateKey || !child.publicKey) {
|
|
1327
|
+
throw new Error("Failed to derive agent master keypair from mnemonic");
|
|
1328
|
+
}
|
|
1329
|
+
return {
|
|
1330
|
+
privkey: Buffer.from(child.privateKey).toString("hex"),
|
|
1331
|
+
pubkey: Buffer.from(child.publicKey).toString("hex")
|
|
1332
|
+
};
|
|
1333
|
+
}
|
|
1334
|
+
function validateAgentMnemonic(mnemonic) {
|
|
1335
|
+
const words = mnemonic.trim().toLowerCase().split(/\s+/);
|
|
1336
|
+
if (words.length !== 12) {
|
|
1337
|
+
return { ok: false, reason: "wrong_length", wordCount: words.length };
|
|
1338
|
+
}
|
|
1339
|
+
for (let i = 0; i < words.length; i++) {
|
|
1340
|
+
if (!wordlist.includes(words[i])) {
|
|
1341
|
+
return { ok: false, reason: "unknown_word", badWordIndex: i + 1, badWord: words[i] };
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
if (!validateMnemonic(words.join(" "), wordlist)) {
|
|
1345
|
+
return { ok: false, reason: "checksum" };
|
|
1346
|
+
}
|
|
1347
|
+
return { ok: true };
|
|
1348
|
+
}
|
|
1349
|
+
function looksLikeMnemonic(input) {
|
|
1350
|
+
return /\s/.test(input.trim());
|
|
1351
|
+
}
|
|
1352
|
+
function mnemonicFingerprint(mnemonic) {
|
|
1353
|
+
const normalised = mnemonic.trim().toLowerCase().split(/\s+/).join(" ");
|
|
1354
|
+
const entropy = mnemonicToEntropy(normalised, wordlist);
|
|
1355
|
+
return createHash3("sha256").update(entropy).digest("hex");
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1249
1358
|
// src/lib/wallet-agent/store.ts
|
|
1250
|
-
import { existsSync as
|
|
1359
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
1251
1360
|
import lockfile from "proper-lockfile";
|
|
1252
1361
|
var LOCK_FILE = `${WALLET_FILE}.lock`;
|
|
1253
1362
|
var DEFAULT_LOCK_STALE_MS = 3e4;
|
|
@@ -1255,9 +1364,10 @@ var DEFAULT_LOCK_RETRIES = 30;
|
|
|
1255
1364
|
var DEFAULT_LOCK_RETRY_INTERVAL_MS = 1e3;
|
|
1256
1365
|
var WALLET_VERSION = 8;
|
|
1257
1366
|
var RECENT_SPENDS_CAP = 10;
|
|
1367
|
+
var DEFAULT_AGENT_MINT_URL = "https://mint.coinos.io";
|
|
1258
1368
|
function loadAgentWallet() {
|
|
1259
|
-
if (!
|
|
1260
|
-
const raw =
|
|
1369
|
+
if (!existsSync3(WALLET_FILE)) return null;
|
|
1370
|
+
const raw = readFileSync3(WALLET_FILE, "utf-8");
|
|
1261
1371
|
let parsed;
|
|
1262
1372
|
try {
|
|
1263
1373
|
parsed = JSON.parse(raw);
|
|
@@ -1322,17 +1432,20 @@ function saveAgentWallet(wallet) {
|
|
|
1322
1432
|
ensureConfigDir();
|
|
1323
1433
|
const tmp = `${WALLET_FILE}.tmp`;
|
|
1324
1434
|
const data = JSON.stringify(wallet, null, 2) + "\n";
|
|
1325
|
-
|
|
1435
|
+
writeFileSync3(tmp, data, { mode: 384 });
|
|
1326
1436
|
try {
|
|
1327
|
-
|
|
1437
|
+
renameSync2(tmp, WALLET_FILE);
|
|
1328
1438
|
} catch (err) {
|
|
1329
1439
|
try {
|
|
1330
|
-
|
|
1440
|
+
unlinkSync2(tmp);
|
|
1331
1441
|
} catch {
|
|
1332
1442
|
}
|
|
1333
1443
|
throw err;
|
|
1334
1444
|
}
|
|
1335
1445
|
}
|
|
1446
|
+
function walletExists() {
|
|
1447
|
+
return existsSync3(WALLET_FILE);
|
|
1448
|
+
}
|
|
1336
1449
|
async function withWalletLock(fn, opts = {}) {
|
|
1337
1450
|
ensureConfigDir();
|
|
1338
1451
|
const stale = opts.stale ?? DEFAULT_LOCK_STALE_MS;
|
|
@@ -1363,6 +1476,12 @@ async function withWalletLock(fn, opts = {}) {
|
|
|
1363
1476
|
}
|
|
1364
1477
|
}
|
|
1365
1478
|
}
|
|
1479
|
+
function effectiveMints(wallet) {
|
|
1480
|
+
return wallet.mints.length > 0 ? wallet.mints : [DEFAULT_AGENT_MINT_URL];
|
|
1481
|
+
}
|
|
1482
|
+
function effectiveFundingMint(mints) {
|
|
1483
|
+
return mints.length > 0 ? { url: mints[0], source: "configured" } : { url: DEFAULT_AGENT_MINT_URL, source: "fallback" };
|
|
1484
|
+
}
|
|
1366
1485
|
function migrateV1ToV4(parsed) {
|
|
1367
1486
|
const proofs = Array.isArray(parsed.proofs) ? parsed.proofs : [];
|
|
1368
1487
|
const pending = Array.isArray(parsed.pending_mints) ? parsed.pending_mints : [];
|
|
@@ -1473,6 +1592,98 @@ function isCounterMap(value) {
|
|
|
1473
1592
|
return true;
|
|
1474
1593
|
}
|
|
1475
1594
|
|
|
1595
|
+
// src/lib/wallet-agent/nut13-counters.ts
|
|
1596
|
+
var PersistentCounterSource = class {
|
|
1597
|
+
constructor(mintUrl) {
|
|
1598
|
+
this.mintUrl = mintUrl;
|
|
1599
|
+
}
|
|
1600
|
+
mintUrl;
|
|
1601
|
+
/**
|
|
1602
|
+
* Reserve `n` counters for `keysetId`. Returns `{ start, count: n }`
|
|
1603
|
+
* and bumps the persisted `next` to `start + n` under the wallet lock.
|
|
1604
|
+
*
|
|
1605
|
+
* Per cashu-ts contract, `n === 0` MUST NOT mutate state and is a
|
|
1606
|
+
* pure peek — we honour that by returning `{ start, count: 0 }`
|
|
1607
|
+
* without acquiring the lock.
|
|
1608
|
+
*/
|
|
1609
|
+
async reserve(keysetId, n) {
|
|
1610
|
+
if (n === 0) {
|
|
1611
|
+
const wallet = loadAgentWallet();
|
|
1612
|
+
const current = wallet?.nut13_counters[this.mintUrl]?.[keysetId] ?? 0;
|
|
1613
|
+
return { start: current, count: 0 };
|
|
1614
|
+
}
|
|
1615
|
+
return withWalletLock(() => {
|
|
1616
|
+
const wallet = this.loadOrThrow();
|
|
1617
|
+
const current = wallet.nut13_counters[this.mintUrl]?.[keysetId] ?? 0;
|
|
1618
|
+
saveAgentWallet(this.withCounter(wallet, keysetId, current + n));
|
|
1619
|
+
return { start: current, count: n };
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
/**
|
|
1623
|
+
* Monotonic bump: ensure the persisted `next` for `keysetId` is at
|
|
1624
|
+
* least `minNext`. No-op if the wallet is already ahead. Used by
|
|
1625
|
+
* cashu-ts when the mint reports `outputs_already_signed` — the
|
|
1626
|
+
* losing wallet rolls forward past the colliding counter.
|
|
1627
|
+
*/
|
|
1628
|
+
async advanceToAtLeast(keysetId, minNext) {
|
|
1629
|
+
await withWalletLock(() => {
|
|
1630
|
+
const wallet = this.loadOrThrow();
|
|
1631
|
+
const current = wallet.nut13_counters[this.mintUrl]?.[keysetId] ?? 0;
|
|
1632
|
+
if (current >= minNext) return;
|
|
1633
|
+
saveAgentWallet(this.withCounter(wallet, keysetId, minNext));
|
|
1634
|
+
});
|
|
1635
|
+
}
|
|
1636
|
+
/**
|
|
1637
|
+
* Snapshot the per-keyset `next` map for this mint. Useful for
|
|
1638
|
+
* `dvm wallet show` and test assertions; cashu-ts itself does not
|
|
1639
|
+
* depend on `snapshot` being implemented.
|
|
1640
|
+
*/
|
|
1641
|
+
snapshot() {
|
|
1642
|
+
const wallet = loadAgentWallet();
|
|
1643
|
+
const counters = wallet?.nut13_counters[this.mintUrl] ?? {};
|
|
1644
|
+
const out = {};
|
|
1645
|
+
for (const [keyset, next] of Object.entries(counters)) {
|
|
1646
|
+
if (typeof next === "number") out[keyset] = next;
|
|
1647
|
+
}
|
|
1648
|
+
return Promise.resolve(out);
|
|
1649
|
+
}
|
|
1650
|
+
/**
|
|
1651
|
+
* Hard-set the persisted `next` for `keysetId`. Used by
|
|
1652
|
+
* `dvm wallet restore` to re-anchor counters after walking
|
|
1653
|
+
* `batchRestore` per keyset. cashu-ts also exposes this on
|
|
1654
|
+
* `wallet.counters.setNext`.
|
|
1655
|
+
*/
|
|
1656
|
+
async setNext(keysetId, next) {
|
|
1657
|
+
await withWalletLock(() => {
|
|
1658
|
+
const wallet = this.loadOrThrow();
|
|
1659
|
+
saveAgentWallet(this.withCounter(wallet, keysetId, next));
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
loadOrThrow() {
|
|
1663
|
+
const wallet = loadAgentWallet();
|
|
1664
|
+
if (!wallet) {
|
|
1665
|
+
throw new DvmError(
|
|
1666
|
+
"wallet_missing",
|
|
1667
|
+
"NUT-13 counter persistence requires an agent wallet at ~/.dvm/wallet.json.",
|
|
1668
|
+
"Run `dvm wallet init` or `dvm wallet recover --file <path>` (or pipe the mnemonic via stdin) first."
|
|
1669
|
+
);
|
|
1670
|
+
}
|
|
1671
|
+
return wallet;
|
|
1672
|
+
}
|
|
1673
|
+
withCounter(wallet, keysetId, next) {
|
|
1674
|
+
return {
|
|
1675
|
+
...wallet,
|
|
1676
|
+
nut13_counters: {
|
|
1677
|
+
...wallet.nut13_counters,
|
|
1678
|
+
[this.mintUrl]: {
|
|
1679
|
+
...wallet.nut13_counters[this.mintUrl] ?? {},
|
|
1680
|
+
[keysetId]: next
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
};
|
|
1684
|
+
}
|
|
1685
|
+
};
|
|
1686
|
+
|
|
1476
1687
|
// src/lib/cashu/health.ts
|
|
1477
1688
|
var DEFAULT_REQUIRED_NUTS = [4, 5, 7, 9, 10, 11, 20];
|
|
1478
1689
|
async function checkMintHealth(mintUrl, opts) {
|
|
@@ -1592,6 +1803,12 @@ function mintAmountBounds(nuts) {
|
|
|
1592
1803
|
function meltAmountBounds(nuts) {
|
|
1593
1804
|
return boundsOf(nuts["5"]);
|
|
1594
1805
|
}
|
|
1806
|
+
function amountBoundsVerdict(sats, bounds) {
|
|
1807
|
+
if (!bounds) return "ok";
|
|
1808
|
+
if (bounds.minSats !== null && sats < bounds.minSats) return "below_min";
|
|
1809
|
+
if (bounds.maxSats !== null && sats > bounds.maxSats) return "above_max";
|
|
1810
|
+
return "ok";
|
|
1811
|
+
}
|
|
1595
1812
|
function boundsOf(node) {
|
|
1596
1813
|
const method = findBolt11Sat(node);
|
|
1597
1814
|
return {
|
|
@@ -1680,131 +1897,6 @@ function sleep(ms) {
|
|
|
1680
1897
|
// src/lib/cashu/wallet.ts
|
|
1681
1898
|
import { Wallet } from "@cashu/cashu-ts";
|
|
1682
1899
|
import { mnemonicToSeedSync as mnemonicToSeedSync2 } from "@scure/bip39";
|
|
1683
|
-
|
|
1684
|
-
// src/lib/wallet-agent/agent-mnemonic-file.ts
|
|
1685
|
-
import {
|
|
1686
|
-
chmodSync,
|
|
1687
|
-
existsSync as existsSync3,
|
|
1688
|
-
readFileSync as readFileSync3,
|
|
1689
|
-
renameSync as renameSync2,
|
|
1690
|
-
unlinkSync as unlinkSync2,
|
|
1691
|
-
writeFileSync as writeFileSync3
|
|
1692
|
-
} from "fs";
|
|
1693
|
-
function readAgentMnemonic() {
|
|
1694
|
-
if (!existsSync3(AGENT_MNEMONIC_FILE)) return null;
|
|
1695
|
-
const raw = readFileSync3(AGENT_MNEMONIC_FILE, "utf-8");
|
|
1696
|
-
return raw.trim().toLowerCase().split(/\s+/).join(" ");
|
|
1697
|
-
}
|
|
1698
|
-
|
|
1699
|
-
// src/lib/wallet-agent/mnemonic.ts
|
|
1700
|
-
import { createHash as createHash3 } from "crypto";
|
|
1701
|
-
import { HDKey } from "@scure/bip32";
|
|
1702
|
-
import {
|
|
1703
|
-
generateMnemonic,
|
|
1704
|
-
mnemonicToEntropy,
|
|
1705
|
-
mnemonicToSeedSync,
|
|
1706
|
-
validateMnemonic
|
|
1707
|
-
} from "@scure/bip39";
|
|
1708
|
-
import { wordlist } from "@scure/bip39/wordlists/english.js";
|
|
1709
|
-
function mnemonicFingerprint(mnemonic) {
|
|
1710
|
-
const normalised = mnemonic.trim().toLowerCase().split(/\s+/).join(" ");
|
|
1711
|
-
const entropy = mnemonicToEntropy(normalised, wordlist);
|
|
1712
|
-
return createHash3("sha256").update(entropy).digest("hex");
|
|
1713
|
-
}
|
|
1714
|
-
|
|
1715
|
-
// src/lib/wallet-agent/nut13-counters.ts
|
|
1716
|
-
var PersistentCounterSource = class {
|
|
1717
|
-
constructor(mintUrl) {
|
|
1718
|
-
this.mintUrl = mintUrl;
|
|
1719
|
-
}
|
|
1720
|
-
mintUrl;
|
|
1721
|
-
/**
|
|
1722
|
-
* Reserve `n` counters for `keysetId`. Returns `{ start, count: n }`
|
|
1723
|
-
* and bumps the persisted `next` to `start + n` under the wallet lock.
|
|
1724
|
-
*
|
|
1725
|
-
* Per cashu-ts contract, `n === 0` MUST NOT mutate state and is a
|
|
1726
|
-
* pure peek — we honour that by returning `{ start, count: 0 }`
|
|
1727
|
-
* without acquiring the lock.
|
|
1728
|
-
*/
|
|
1729
|
-
async reserve(keysetId, n) {
|
|
1730
|
-
if (n === 0) {
|
|
1731
|
-
const wallet = loadAgentWallet();
|
|
1732
|
-
const current = wallet?.nut13_counters[this.mintUrl]?.[keysetId] ?? 0;
|
|
1733
|
-
return { start: current, count: 0 };
|
|
1734
|
-
}
|
|
1735
|
-
return withWalletLock(() => {
|
|
1736
|
-
const wallet = this.loadOrThrow();
|
|
1737
|
-
const current = wallet.nut13_counters[this.mintUrl]?.[keysetId] ?? 0;
|
|
1738
|
-
saveAgentWallet(this.withCounter(wallet, keysetId, current + n));
|
|
1739
|
-
return { start: current, count: n };
|
|
1740
|
-
});
|
|
1741
|
-
}
|
|
1742
|
-
/**
|
|
1743
|
-
* Monotonic bump: ensure the persisted `next` for `keysetId` is at
|
|
1744
|
-
* least `minNext`. No-op if the wallet is already ahead. Used by
|
|
1745
|
-
* cashu-ts when the mint reports `outputs_already_signed` — the
|
|
1746
|
-
* losing wallet rolls forward past the colliding counter.
|
|
1747
|
-
*/
|
|
1748
|
-
async advanceToAtLeast(keysetId, minNext) {
|
|
1749
|
-
await withWalletLock(() => {
|
|
1750
|
-
const wallet = this.loadOrThrow();
|
|
1751
|
-
const current = wallet.nut13_counters[this.mintUrl]?.[keysetId] ?? 0;
|
|
1752
|
-
if (current >= minNext) return;
|
|
1753
|
-
saveAgentWallet(this.withCounter(wallet, keysetId, minNext));
|
|
1754
|
-
});
|
|
1755
|
-
}
|
|
1756
|
-
/**
|
|
1757
|
-
* Snapshot the per-keyset `next` map for this mint. Useful for
|
|
1758
|
-
* `dvm wallet show` and test assertions; cashu-ts itself does not
|
|
1759
|
-
* depend on `snapshot` being implemented.
|
|
1760
|
-
*/
|
|
1761
|
-
snapshot() {
|
|
1762
|
-
const wallet = loadAgentWallet();
|
|
1763
|
-
const counters = wallet?.nut13_counters[this.mintUrl] ?? {};
|
|
1764
|
-
const out = {};
|
|
1765
|
-
for (const [keyset, next] of Object.entries(counters)) {
|
|
1766
|
-
if (typeof next === "number") out[keyset] = next;
|
|
1767
|
-
}
|
|
1768
|
-
return Promise.resolve(out);
|
|
1769
|
-
}
|
|
1770
|
-
/**
|
|
1771
|
-
* Hard-set the persisted `next` for `keysetId`. Used by
|
|
1772
|
-
* `dvm wallet restore` to re-anchor counters after walking
|
|
1773
|
-
* `batchRestore` per keyset. cashu-ts also exposes this on
|
|
1774
|
-
* `wallet.counters.setNext`.
|
|
1775
|
-
*/
|
|
1776
|
-
async setNext(keysetId, next) {
|
|
1777
|
-
await withWalletLock(() => {
|
|
1778
|
-
const wallet = this.loadOrThrow();
|
|
1779
|
-
saveAgentWallet(this.withCounter(wallet, keysetId, next));
|
|
1780
|
-
});
|
|
1781
|
-
}
|
|
1782
|
-
loadOrThrow() {
|
|
1783
|
-
const wallet = loadAgentWallet();
|
|
1784
|
-
if (!wallet) {
|
|
1785
|
-
throw new DvmError(
|
|
1786
|
-
"wallet_missing",
|
|
1787
|
-
"NUT-13 counter persistence requires an agent wallet at ~/.dvm/wallet.json.",
|
|
1788
|
-
"Run `dvm wallet init` or `dvm wallet recover --file <path>` (or pipe the mnemonic via stdin) first."
|
|
1789
|
-
);
|
|
1790
|
-
}
|
|
1791
|
-
return wallet;
|
|
1792
|
-
}
|
|
1793
|
-
withCounter(wallet, keysetId, next) {
|
|
1794
|
-
return {
|
|
1795
|
-
...wallet,
|
|
1796
|
-
nut13_counters: {
|
|
1797
|
-
...wallet.nut13_counters,
|
|
1798
|
-
[this.mintUrl]: {
|
|
1799
|
-
...wallet.nut13_counters[this.mintUrl] ?? {},
|
|
1800
|
-
[keysetId]: next
|
|
1801
|
-
}
|
|
1802
|
-
}
|
|
1803
|
-
};
|
|
1804
|
-
}
|
|
1805
|
-
};
|
|
1806
|
-
|
|
1807
|
-
// src/lib/cashu/wallet.ts
|
|
1808
1900
|
var walletCache = /* @__PURE__ */ new Map();
|
|
1809
1901
|
var keysetResidueRegistry = /* @__PURE__ */ new Map();
|
|
1810
1902
|
var NUT13_MODULUS = BigInt(2 ** 31 - 1);
|
|
@@ -4365,7 +4457,7 @@ async function verifyUpfrontPayment(opts) {
|
|
|
4365
4457
|
x402Requirements
|
|
4366
4458
|
});
|
|
4367
4459
|
}
|
|
4368
|
-
const { verifyX402Payment } = await import("./x402-
|
|
4460
|
+
const { verifyX402Payment } = await import("./x402-FTG2GRAQ.js");
|
|
4369
4461
|
const receipt = await verifyX402Payment(
|
|
4370
4462
|
x402Payment,
|
|
4371
4463
|
x402Config,
|
|
@@ -5285,7 +5377,7 @@ async function verifyIncomingPayment(body, opts, snapshot) {
|
|
|
5285
5377
|
snapshot
|
|
5286
5378
|
});
|
|
5287
5379
|
}
|
|
5288
|
-
const { verifyX402Payment } = await import("./x402-
|
|
5380
|
+
const { verifyX402Payment } = await import("./x402-FTG2GRAQ.js");
|
|
5289
5381
|
const receipt = await verifyX402Payment(
|
|
5290
5382
|
body.content.x402_payment,
|
|
5291
5383
|
opts.x402Config,
|
|
@@ -5695,7 +5787,7 @@ async function processIncomingPayment(job, body, opts) {
|
|
|
5695
5787
|
try {
|
|
5696
5788
|
const requiredMsats = job.pendingPaymentMsats ?? 0;
|
|
5697
5789
|
const requiredUsdcMicro = job.pendingX402AmountUsdcMicro !== void 0 ? BigInt(job.pendingX402AmountUsdcMicro) : BigInt(msatsToUsdc(requiredMsats, rate));
|
|
5698
|
-
const { verifyX402Payment } = await import("./x402-
|
|
5790
|
+
const { verifyX402Payment } = await import("./x402-FTG2GRAQ.js");
|
|
5699
5791
|
const receipt = await verifyX402Payment(
|
|
5700
5792
|
body.content.x402_payment,
|
|
5701
5793
|
opts.x402Config,
|
|
@@ -9098,6 +9190,34 @@ function pickPrimary(credits, currency) {
|
|
|
9098
9190
|
return best;
|
|
9099
9191
|
}
|
|
9100
9192
|
|
|
9193
|
+
// src/lib/mints.ts
|
|
9194
|
+
var RECOMMENDED_MINTS = [
|
|
9195
|
+
{
|
|
9196
|
+
name: "Testnut",
|
|
9197
|
+
description: "Cashu test mint \u2014 use for testing only, not real funds",
|
|
9198
|
+
url: "https://testnut.cashu.space"
|
|
9199
|
+
},
|
|
9200
|
+
{
|
|
9201
|
+
name: "Voltz Mint",
|
|
9202
|
+
description: "Production Cashu mint by the Voltz Wallet team",
|
|
9203
|
+
url: "https://mint.lnvoltz.com"
|
|
9204
|
+
}
|
|
9205
|
+
];
|
|
9206
|
+
function isTestMintUrl(url) {
|
|
9207
|
+
let host;
|
|
9208
|
+
try {
|
|
9209
|
+
host = new URL(url).hostname.toLowerCase();
|
|
9210
|
+
} catch {
|
|
9211
|
+
return false;
|
|
9212
|
+
}
|
|
9213
|
+
if (host.startsWith("[") && host.endsWith("]")) host = host.slice(1, -1);
|
|
9214
|
+
if (LOOPBACK_HOSTS.has(host)) return true;
|
|
9215
|
+
if (host.endsWith(".localhost")) return true;
|
|
9216
|
+
return TEST_MINT_HOSTS.has(host);
|
|
9217
|
+
}
|
|
9218
|
+
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1"]);
|
|
9219
|
+
var TEST_MINT_HOSTS = /* @__PURE__ */ new Set(["testnut.cashu.space"]);
|
|
9220
|
+
|
|
9101
9221
|
// src/lib/receipt-key.ts
|
|
9102
9222
|
import { schnorr, secp256k1 } from "@noble/curves/secp256k1.js";
|
|
9103
9223
|
import { hkdf } from "@noble/hashes/hkdf.js";
|
|
@@ -10076,14 +10196,57 @@ function formatPaid(msats, rate) {
|
|
|
10076
10196
|
return `~$${formatUsd(usd)} (${satsLabel}, ${costAnchor(usd)})`;
|
|
10077
10197
|
}
|
|
10078
10198
|
|
|
10079
|
-
// src/lib/
|
|
10080
|
-
|
|
10199
|
+
// src/lib/request-id.ts
|
|
10200
|
+
var MAX_REQUEST_ID_LENGTH = 128;
|
|
10201
|
+
var REQUEST_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/;
|
|
10202
|
+
function isPublicRequestId(value) {
|
|
10203
|
+
return typeof value === "string" && value.length > 0 && value.length <= MAX_REQUEST_ID_LENGTH && REQUEST_ID_PATTERN.test(value);
|
|
10204
|
+
}
|
|
10205
|
+
|
|
10206
|
+
// src/lib/traceparent.ts
|
|
10207
|
+
var TRACEPARENT_RE = /^00-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/;
|
|
10208
|
+
var ALL_ZERO_TRACE = "00000000000000000000000000000000";
|
|
10209
|
+
var ALL_ZERO_SPAN = "0000000000000000";
|
|
10210
|
+
function parseTraceparent(header) {
|
|
10211
|
+
if (!header) return null;
|
|
10212
|
+
const match = TRACEPARENT_RE.exec(header.trim());
|
|
10213
|
+
if (!match) return null;
|
|
10214
|
+
const [, traceId, parentSpanId, flagsHex] = match;
|
|
10215
|
+
if (traceId === ALL_ZERO_TRACE || parentSpanId === ALL_ZERO_SPAN) return null;
|
|
10216
|
+
return { traceId, parentSpanId, flags: parseInt(flagsHex, 16) };
|
|
10217
|
+
}
|
|
10081
10218
|
|
|
10082
10219
|
// src/lib/wallet-agent/keypair.ts
|
|
10083
10220
|
import { secp256k1 as secp256k12 } from "@noble/curves/secp256k1.js";
|
|
10084
10221
|
var PRIVKEY_HEX_RE = /^[0-9a-f]{64}$/i;
|
|
10222
|
+
function derivePubkeyFromPrivkey(privkey) {
|
|
10223
|
+
if (!PRIVKEY_HEX_RE.test(privkey)) {
|
|
10224
|
+
throw new DvmError(
|
|
10225
|
+
"invalid_privkey",
|
|
10226
|
+
"Privkey must be 64 hex characters (32 bytes).",
|
|
10227
|
+
"Provide a valid secp256k1 private key as a 64-char hex string."
|
|
10228
|
+
);
|
|
10229
|
+
}
|
|
10230
|
+
const bytes = hexToBytes4(privkey);
|
|
10231
|
+
if (!secp256k12.utils.isValidSecretKey(bytes)) {
|
|
10232
|
+
throw new DvmError(
|
|
10233
|
+
"invalid_privkey",
|
|
10234
|
+
"Privkey is not a valid secp256k1 scalar (zero or out of curve order).",
|
|
10235
|
+
"Generate a fresh key with: dvm wallet init"
|
|
10236
|
+
);
|
|
10237
|
+
}
|
|
10238
|
+
const pub = secp256k12.getPublicKey(bytes);
|
|
10239
|
+
return bytesToHex4(pub);
|
|
10240
|
+
}
|
|
10241
|
+
function bytesToHex4(bytes) {
|
|
10242
|
+
return Buffer.from(bytes).toString("hex");
|
|
10243
|
+
}
|
|
10244
|
+
function hexToBytes4(hex) {
|
|
10245
|
+
return Uint8Array.from(Buffer.from(hex, "hex"));
|
|
10246
|
+
}
|
|
10085
10247
|
|
|
10086
10248
|
// src/lib/wallet-agent/sign.ts
|
|
10249
|
+
import { schnorr as schnorr4 } from "@noble/curves/secp256k1.js";
|
|
10087
10250
|
var PUBKEY_COMPRESSED_HEX_RE = /^0[23][0-9a-f]{64}$/i;
|
|
10088
10251
|
var PUBKEY_XONLY_HEX_RE = /^[0-9a-f]{64}$/i;
|
|
10089
10252
|
var SIG_HEX_RE = /^[0-9a-f]{128}$/i;
|
|
@@ -10102,30 +10265,30 @@ function signChallenge(privkeyHex, challenge) {
|
|
|
10102
10265
|
"Pass the SHA-256 digest output (32 bytes)."
|
|
10103
10266
|
);
|
|
10104
10267
|
}
|
|
10105
|
-
const sig = schnorr4.sign(challenge,
|
|
10106
|
-
return
|
|
10268
|
+
const sig = schnorr4.sign(challenge, hexToBytes5(privkeyHex));
|
|
10269
|
+
return bytesToHex5(sig);
|
|
10107
10270
|
}
|
|
10108
10271
|
function verifyChallenge(pubkeyHex, challenge, sigHex) {
|
|
10109
10272
|
if (!SIG_HEX_RE.test(sigHex)) return false;
|
|
10110
10273
|
if (challenge.length !== 32) return false;
|
|
10111
10274
|
let xonly;
|
|
10112
10275
|
if (PUBKEY_COMPRESSED_HEX_RE.test(pubkeyHex)) {
|
|
10113
|
-
xonly =
|
|
10276
|
+
xonly = hexToBytes5(pubkeyHex.slice(2));
|
|
10114
10277
|
} else if (PUBKEY_XONLY_HEX_RE.test(pubkeyHex)) {
|
|
10115
|
-
xonly =
|
|
10278
|
+
xonly = hexToBytes5(pubkeyHex);
|
|
10116
10279
|
} else {
|
|
10117
10280
|
return false;
|
|
10118
10281
|
}
|
|
10119
10282
|
try {
|
|
10120
|
-
return schnorr4.verify(
|
|
10283
|
+
return schnorr4.verify(hexToBytes5(sigHex), challenge, xonly);
|
|
10121
10284
|
} catch {
|
|
10122
10285
|
return false;
|
|
10123
10286
|
}
|
|
10124
10287
|
}
|
|
10125
|
-
function
|
|
10288
|
+
function hexToBytes5(hex) {
|
|
10126
10289
|
return Uint8Array.from(Buffer.from(hex, "hex"));
|
|
10127
10290
|
}
|
|
10128
|
-
function
|
|
10291
|
+
function bytesToHex5(bytes) {
|
|
10129
10292
|
return Buffer.from(bytes).toString("hex");
|
|
10130
10293
|
}
|
|
10131
10294
|
|
|
@@ -10867,13 +11030,6 @@ function createMonotonicClock() {
|
|
|
10867
11030
|
};
|
|
10868
11031
|
}
|
|
10869
11032
|
|
|
10870
|
-
// src/lib/request-id.ts
|
|
10871
|
-
var MAX_REQUEST_ID_LENGTH = 128;
|
|
10872
|
-
var REQUEST_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/;
|
|
10873
|
-
function isPublicRequestId(value) {
|
|
10874
|
-
return typeof value === "string" && value.length > 0 && value.length <= MAX_REQUEST_ID_LENGTH && REQUEST_ID_PATTERN.test(value);
|
|
10875
|
-
}
|
|
10876
|
-
|
|
10877
11033
|
// src/sdk/server/request-envelope.ts
|
|
10878
11034
|
var REQUEST_ID_FIELD = "request_id";
|
|
10879
11035
|
var AUTH_STATEMENT_FIELD = "auth_statement";
|
|
@@ -15068,19 +15224,6 @@ function shortSha(sha) {
|
|
|
15068
15224
|
return base2.slice(0, 7) + (dirty ? DIRTY_SUFFIX : "");
|
|
15069
15225
|
}
|
|
15070
15226
|
|
|
15071
|
-
// src/lib/traceparent.ts
|
|
15072
|
-
var TRACEPARENT_RE = /^00-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/;
|
|
15073
|
-
var ALL_ZERO_TRACE = "00000000000000000000000000000000";
|
|
15074
|
-
var ALL_ZERO_SPAN = "0000000000000000";
|
|
15075
|
-
function parseTraceparent(header) {
|
|
15076
|
-
if (!header) return null;
|
|
15077
|
-
const match = TRACEPARENT_RE.exec(header.trim());
|
|
15078
|
-
if (!match) return null;
|
|
15079
|
-
const [, traceId, parentSpanId, flagsHex] = match;
|
|
15080
|
-
if (traceId === ALL_ZERO_TRACE || parentSpanId === ALL_ZERO_SPAN) return null;
|
|
15081
|
-
return { traceId, parentSpanId, flags: parseInt(flagsHex, 16) };
|
|
15082
|
-
}
|
|
15083
|
-
|
|
15084
15227
|
// src/sdk/server/admin-credit.ts
|
|
15085
15228
|
import {
|
|
15086
15229
|
Amount as Amount3,
|
|
@@ -20978,7 +21121,7 @@ function parseNwcUri(uri) {
|
|
|
20978
21121
|
if (!/^[0-9a-fA-F]{64}$/.test(secretHex)) {
|
|
20979
21122
|
throw new NwcError("invalid_uri", "NWC URI secret must be 64 hex characters");
|
|
20980
21123
|
}
|
|
20981
|
-
const secret =
|
|
21124
|
+
const secret = hexToBytes6(secretHex);
|
|
20982
21125
|
const clientPubkey = getPublicKey(secret);
|
|
20983
21126
|
return { walletPubkey, relays, secret, clientPubkey };
|
|
20984
21127
|
}
|
|
@@ -21358,7 +21501,7 @@ function positiveSecondsEnv(name, fallbackSec) {
|
|
|
21358
21501
|
if (!Number.isFinite(n) || n <= 0) return fallbackSec * 1e3;
|
|
21359
21502
|
return Math.floor(n) * 1e3;
|
|
21360
21503
|
}
|
|
21361
|
-
function
|
|
21504
|
+
function hexToBytes6(hex) {
|
|
21362
21505
|
const bytes = new Uint8Array(hex.length / 2);
|
|
21363
21506
|
for (let i = 0; i < bytes.length; i++) {
|
|
21364
21507
|
bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
@@ -21626,12 +21769,29 @@ export {
|
|
|
21626
21769
|
rateUnavailablePaymentError,
|
|
21627
21770
|
backupSecretFile,
|
|
21628
21771
|
readAgentMnemonic,
|
|
21772
|
+
assertAgentMnemonicReplaceable,
|
|
21773
|
+
writeAgentMnemonic,
|
|
21774
|
+
deleteAgentMnemonic,
|
|
21775
|
+
generateAgentMnemonic,
|
|
21776
|
+
deriveAgentMasterKeypair,
|
|
21777
|
+
validateAgentMnemonic,
|
|
21778
|
+
looksLikeMnemonic,
|
|
21779
|
+
mnemonicFingerprint,
|
|
21780
|
+
WALLET_VERSION,
|
|
21629
21781
|
RECENT_SPENDS_CAP,
|
|
21782
|
+
DEFAULT_AGENT_MINT_URL,
|
|
21630
21783
|
loadAgentWallet,
|
|
21631
21784
|
saveAgentWallet,
|
|
21785
|
+
walletExists,
|
|
21632
21786
|
withWalletLock,
|
|
21787
|
+
effectiveMints,
|
|
21788
|
+
effectiveFundingMint,
|
|
21789
|
+
PersistentCounterSource,
|
|
21633
21790
|
checkMintHealth,
|
|
21634
21791
|
canSwap,
|
|
21792
|
+
meltAmountBounds,
|
|
21793
|
+
amountBoundsVerdict,
|
|
21794
|
+
assertNutSupport,
|
|
21635
21795
|
getWallet,
|
|
21636
21796
|
X402_BATCH_CHANNEL_ABI,
|
|
21637
21797
|
X402_BATCH_SETTLEMENT_NETWORK,
|
|
@@ -21656,6 +21816,8 @@ export {
|
|
|
21656
21816
|
repairX402ExactSettlementEffect,
|
|
21657
21817
|
CREDIT_REQUEST_SCHEMA,
|
|
21658
21818
|
handleCreditRequest,
|
|
21819
|
+
RECOMMENDED_MINTS,
|
|
21820
|
+
isTestMintUrl,
|
|
21659
21821
|
deriveReceiptSecret,
|
|
21660
21822
|
receiptPubkeyFromSecret,
|
|
21661
21823
|
deriveReceiptKeypair,
|
|
@@ -21702,6 +21864,10 @@ export {
|
|
|
21702
21864
|
epochMsToIso,
|
|
21703
21865
|
receiptHint,
|
|
21704
21866
|
receiptFailureDetail,
|
|
21867
|
+
isPublicRequestId,
|
|
21868
|
+
parseTraceparent,
|
|
21869
|
+
PRIVKEY_HEX_RE,
|
|
21870
|
+
derivePubkeyFromPrivkey,
|
|
21705
21871
|
verifyChallenge,
|
|
21706
21872
|
ADMIN_STATE_UNAVAILABLE,
|
|
21707
21873
|
buildAdminChallenge,
|