diff --git a/.gitignore b/.gitignore index d89feb0..e12c8c6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules build -src/test/e2e/albyhub-* \ No newline at end of file +src/test/e2e/albyhub-* +.env \ No newline at end of file diff --git a/README.md b/README.md index cd2d25f..9a57f93 100644 --- a/README.md +++ b/README.md @@ -62,12 +62,12 @@ npx @getalby/hub-cli balances --hub production **Token storage locations:** -| Location | Used when | -| --- | --- | -| `~/.hub-cli/token.jwt` | `--save` flag or default | +| Location | Used when | +| ----------------------------- | ------------------------------------ | +| `~/.hub-cli/token.jwt` | `--save` flag or default | | `~/.hub-cli/token-.jwt` | `--save-as ` or `--hub ` | -| `HUB_TOKEN` env var | Always checked before file | -| `-t, --token ` | Highest priority | +| `HUB_TOKEN` env var | Always checked before file | +| `-t, --token ` | Highest priority | ## Testing with Mutinynet @@ -146,8 +146,8 @@ npx @getalby/hub-cli list-channels # List LSP providers with fees and channel size limits npx @getalby/hub-cli get-channel-suggestions -# Get Alby LSP offer (requires linked Alby account) -npx @getalby/hub-cli get-channel-offer +# Request Alby LSP offer (requires linked Alby account) +npx @getalby/hub-cli request-alby-lsp-channel-offer # Get your node's connection info (pubkey, address, port) npx @getalby/hub-cli get-node-connection-info @@ -242,68 +242,93 @@ npx @getalby/hub-cli create-app --name "Isolated App" --isolated --unlock-passwo ### Setup & Auth -| Command | Description | Required Options | -| --- | --- | --- | -| `setup` | Initialize hub for the first time (one-time) | `--password` | -| `start` | Start the node after setup or restart; returns a JWT token | `--password` | -| `unlock` | Get a JWT token for an already-running hub (no restart) | `--password` | +| Command | Description | Required Options | +| -------- | ---------------------------------------------------------- | ---------------- | +| `setup` | Initialize hub for the first time (one-time) | `--password` | +| `start` | Start the node after setup or restart; returns a JWT token | `--password` | +| `unlock` | Get a JWT token for an already-running hub (no restart) | `--password` | ### Info & Status -| Command | Description | Required Options | -| --- | --- | --- | -| `get-info` | Hub status, version, backend type | — | -| `get-node-status` | Lightning node readiness | — | -| `get-health` | Health check and active alarms | — | +| Command | Description | Required Options | +| ----------------- | --------------------------------- | ---------------- | +| `get-info` | Hub status, version, backend type | — | +| `get-node-status` | Lightning node readiness | — | +| `get-health` | Health check and active alarms | — | ### Balances & Wallet -| Command | Description | Required Options | -| --- | --- | --- | -| `balances` | Lightning + on-chain balances | — | -| `get-onchain-address` | On-chain deposit address | — | +| Command | Description | Required Options | +| --------------------- | ----------------------------- | ---------------- | +| `balances` | Lightning + on-chain balances | — | +| `get-onchain-address` | On-chain deposit address | — | ### Channels & Peers -| Command | Description | Required Options | -| --- | --- | --- | -| `list-channels` | List Lightning channels | — | -| `get-channel-suggestions` | List LSP providers with fees | — | -| `get-channel-offer` | Get Alby LSP offer | — | -| `get-node-connection-info` | Get node pubkey, address, port | — | -| `list-peers` | List connected peers | — | -| `connect-peer` | Connect to a Lightning peer | `--pubkey`, `--address`, `--port` | -| `open-channel` | Open an outbound channel to a peer | `--pubkey`, `--amount-sats` | -| `close-channel` | Close a lightning channel (cooperative or force) | `--peer-id`, `--channel-id` | -| `request-lsp-order` | Request LSP channel invoice | `--amount`, `--lsp-type`, `--lsp-identifier` | +| Command | Description | Required Options | +| -------------------------------- | ------------------------------------------------ | -------------------------------------------- | +| `list-channels` | List Lightning channels | — | +| `get-channel-suggestions` | List LSP providers with fees | — | +| `request-alby-lsp-channel-offer` | Request Alby LSP offer | — | +| `get-node-connection-info` | Get node pubkey, address, port | — | +| `list-peers` | List connected peers | — | +| `connect-peer` | Connect to a Lightning peer | `--pubkey`, `--address`, `--port` | +| `open-channel` | Open an outbound channel to a peer | `--pubkey`, `--amount-sats` | +| `close-channel` | Close a lightning channel (cooperative or force) | `--peer-id`, `--channel-id` | +| `request-lsp-order` | Request LSP channel invoice | `--amount`, `--lsp-type`, `--lsp-identifier` | ### Node Management -| Command | Description | Required Options | -| --- | --- | --- | -| `stop` | Stop the Lightning node (HTTP server keeps running) | — | +| Command | Description | Required Options | +| ------- | --------------------------------------------------- | ---------------- | +| `stop` | Stop the Lightning node (HTTP server keeps running) | — | ### Payments -| Command | Description | Required Options | -| --- | --- | --- | -| `pay-invoice` | Pay a BOLT11 invoice | `` (argument) | -| `make-invoice` | Create a BOLT11 invoice | `--amount` | +| Command | Description | Required Options | +| -------------- | ----------------------- | ---------------------- | +| `pay-invoice` | Pay a BOLT11 invoice | `` (argument) | +| `make-invoice` | Create a BOLT11 invoice | `--amount` | ### Transactions -| Command | Description | Required Options | -| --- | --- | --- | -| `list-transactions` | List payment history | — | +| Command | Description | Required Options | +| -------------------- | ------------------------- | -------------------------- | +| `list-transactions` | List payment history | — | | `lookup-transaction` | Look up a payment by hash | `` (argument) | ### NWC Apps -| Command | Description | Required Options | -| --- | --- | --- | -| `apps` | List NWC app connections | — | -| `create-app` | Create a new NWC connection | `--name` | +| Command | Description | Required Options | +| ------------ | --------------------------- | ---------------- | +| `apps` | List NWC app connections | — | +| `create-app` | Create a new NWC connection | `--name` | ## Output All commands output JSON to stdout. Errors are written to stderr as JSON with a `message` field. + +## Development + +`yarn install` + +`yarn dev` + +`yarn test` + +### E2E Testing + +End-to-end tests spawn a real Alby Hub binary and exercise the CLI against it. Full setup instructions are in [`src/test/e2e/README.md`](src/test/e2e/README.md). + +**Quick start:** + +1. Download the Linux Ubuntu 24.04 Alby Hub binary from [GitHub releases](https://github.com/getAlby/hub/releases) and extract it to `src/test/e2e/albyhub-Server-Linux-x86_64/` +2. For regtest channel tests: install [Polar](https://lightningpolar.com/) and start a network with a Bitcoin Core node +3. For Mutinynet LSP tests: copy `src/test/e2e/.env.example` → `src/test/e2e/.env` and fill in `MUTINYNET_NWC_URL` (you need a Mutinynet hub running with sufficient liquidity, ideally a direct channel directly to Megalith Mutinynet LSP) + +```bash +cd hub-cli +yarn test:e2e +``` + +Mutinynet tests are skipped automatically when `MUTINYNET_NWC_URL` is not set. diff --git a/package.json b/package.json index f422c67..3d0e821 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "commander": "^13.1.0" }, "devDependencies": { + "@getalby/sdk": "^7.0.0", "@types/node": "^22.0.0", "typescript": "^5.5.0", "vitest": "^3.0.0" diff --git a/src/commands/channel-offer.ts b/src/commands/channel-offer.ts index 445928d..fae0c57 100644 --- a/src/commands/channel-offer.ts +++ b/src/commands/channel-offer.ts @@ -4,9 +4,9 @@ import { getClient, handleError, output } from "../utils.js"; export function registerChannelOfferCommand(program: Command): void { program - .command("get-channel-offer") + .command("request-alby-lsp-channel-offer") .description( - "Get Alby LSP channel offer with recommended size and fee (requires linked Alby account)", + "Request Alby LSP channel offer with recommended size and fee (requires linked Alby account)", ) .action(async () => { await handleError(async () => { diff --git a/src/test/e2e/.env.example b/src/test/e2e/.env.example new file mode 100644 index 0000000..13bf86e --- /dev/null +++ b/src/test/e2e/.env.example @@ -0,0 +1,2 @@ +# NWC connection URL from a funded Mutinynet Alby Hub, used to pay LSP invoices in E2E tests +MUTINYNET_NWC_URL="nostr+walletconnect://..." diff --git a/src/test/e2e/README.md b/src/test/e2e/README.md index 7bde896..9e467d7 100644 --- a/src/test/e2e/README.md +++ b/src/test/e2e/README.md @@ -16,29 +16,50 @@ The directory must contain at minimum: - `bin/albyhub` — the executable - `lib/libldk_node.so` — the LDK node shared library -### Polar (optional — for future `start`/`unlock` tests) +### Polar (regtest channel lifecycle tests) -For tests that require Bitcoin connectivity (not needed for `setup`): +The `channel-lifecycle.e2e.test.ts` suite requires a local Bitcoin Core node accessible via RPC. 1. Download [Polar](https://lightningpolar.com/) -2. Create a network with a Bitcoin Core node +2. Create a network with a Bitcoin Core node using the default credentials (`polaruser` / `polarpass`) 3. Start the network -4. Set `POLAR_ESPLORA_URL` env var to your Polar Esplora URL (e.g. `http://127.0.0.1:3000`) + +The tests connect to Bitcoin Core on `127.0.0.1:18443` using those defaults — no extra env vars needed. + +### Mutinynet NWC URL (Mutinynet LSP test) + +The `mutinynet-lsp.e2e.test.ts` suite requires a pre-funded Mutinynet (signet) Alby Hub with an NWC connection URL so it can pay LSP invoices automatically. + +1. Copy the example env file: + ```bash + cp src/test/e2e/.env.example src/test/e2e/.env + ``` +2. Edit `src/test/e2e/.env` and set `MUTINYNET_NWC_URL` to your NWC connection URL. + +Without this file (or with the variable unset) the Mutinynet tests are skipped automatically — no failures. ## Running ```bash +# All E2E tests yarn test:e2e + +# Individual suite (vitest pattern matching) +yarn test:e2e --reporter=verbose ``` -## Environment variables +## Test suites -| Variable | Default | Description | -|----------|---------|-------------| -| `POLAR_ESPLORA_URL` | `http://127.0.0.1:3000` | Esplora URL from Polar (only needed for `start`/`unlock` tests) | +| File | Requires | Description | +|------|----------|-------------| +| `setup.e2e.test.ts` | Hub binary | Hub initialisation | +| `start.e2e.test.ts` | Hub binary | Node start + JWT | +| `unlock.e2e.test.ts` | Hub binary | Token refresh | +| `stop.e2e.test.ts` | Hub binary | Node stop | +| `channel-lifecycle.e2e.test.ts` | Hub binary + Polar | Two-hub regtest channel open, payments, close | +| `mutinynet-lsp.e2e.test.ts` | Hub binary + `MUTINYNET_NWC_URL` | Signet LSP channel open via NWC payment, payments, close | ## Notes -- The hub is started on port `18080` to avoid conflicts with a locally running hub -- A temporary `WORK_DIR` is created per test run and cleaned up automatically -- The `setup` test does not require Bitcoin/Polar connectivity — it only calls `POST /api/setup` +- Each test suite spawns its own hub on a dedicated port and temporary `WORK_DIR`, cleaned up automatically +- Mutinynet tests are skipped (not failed) when `MUTINYNET_NWC_URL` is not set, so CI stays green without credentials diff --git a/src/test/e2e/helpers.ts b/src/test/e2e/helpers.ts index c6164c4..44a7ec8 100644 --- a/src/test/e2e/helpers.ts +++ b/src/test/e2e/helpers.ts @@ -61,6 +61,38 @@ export async function spawnHub( return { hubProcess, workDir }; } +export async function spawnMutinynetHub( + port: number, + tmpPrefix: string, + ldkPort = DEFAULT_LDK_PORT, +): Promise<{ hubProcess: ChildProcess; workDir: string }> { + const workDir = mkdtempSync(join(tmpdir(), tmpPrefix)); + + console.log("Hub WORK_DIR:", workDir); + + const hubProcess = spawn(HUB_BINARY, [], { + env: { + ...process.env, + WORK_DIR: workDir, + PORT: String(port), + NETWORK: "signet", + MEMPOOL_API: "https://mutinynet.com/api", + LDK_ESPLORA_SERVER: "https://mutinynet.com/api", + LDK_GOSSIP_SOURCE: "https://rgs.mutinynet.com/snapshot", + LDK_LISTENING_ADDRESSES: `0.0.0.0:${ldkPort}`, + LDK_ANNOUNCEMENT_ADDRESSES: `127.0.0.1:${ldkPort}`, + }, + stdio: "pipe", + }); + + hubProcess.stdout?.on("data", (d) => process.stdout.write(`[hub] ${d}`)); + hubProcess.stderr?.on("data", (d) => process.stderr.write(`[hub] ${d}`)); + + await waitForHub(`http://localhost:${port}`); + + return { hubProcess, workDir }; +} + export async function waitForHub( url: string, timeoutMs = 20_000, diff --git a/src/test/e2e/mutinynet-lsp.e2e.test.ts b/src/test/e2e/mutinynet-lsp.e2e.test.ts new file mode 100644 index 0000000..b0dc3a6 --- /dev/null +++ b/src/test/e2e/mutinynet-lsp.e2e.test.ts @@ -0,0 +1,226 @@ +import { test, expect, beforeAll, afterAll } from "vitest"; +import { join } from "node:path"; +import type { ChildProcess } from "node:child_process"; +import { NWCClient } from "@getalby/sdk/nwc"; +import { + E2E_DIR, + TEST_PASSWORD, + spawnMutinynetHub, + runCommand, + killHub, + waitForInfo, + waitForChannels, +} from "./helpers.js"; +import type { ChannelPeerSuggestion, Transaction } from "../../types.js"; + +try { + process.loadEnvFile(join(E2E_DIR, ".env")); +} catch { + // .env file not present — tests will be skipped +} + +const MUTINYNET_NWC_URL = process.env.MUTINYNET_NWC_URL; + +const HUB_PORT = 18085; +const HUB_LDK_PORT = 19738; +const HUB_URL = `http://localhost:${HUB_PORT}`; + +let hubProcess: ChildProcess; +let token: string; + +beforeAll(async () => { + if (!MUTINYNET_NWC_URL) return; + + ({ hubProcess } = await spawnMutinynetHub( + HUB_PORT, + "hub-cli-e2e-mutinynet-", + HUB_LDK_PORT, + )); + + const setup = runCommand([ + "--url", + HUB_URL, + "setup", + "--password", + TEST_PASSWORD, + "--backend", + "LDK", + ]); + if (setup.status !== 0) throw new Error(`Hub setup failed: ${setup.stderr}`); + + const start = runCommand([ + "--url", + HUB_URL, + "start", + "--password", + TEST_PASSWORD, + ]); + if (start.status !== 0) throw new Error(`Hub start failed: ${start.stderr}`); + token = JSON.parse(start.stdout).token; + + await waitForInfo(HUB_URL, (info) => info.running); +}, 120_000); + +afterAll(async () => { + if (hubProcess) await killHub(hubProcess); +}); + +test.skipIf(!MUTINYNET_NWC_URL)( + "opens channel via LSP order, NWC client pays invoice", + { timeout: 120_000 }, + async () => { + const suggestionsResult = runCommand([ + "--url", + HUB_URL, + "--token", + token, + "get-channel-suggestions", + ]); + expect(suggestionsResult.status).toBe(0); + const suggestions = JSON.parse( + suggestionsResult.stdout, + ) as ChannelPeerSuggestion[]; + expect(suggestions.length).toBeGreaterThan(0); + const lsp = suggestions.find( + (suggestion) => + suggestion.identifier === "megalith" && + suggestion.network === "signet" && + suggestion.paymentMethod === "lightning", + ); + expect(lsp).toBeTruthy(); + if (!lsp) { + return; + } + + const orderResult = runCommand([ + "--url", + HUB_URL, + "--token", + token, + "request-lsp-order", + "--amount", + String(lsp.minimumChannelSize), + "--lsp-type", + lsp.type, + "--lsp-identifier", + lsp.identifier, + ]); + expect(orderResult.status).toBe(0); + const order = JSON.parse(orderResult.stdout) as { invoice: string }; + expect(typeof order.invoice).toBe("string"); + expect(order.invoice.startsWith("ln")).toBe(true); + + const nwcClient = new NWCClient({ + nostrWalletConnectUrl: MUTINYNET_NWC_URL!, + }); + try { + const payResult = await nwcClient.payInvoice({ invoice: order.invoice }); + expect(typeof payResult.preimage).toBe("string"); + expect(payResult.preimage.length).toBeGreaterThan(0); + } finally { + nwcClient.close(); + } + + await waitForChannels( + HUB_URL, + token, + (chs) => chs.some((c) => c.active), + 120_000, + ); + }, +); + +test.skipIf(!MUTINYNET_NWC_URL)( + "hub makes invoice, NWC client pays it", + { timeout: 60_000 }, + async () => { + const invoiceResult = runCommand([ + "--url", + HUB_URL, + "--token", + token, + "make-invoice", + "--amount", + "2000", + "--description", + "mutinynet e2e test", + ]); + expect(invoiceResult.status).toBe(0); + const tx = JSON.parse(invoiceResult.stdout) as Transaction; + expect(typeof tx.invoice).toBe("string"); + expect(tx.invoice.length).toBeGreaterThan(0); + + const nwcClient = new NWCClient({ + nostrWalletConnectUrl: MUTINYNET_NWC_URL!, + }); + try { + const payResult = await nwcClient.payInvoice({ invoice: tx.invoice }); + expect(typeof payResult.preimage).toBe("string"); + expect(payResult.preimage.length).toBeGreaterThan(0); + } finally { + nwcClient.close(); + } + }, +); + +test.skipIf(!MUTINYNET_NWC_URL)( + "NWC client makes invoice, hub CLI pays it", + { timeout: 60_000 }, + async () => { + const nwcClient = new NWCClient({ + nostrWalletConnectUrl: MUTINYNET_NWC_URL!, + }); + try { + const makeResult = await nwcClient.makeInvoice({ amount: 500_000 }); + expect(typeof makeResult.invoice).toBe("string"); + expect(makeResult.invoice.length).toBeGreaterThan(0); + + const payResult = runCommand([ + "--url", + HUB_URL, + "--token", + token, + "pay-invoice", + makeResult.invoice, + ]); + expect(payResult.status).toBe(0); + } finally { + nwcClient.close(); + } + }, +); + +test.skipIf(!MUTINYNET_NWC_URL)( + "closes the channel", + { timeout: 60_000 }, + async () => { + const channelsResult = runCommand([ + "--url", + HUB_URL, + "--token", + token, + "list-channels", + ]); + expect(channelsResult.status).toBe(0); + const channels = JSON.parse(channelsResult.stdout) as { + id: string; + remotePubkey: string; + active: boolean; + }[]; + const channel = channels.find((c) => c.active); + expect(channel).toBeDefined(); + + const closeResult = runCommand([ + "--url", + HUB_URL, + "--token", + token, + "close-channel", + "--peer-id", + channel!.remotePubkey, + "--channel-id", + channel!.id, + ]); + expect(closeResult.status).toBe(0); + }, +); diff --git a/yarn.lock b/yarn.lock index f1a881e..205c4ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -132,11 +132,41 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz#0eaf705c941a218a43dba8e09f1df1d6cd2f1f17" integrity sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA== +"@getalby/lightning-tools@^6.0.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@getalby/lightning-tools/-/lightning-tools-6.1.0.tgz#558b90a83b961cb6aa760e62de69f5b5ceeb2fe9" + integrity sha512-rGurar9X4Gm+9xwoNYS8s9YLK7ZYqvbqv4KbHLYV0LEeB0HxZHRgmxblGqg+fYfp6iiYHx+edIgUpt9rS3VwFw== + +"@getalby/sdk@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@getalby/sdk/-/sdk-7.0.0.tgz#6ab17f27bd9e762d383b70cfabd0cdeeded6bd53" + integrity sha512-0c8gyvFbRDHZIgHmOD/dfyPukxZLeidx/hx7SXlMIS/hsx4mXpKpo9Gx1zW90buElnd3k9TVB/S/bnFSEZPE7w== + dependencies: + "@getalby/lightning-tools" "^6.0.0" + nostr-tools "^2.17.0" + "@jridgewell/sourcemap-codec@^1.5.5": version "1.5.5" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== +"@noble/ciphers@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-2.1.1.tgz#c8c74fcda8c3d1f88797d0ecda24f9fc8b92b052" + integrity sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw== + +"@noble/curves@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-2.0.1.tgz#64ba8bd5e8564a02942655602515646df1cdb3ad" + integrity sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw== + dependencies: + "@noble/hashes" "2.0.1" + +"@noble/hashes@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-2.0.1.tgz#fc1a928061d1232b0a52bb754393c37a5216c89e" + integrity sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw== + "@rollup/rollup-android-arm-eabi@4.57.1": version "4.57.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz#add5e608d4e7be55bc3ca3d962490b8b1890e088" @@ -262,6 +292,28 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz#a03348e7b559c792b6277cc58874b89ef46e1e72" integrity sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA== +"@scure/base@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-2.0.0.tgz#ba6371fddf92c2727e88ad6ab485db6e624f9a98" + integrity sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w== + +"@scure/bip32@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-2.0.1.tgz#4ceea207cee8626d3fe8f0b6ab68b6af8f81c482" + integrity sha512-4Md1NI5BzoVP+bhyJaY3K6yMesEFzNS1sE/cP+9nuvE7p/b0kx9XbpDHHFl8dHtufcbdHRUUQdRqLIPHN/s7yA== + dependencies: + "@noble/curves" "2.0.1" + "@noble/hashes" "2.0.1" + "@scure/base" "2.0.0" + +"@scure/bip39@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-2.0.1.tgz#47a6dc15e04faf200041239d46ae3bb7c3c96add" + integrity sha512-PsxdFj/d2AcJcZDX1FXN3dDgitDDTmwf78rKZq1a6c1P1Nan1X/Sxc7667zU3U+AN60g7SxxP0YCVw2H/hBycg== + dependencies: + "@noble/hashes" "2.0.1" + "@scure/base" "2.0.0" + "@types/chai@^5.2.2": version "5.2.3" resolved "https://registry.yarnpkg.com/@types/chai/-/chai-5.2.3.tgz#8e9cd9e1c3581fa6b341a5aed5588eb285be0b4a" @@ -477,6 +529,24 @@ nanoid@^3.3.11: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== +nostr-tools@^2.17.0: + version "2.23.3" + resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-2.23.3.tgz#1a7501988b72499cf27c8f3951f00d11d9ac6025" + integrity sha512-AALyt9k8xPdF4UV2mlLJ2mgCn4kpTB0DZ8t2r6wjdUh6anfx2cTVBsHUlo9U0EY/cKC5wcNyiMAmRJV5OVEalA== + dependencies: + "@noble/ciphers" "2.1.1" + "@noble/curves" "2.0.1" + "@noble/hashes" "2.0.1" + "@scure/base" "2.0.0" + "@scure/bip32" "2.0.1" + "@scure/bip39" "2.0.1" + nostr-wasm "0.1.0" + +nostr-wasm@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/nostr-wasm/-/nostr-wasm-0.1.0.tgz#17af486745feb2b7dd29503fdd81613a24058d94" + integrity sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA== + pathe@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716"