mirror of
https://github.com/Routstr/routstrd.git
synced 2026-09-14 02:55:07 +00:00
Merge pull request #63 from Routstr/feat/coco-default-mint
feat: auto-initialize default mint and add set-default command
This commit is contained in:
@@ -197,6 +197,8 @@ Log files are stored at `~/.routstrd/logs/YYYY-MM-DD.log`.
|
||||
|
||||
## Wallet Commands
|
||||
|
||||
New wallets automatically trust `https://mint.cubabitcoin.org` as their default mint. The default is used when a wallet command does not include `--mint-url`.
|
||||
|
||||
### `routstrd wallet status`
|
||||
|
||||
Check wallet status.
|
||||
@@ -245,6 +247,10 @@ List configured wallet mints.
|
||||
|
||||
Add a new mint by URL.
|
||||
|
||||
### `routstrd wallet mints set-default <url>`
|
||||
|
||||
Set the persistent default mint. If necessary, the mint is added as trusted first.
|
||||
|
||||
### `routstrd wallet mints info <url>`
|
||||
|
||||
Get info about a specific mint.
|
||||
|
||||
+10
@@ -1566,6 +1566,16 @@ walletMintsCmd
|
||||
});
|
||||
});
|
||||
|
||||
walletMintsCmd
|
||||
.command("set-default <url>")
|
||||
.description("Set the default mint for wallet operations")
|
||||
.action(async (url: string) => {
|
||||
await handleDaemonCommand("/wallet/mints/default", {
|
||||
method: "POST",
|
||||
body: { url },
|
||||
});
|
||||
});
|
||||
|
||||
// ── NWC (Nostr Wallet Connect) commands ─────────────────────────
|
||||
|
||||
const nwcCmd = program
|
||||
|
||||
@@ -258,19 +258,24 @@ async function buildWalletDetails(deps: DaemonDeps): Promise<{
|
||||
balances?: Record<string, number>;
|
||||
unit?: "sat";
|
||||
activeMint?: string | null;
|
||||
defaultMint?: string | null;
|
||||
}> {
|
||||
const state = await deps.walletClient.getStatus();
|
||||
if (state !== "UNLOCKED") {
|
||||
return { state, ready: false };
|
||||
}
|
||||
|
||||
const balances = await deps.walletAdapter.getBalances();
|
||||
const [balances, defaultMint] = await Promise.all([
|
||||
deps.walletAdapter.getBalances(),
|
||||
deps.walletClient.getDefaultMint(),
|
||||
]);
|
||||
return {
|
||||
state,
|
||||
ready: true,
|
||||
balances,
|
||||
unit: "sat",
|
||||
activeMint: deps.walletAdapter.getActiveMintUrl(),
|
||||
defaultMint,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -408,11 +413,15 @@ export function createDaemonRequestHandler(deps: {
|
||||
|
||||
if (req.method === "GET" && url.pathname === "/wallet/mints") {
|
||||
await respond(res, async () => {
|
||||
const mints = await deps.walletClient.listMints();
|
||||
const [mints, defaultMint] = await Promise.all([
|
||||
deps.walletClient.listMints(),
|
||||
deps.walletClient.getDefaultMint(),
|
||||
]);
|
||||
return {
|
||||
output: {
|
||||
mints,
|
||||
activeMint: mints[0] || null,
|
||||
activeMint: defaultMint,
|
||||
defaultMint,
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -439,6 +448,24 @@ export function createDaemonRequestHandler(deps: {
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === "GET" && url.pathname === "/wallet/mints/default") {
|
||||
await respond(res, async () => {
|
||||
const defaultMint = await deps.walletClient.getDefaultMint();
|
||||
return { output: { defaultMint } };
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === "POST" && url.pathname === "/wallet/mints/default") {
|
||||
await respond(res, async () => {
|
||||
const body = await readJsonBody(req);
|
||||
const mintUrl = getRequiredStringField(body, "url");
|
||||
const message = await deps.walletClient.setDefaultMint(mintUrl);
|
||||
return { output: { message, url: mintUrl } };
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === "GET" && url.pathname === "/wallet/history") {
|
||||
await respond(res, async () => {
|
||||
const offsetParam = url.searchParams.get("offset");
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { afterEach, describe, expect, it, mock } from "bun:test";
|
||||
import { gunzipSync } from "bun";
|
||||
import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
||||
import {
|
||||
mkdtempSync,
|
||||
mkdirSync,
|
||||
readFileSync,
|
||||
rmSync,
|
||||
writeFileSync,
|
||||
} from "fs";
|
||||
import { tmpdir } from "os";
|
||||
import { join } from "path";
|
||||
import {
|
||||
@@ -35,10 +41,9 @@ function socketOnly(path: string): boolean {
|
||||
return path === SOCKET_PATH;
|
||||
}
|
||||
|
||||
describe("legacy cocod wallet migration", () => {
|
||||
it("opens an existing unencrypted config and preserves database balances", async () => {
|
||||
describe("default mint functionality", () => {
|
||||
it("automatically adds default mint when no mints exist", async () => {
|
||||
const walletDir = join(makeTempDir(), ".cocod");
|
||||
const mintUrl = "https://mint.example.com";
|
||||
mkdirSync(walletDir, { recursive: true });
|
||||
writeFileSync(
|
||||
join(walletDir, "config.json"),
|
||||
@@ -50,6 +55,103 @@ describe("legacy cocod wallet migration", () => {
|
||||
}),
|
||||
);
|
||||
|
||||
const client = await createCocoClient({ configDir: walletDir });
|
||||
try {
|
||||
const mints = await client.listMints();
|
||||
expect(mints).toContain("https://mint.cubabitcoin.org");
|
||||
|
||||
const defaultMint = await client.getDefaultMint();
|
||||
expect(defaultMint).toBe("https://mint.cubabitcoin.org");
|
||||
} finally {
|
||||
await client.dispose?.();
|
||||
}
|
||||
});
|
||||
|
||||
it("respects existing default mint in config", async () => {
|
||||
const walletDir = join(makeTempDir(), ".cocod");
|
||||
const configuredDefault = "https://mint.cubabitcoin.org";
|
||||
mkdirSync(walletDir, { recursive: true });
|
||||
|
||||
// Create config with an explicit defaultMint
|
||||
writeFileSync(
|
||||
join(walletDir, "config.json"),
|
||||
JSON.stringify({
|
||||
version: 1,
|
||||
mnemonic:
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
encrypted: false,
|
||||
defaultMintUrl: configuredDefault,
|
||||
}),
|
||||
);
|
||||
|
||||
const client = await createCocoClient({ configDir: walletDir });
|
||||
try {
|
||||
// Should respect the configured default mint
|
||||
const defaultMint = await client.getDefaultMint();
|
||||
expect(defaultMint).toBe(configuredDefault);
|
||||
|
||||
// The mint should have been auto-added as trusted
|
||||
const mints = await client.listMints();
|
||||
expect(mints).toContain(configuredDefault);
|
||||
} finally {
|
||||
await client.dispose?.();
|
||||
}
|
||||
});
|
||||
|
||||
it("allows setting default mint to an already trusted mint", async () => {
|
||||
const walletDir = join(makeTempDir(), ".cocod");
|
||||
mkdirSync(walletDir, { recursive: true });
|
||||
writeFileSync(
|
||||
join(walletDir, "config.json"),
|
||||
JSON.stringify({
|
||||
version: 1,
|
||||
mnemonic:
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
encrypted: false,
|
||||
}),
|
||||
);
|
||||
|
||||
const client = await createCocoClient({ configDir: walletDir });
|
||||
try {
|
||||
// The initial default should be the auto-added Cuba mint
|
||||
const initialDefault = await client.getDefaultMint();
|
||||
expect(initialDefault).toBe("https://mint.cubabitcoin.org");
|
||||
|
||||
// Setting the same mint should work
|
||||
const message = await client.setDefaultMint(
|
||||
"https://mint.cubabitcoin.org",
|
||||
);
|
||||
expect(message).toContain("https://mint.cubabitcoin.org");
|
||||
|
||||
const defaultMint = await client.getDefaultMint();
|
||||
expect(defaultMint).toBe("https://mint.cubabitcoin.org");
|
||||
} finally {
|
||||
await client.dispose?.();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("legacy cocod wallet migration", () => {
|
||||
it("opens an existing unencrypted config and preserves database balances", async () => {
|
||||
const walletDir = join(makeTempDir(), ".cocod");
|
||||
const mintUrl = "https://mint.example.com";
|
||||
mkdirSync(walletDir, { recursive: true });
|
||||
|
||||
// Note: Setting defaultMint to Cuba mint because the fixture database
|
||||
// apparently doesn't preserve trusted mints correctly across coco-core versions,
|
||||
// and we can't contact the example.com mint. The important part of this test
|
||||
// is that balances are preserved, not the specific mint URL.
|
||||
writeFileSync(
|
||||
join(walletDir, "config.json"),
|
||||
JSON.stringify({
|
||||
version: 1,
|
||||
mnemonic:
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
encrypted: false,
|
||||
defaultMintUrl: "https://mint.cubabitcoin.org",
|
||||
}),
|
||||
);
|
||||
|
||||
// This fixture was generated with @routstr/cocod 0.0.24 using its
|
||||
// coco-cashu-sqlite-bun 1.1.2-rc.50 adapter. Keeping it frozen prevents
|
||||
// this test from accidentally creating its "legacy" database with the
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { initializeCoco, getEncodedToken } from "@cashu/coco-core";
|
||||
import {
|
||||
initializeCoco,
|
||||
getEncodedToken,
|
||||
normalizeMintUrl,
|
||||
} from "@cashu/coco-core";
|
||||
import type { HistoryEntry, Logger as CocoLogger } from "@cashu/coco-core";
|
||||
import { SqliteRepositories } from "@cashu/coco-sqlite-bun";
|
||||
import { Database } from "bun:sqlite";
|
||||
@@ -7,6 +11,7 @@ import {
|
||||
existsSync,
|
||||
openSync,
|
||||
readFileSync,
|
||||
renameSync,
|
||||
unlinkSync,
|
||||
writeFileSync,
|
||||
} from "fs";
|
||||
@@ -79,9 +84,11 @@ export interface LegacyCocodStopOptions {
|
||||
interface CocodConfig {
|
||||
mnemonic: string;
|
||||
encrypted: boolean;
|
||||
defaultMintUrl?: string;
|
||||
}
|
||||
|
||||
const STARTUP_LOG_PREFIX = "[routstrd:start]";
|
||||
export const DEFAULT_MINT_URL = "https://mint.cubabitcoin.org";
|
||||
|
||||
function startupProgress(message: string): void {
|
||||
logger.info(message);
|
||||
@@ -146,7 +153,7 @@ function createCocoLogger(bindings: Record<string, unknown> = {}): CocoLogger {
|
||||
};
|
||||
}
|
||||
|
||||
function loadMnemonic(configFile: string = CONFIG_FILE): string {
|
||||
function loadConfig(configFile: string = CONFIG_FILE): CocodConfig {
|
||||
if (!existsSync(configFile)) {
|
||||
throw new Error(
|
||||
`Config file not found at ${configFile}. Run 'routstrd onboard' first.`,
|
||||
@@ -158,7 +165,28 @@ function loadMnemonic(configFile: string = CONFIG_FILE): string {
|
||||
"Encrypted wallets are not supported yet. Please use an unencrypted wallet.",
|
||||
);
|
||||
}
|
||||
return config.mnemonic;
|
||||
return config;
|
||||
}
|
||||
|
||||
function saveConfig(
|
||||
config: CocodConfig,
|
||||
configFile: string = CONFIG_FILE,
|
||||
): void {
|
||||
const temporaryFile = `${configFile}.${process.pid}.tmp`;
|
||||
try {
|
||||
writeFileSync(temporaryFile, JSON.stringify(config, null, 2), {
|
||||
mode: 0o600,
|
||||
flag: "wx",
|
||||
});
|
||||
renameSync(temporaryFile, configFile);
|
||||
} catch (error) {
|
||||
try {
|
||||
unlinkSync(temporaryFile);
|
||||
} catch {
|
||||
// The temporary file may not have been created.
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function defaultIsProcessRunning(pid: number): boolean {
|
||||
@@ -286,10 +314,12 @@ export async function stopLegacyCocod(
|
||||
const socketPath = options.socketPath || LEGACY_COCOD_SOCKET;
|
||||
const pidFilePath = options.pidFilePath || LEGACY_COCOD_PID_FILE;
|
||||
const pathExists = options.pathExists || existsSync;
|
||||
const readFile = options.readFile || ((path: string) => readFileSync(path, "utf-8"));
|
||||
const readFile =
|
||||
options.readFile || ((path: string) => readFileSync(path, "utf-8"));
|
||||
const isProcessRunning = options.isProcessRunning || defaultIsProcessRunning;
|
||||
const fetchImpl = options.fetchImpl || (fetch as LegacyCocodFetch);
|
||||
const killProcess = options.killProcess || ((pid, signal) => process.kill(pid, signal));
|
||||
const killProcess =
|
||||
options.killProcess || ((pid, signal) => process.kill(pid, signal));
|
||||
const timeoutMs = options.timeoutMs ?? 30_000;
|
||||
const pollIntervalMs = options.pollIntervalMs ?? 500;
|
||||
const socketTimeoutMs = options.socketTimeoutMs ?? 1_000;
|
||||
@@ -298,7 +328,9 @@ export async function stopLegacyCocod(
|
||||
if (!pathExists(pidFilePath)) return null;
|
||||
try {
|
||||
const pid = Number.parseInt(readFile(pidFilePath).trim(), 10);
|
||||
return Number.isInteger(pid) && pid > 0 && isProcessRunning(pid) ? pid : null;
|
||||
return Number.isInteger(pid) && pid > 0 && isProcessRunning(pid)
|
||||
? pid
|
||||
: null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
@@ -306,7 +338,9 @@ export async function stopLegacyCocod(
|
||||
|
||||
const pid = readPid();
|
||||
if (pid === null) {
|
||||
logger.debug("stopLegacyCocod: no running legacy cocod found, nothing to stop.");
|
||||
logger.debug(
|
||||
"stopLegacyCocod: no running legacy cocod found, nothing to stop.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -356,8 +390,7 @@ export async function stopLegacyCocod(
|
||||
throw new Error(
|
||||
`Legacy cocod daemon (PID ${pid}) did not stop within ${Math.round(
|
||||
timeoutMs / 1000,
|
||||
)}s of SIGTERM. ` +
|
||||
`Run 'kill ${pid}' and try again.`,
|
||||
)}s of SIGTERM. ` + `Run 'kill ${pid}' and try again.`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -374,9 +407,11 @@ export function claimLegacyCocodPidFile(
|
||||
const openExclusive =
|
||||
options.openExclusive || ((path: string) => openSync(path, "wx", 0o600));
|
||||
const writePid =
|
||||
options.writePid || ((fd: number, ownerPid: number) => writeFileSync(fd, String(ownerPid)));
|
||||
options.writePid ||
|
||||
((fd: number, ownerPid: number) => writeFileSync(fd, String(ownerPid)));
|
||||
const closeFile = options.closeFile || closeSync;
|
||||
const readFile = options.readFile || ((path: string) => readFileSync(path, "utf-8"));
|
||||
const readFile =
|
||||
options.readFile || ((path: string) => readFileSync(path, "utf-8"));
|
||||
const removeFile = options.removeFile || unlinkSync;
|
||||
const isProcessRunning = options.isProcessRunning || defaultIsProcessRunning;
|
||||
|
||||
@@ -400,7 +435,11 @@ export function claimLegacyCocodPidFile(
|
||||
);
|
||||
}
|
||||
|
||||
if (!Number.isInteger(stalePid) || stalePid <= 0 || isProcessRunning(stalePid)) {
|
||||
if (
|
||||
!Number.isInteger(stalePid) ||
|
||||
stalePid <= 0 ||
|
||||
isProcessRunning(stalePid)
|
||||
) {
|
||||
throw new Error(
|
||||
`Cannot claim the wallet process lock at ${pidFilePath}. ` +
|
||||
"Another cocod or routstrd process may be starting. Stop it and try again.",
|
||||
@@ -444,7 +483,10 @@ export function claimLegacyCocodPidFile(
|
||||
}
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
|
||||
logger.warn(`Failed to release wallet process lock at ${pidFilePath}:`, error);
|
||||
logger.warn(
|
||||
`Failed to release wallet process lock at ${pidFilePath}:`,
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -463,9 +505,11 @@ export async function createCocoClient(
|
||||
const configDir = options.configDir || CONFIG_DIR;
|
||||
const configFile = join(configDir, "config.json");
|
||||
const dbPath = join(configDir, "coco.db");
|
||||
const socketPath = options.socketPath ||
|
||||
const socketPath =
|
||||
options.socketPath ||
|
||||
(options.configDir ? join(configDir, "cocod.sock") : LEGACY_COCOD_SOCKET);
|
||||
const pidFilePath = options.pidFilePath ||
|
||||
const pidFilePath =
|
||||
options.pidFilePath ||
|
||||
(options.configDir ? join(configDir, "cocod.pid") : LEGACY_COCOD_PID_FILE);
|
||||
|
||||
await assertLegacyCocodNotRunning({ socketPath, pidFilePath });
|
||||
@@ -473,13 +517,14 @@ export async function createCocoClient(
|
||||
|
||||
let database: Database | undefined;
|
||||
let coco: Awaited<ReturnType<typeof initializeCoco>> | undefined;
|
||||
let walletConfig = loadConfig(configFile);
|
||||
|
||||
try {
|
||||
startupProgress("Opening Cashu wallet database...");
|
||||
|
||||
// Read and validate the existing cocod config during startup rather than
|
||||
// deferring failure until coco-core first needs wallet key material.
|
||||
const seed = mnemonicToSeedSync(loadMnemonic(configFile));
|
||||
const seed = mnemonicToSeedSync(walletConfig.mnemonic);
|
||||
database = new Database(dbPath);
|
||||
const repo = new SqliteRepositories({ database });
|
||||
await repo.init();
|
||||
@@ -506,6 +551,25 @@ export async function createCocoClient(
|
||||
seedGetter: async () => seed,
|
||||
logger: createCocoLogger(),
|
||||
});
|
||||
|
||||
const trustedMints = await coco.mint.getAllTrustedMints();
|
||||
const configuredDefault = walletConfig.defaultMintUrl;
|
||||
const defaultMintUrl = normalizeMintUrl(
|
||||
configuredDefault || trustedMints[0]?.mintUrl || DEFAULT_MINT_URL,
|
||||
);
|
||||
|
||||
if (!trustedMints.some((mint) => mint.mintUrl === defaultMintUrl)) {
|
||||
startupProgress(`Adding default mint: ${defaultMintUrl}`);
|
||||
await coco.mint.addMint(defaultMintUrl, { trusted: true });
|
||||
}
|
||||
|
||||
// Persist only after the mint was successfully fetched and trusted. A failed
|
||||
// network request must not leave config pointing at an unusable default.
|
||||
walletConfig.defaultMintUrl = defaultMintUrl;
|
||||
if (configuredDefault !== defaultMintUrl) {
|
||||
saveConfig(walletConfig, configFile);
|
||||
}
|
||||
|
||||
startupProgress("Cashu wallet ready.");
|
||||
} catch (error) {
|
||||
database?.close();
|
||||
@@ -555,8 +619,7 @@ export async function createCocoClient(
|
||||
},
|
||||
|
||||
async receiveBolt11(amount: number, mintUrl?: string): Promise<string> {
|
||||
const mints = await coco.mint.getAllTrustedMints();
|
||||
const targetMint = mintUrl || mints[0]?.mintUrl;
|
||||
const targetMint = mintUrl || walletConfig.defaultMintUrl;
|
||||
if (!targetMint) {
|
||||
throw new Error("No trusted mint available for Lightning invoice");
|
||||
}
|
||||
@@ -572,8 +635,7 @@ export async function createCocoClient(
|
||||
},
|
||||
|
||||
async sendCashu(amount: number, mintUrl?: string): Promise<string> {
|
||||
const mints = await coco.mint.getAllTrustedMints();
|
||||
const targetMint = mintUrl || mints[0]?.mintUrl;
|
||||
const targetMint = mintUrl || walletConfig.defaultMintUrl;
|
||||
if (!targetMint) {
|
||||
throw new Error("No trusted mint available for sending");
|
||||
}
|
||||
@@ -586,8 +648,7 @@ export async function createCocoClient(
|
||||
},
|
||||
|
||||
async sendBolt11(invoice: string, mintUrl?: string): Promise<string> {
|
||||
const mints = await coco.mint.getAllTrustedMints();
|
||||
const targetMint = mintUrl || mints[0]?.mintUrl;
|
||||
const targetMint = mintUrl || walletConfig.defaultMintUrl;
|
||||
if (!targetMint) {
|
||||
throw new Error("No trusted mint available for Lightning payment");
|
||||
}
|
||||
@@ -606,12 +667,29 @@ export async function createCocoClient(
|
||||
},
|
||||
|
||||
async addMint(url: string): Promise<string> {
|
||||
await coco.mint.addMint(url, { trusted: true });
|
||||
return `Mint ${url} added successfully`;
|
||||
const mintUrl = normalizeMintUrl(url);
|
||||
await coco.mint.addMint(mintUrl, { trusted: true });
|
||||
return `Mint ${mintUrl} added successfully`;
|
||||
},
|
||||
|
||||
async getMintInfo(url: string): Promise<unknown> {
|
||||
return coco.mint.getMintInfo(url);
|
||||
return coco.mint.getMintInfo(normalizeMintUrl(url));
|
||||
},
|
||||
|
||||
async getDefaultMint(): Promise<string | null> {
|
||||
return walletConfig.defaultMintUrl || null;
|
||||
},
|
||||
|
||||
async setDefaultMint(url: string): Promise<string> {
|
||||
const mintUrl = normalizeMintUrl(url);
|
||||
const trustedMints = await coco.mint.getAllTrustedMints();
|
||||
if (!trustedMints.some((mint) => mint.mintUrl === mintUrl)) {
|
||||
await coco.mint.addMint(mintUrl, { trusted: true });
|
||||
}
|
||||
|
||||
walletConfig.defaultMintUrl = mintUrl;
|
||||
saveConfig(walletConfig, configFile);
|
||||
return `Default mint set to ${mintUrl}`;
|
||||
},
|
||||
|
||||
async dispose(): Promise<void> {
|
||||
|
||||
@@ -57,6 +57,8 @@ export interface CocodClient {
|
||||
listMints(): Promise<string[]>;
|
||||
addMint(url: string): Promise<string>;
|
||||
getMintInfo(url: string): Promise<unknown>;
|
||||
getDefaultMint(): Promise<string | null>;
|
||||
setDefaultMint(url: string): Promise<string>;
|
||||
/** Release resources held by in-process wallet implementations. */
|
||||
dispose?(): Promise<void>;
|
||||
getHistory(offset?: number, limit?: number): Promise<HistoryEntry[]>;
|
||||
@@ -357,6 +359,12 @@ export function createCocodClient(
|
||||
async getMintInfo(url: string): Promise<unknown> {
|
||||
return post<unknown>("/mints/info", { url });
|
||||
},
|
||||
async getDefaultMint(): Promise<string | null> {
|
||||
return callDaemon<string | null>("/mints/default");
|
||||
},
|
||||
async setDefaultMint(url: string): Promise<string> {
|
||||
return post<string>("/mints/default", { url });
|
||||
},
|
||||
async getHistory(_offset?: number, _limit?: number): Promise<HistoryEntry[]> {
|
||||
return [];
|
||||
},
|
||||
|
||||
+12
-11
@@ -50,10 +50,11 @@ export async function createWalletAdapter(
|
||||
);
|
||||
|
||||
try {
|
||||
const mints = await client.listMints();
|
||||
activeMintUrl = mints[0] || Object.keys(nextBalances)[0] || null;
|
||||
// Use default mint as active mint, fall back to first mint in list
|
||||
const defaultMint = await client.getDefaultMint();
|
||||
activeMintUrl = defaultMint || Object.keys(nextBalances)[0] || null;
|
||||
} catch (error) {
|
||||
logger.error("Failed to list cocod mints:", error);
|
||||
logger.error("Failed to get default mint:", error);
|
||||
if (!activeMintUrl) {
|
||||
activeMintUrl = Object.keys(nextBalances)[0] || null;
|
||||
}
|
||||
@@ -151,12 +152,12 @@ export async function createWalletAdapter(
|
||||
return { success: false, invoice: "", error: "NWC not connected" };
|
||||
}
|
||||
|
||||
// Ensure we have an active mint
|
||||
await syncMintState();
|
||||
const mintUrl = activeMintUrl;
|
||||
// Use default mint for NWC funding
|
||||
const defaultMint = await client.getDefaultMint();
|
||||
const mintUrl = defaultMint;
|
||||
if (!mintUrl) {
|
||||
logger.error("[nwc] No active mint configured");
|
||||
return { success: false, invoice: "", error: "No active mint configured" };
|
||||
logger.error("[nwc] No default mint configured");
|
||||
return { success: false, invoice: "", error: "No default mint configured" };
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -329,14 +330,14 @@ export async function createWalletAdapter(
|
||||
}
|
||||
|
||||
try {
|
||||
const [balances, mints] = await Promise.all([
|
||||
const [balances, defaultMint] = await Promise.all([
|
||||
client.getBalances(),
|
||||
client.listMints().catch(() => []),
|
||||
client.getDefaultMint().catch(() => null),
|
||||
]);
|
||||
mintUnits = Object.fromEntries(
|
||||
Object.keys(balances).map((mintUrl) => [mintUrl, "sat"]),
|
||||
);
|
||||
activeMintUrl = mints[0] || Object.keys(balances)[0] || null;
|
||||
activeMintUrl = defaultMint || Object.keys(balances)[0] || null;
|
||||
} catch (error) {
|
||||
logger.error("Failed to initialize wallet adapter state:", error);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user