Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 44 additions & 1 deletion src/commands/get-balances.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,57 @@ import { Command } from "commander";
import { BalancesResponse } from "../types.js";
import { getClient, handleError, output } from "../utils.js";

interface LightningBalanceResponse {
totalSpendable: number;
totalReceivable: number;
nextMaxSpendable: number;
nextMaxReceivable: number;
}

interface OnchainBalanceResponse {
spendable: number;
total: number;
reserved: number;
pendingBalancesFromChannelClosures: number;
}

interface APIBalancesResponse {
lightning: LightningBalanceResponse;
onchain: OnchainBalanceResponse;
}

export function registerGetBalancesCommand(program: Command): void {
program
.command("get-balances")
.description("Get Lightning and on-chain balances")
.action(async () => {
await handleError(async () => {
const client = getClient(program);
const result = await client.get<BalancesResponse>("/api/balances");
const apiResult =
await client.get<APIBalancesResponse>("/api/balances");
const result: BalancesResponse = {
onchain: {
reservedSat: apiResult.onchain.reserved,
spendableSat: apiResult.onchain.spendable,
totalSat: apiResult.onchain.total,
pendingBalancesFromChannelClosuresSat:
apiResult.onchain.pendingBalancesFromChannelClosures,
},
lightning: {
nextMaxReceivableSat: Math.floor(
apiResult.lightning.nextMaxReceivable / 1000,
),
nextMaxSpendableSat: Math.floor(
apiResult.lightning.nextMaxSpendable / 1000,
),
totalReceivableSat: Math.floor(
apiResult.lightning.totalReceivable / 1000,
),
totalSpendableSat: Math.floor(
apiResult.lightning.totalSpendable / 1000,
),
},
};
output(result);
});
});
Expand Down
8 changes: 7 additions & 1 deletion src/commands/get-channel-suggestions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ export function registerListChannelSuggestionsCommand(program: Command): void {
const result = await client.get<ChannelPeerSuggestion[]>(
"/api/channels/suggestions",
);
output(result);
output(
result.map(({ minimumChannelSize, maximumChannelSize, ...rest }) => ({
...rest,
minimumChannelSizeSat: minimumChannelSize,
maximumChannelSizeSat: maximumChannelSize,
})),
);
});
});
}
25 changes: 23 additions & 2 deletions src/commands/list-channels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,29 @@ export function registerListChannelsCommand(program: Command): void {
.action(async () => {
await handleError(async () => {
const client = getClient(program);
const result = await client.get<Channel[]>("/api/channels");
output(result);
const result = await client.get<(Channel & { internalChannel?: unknown })[]>("/api/channels");
output(
result.map(
({
localBalance,
localSpendableBalance,
remoteBalance,
forwardingFeeBaseMsat,
unspendablePunishmentReserve,
counterpartyUnspendablePunishmentReserve,
internalChannel,
...rest
}) => ({
...rest,
localBalanceSat: Math.floor(localBalance / 1000),
localSpendableBalanceSat: Math.floor(localSpendableBalance / 1000),
remoteBalanceSat: Math.floor(remoteBalance / 1000),
forwardingFeeBaseSat: Math.floor(forwardingFeeBaseMsat / 1000),
unspendablePunishmentReserveSat: unspendablePunishmentReserve,
counterpartyUnspendablePunishmentReserveSat: counterpartyUnspendablePunishmentReserve,
}),
),
);
});
});
}
7 changes: 5 additions & 2 deletions src/commands/list-transactions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Command } from "commander";
import { ListTransactionsResponse } from "../types.js";
import { getClient, handleError, output } from "../utils.js";
import { getClient, handleError, mapTransaction, output } from "../utils.js";

export function registerListTransactionsCommand(program: Command): void {
program
Expand All @@ -22,7 +22,10 @@ export function registerListTransactionsCommand(program: Command): void {
const result = await client.get<ListTransactionsResponse>(
`/api/transactions?${params}`,
);
output(result);
output({
...result,
transactions: result.transactions.map(mapTransaction),
});
});
});
}
4 changes: 2 additions & 2 deletions src/commands/lookup-transaction.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Command } from "commander";
import { Transaction } from "../types.js";
import { getClient, handleError, output } from "../utils.js";
import { getClient, handleError, mapTransaction, output } from "../utils.js";

export function registerLookupTransactionCommand(program: Command): void {
program
Expand All @@ -12,7 +12,7 @@ export function registerLookupTransactionCommand(program: Command): void {
const result = await client.get<Transaction>(
`/api/transactions/${paymentHash}`,
);
output(result);
output(mapTransaction(result));
});
});
}
4 changes: 2 additions & 2 deletions src/commands/make-invoice.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Command } from "commander";
import { Transaction } from "../types.js";
import { getClient, handleError, output } from "../utils.js";
import { getClient, handleError, mapTransaction, output } from "../utils.js";

export function registerMakeInvoiceCommand(program: Command): void {
program
Expand All @@ -15,7 +15,7 @@ export function registerMakeInvoiceCommand(program: Command): void {
amount: opts.amount * 1000,
description: opts.description,
});
output(result);
output(mapTransaction(result));
});
});
}
4 changes: 2 additions & 2 deletions src/commands/pay-invoice.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Command } from "commander";
import { Transaction } from "../types.js";
import { getClient, handleError, output } from "../utils.js";
import { getClient, handleError, mapTransaction, output } from "../utils.js";

export function registerPayInvoiceCommand(program: Command): void {
program
Expand All @@ -18,7 +18,7 @@ export function registerPayInvoiceCommand(program: Command): void {
`/api/payments/${invoice}`,
Object.keys(body).length > 0 ? body : undefined,
);
output(result);
output(mapTransaction(result));
});
});
}
9 changes: 8 additions & 1 deletion src/commands/request-lsp-order.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,14 @@ export function registerRequestLspOrderCommand(program: Command): void {
public: opts.public,
},
);
output(result);
const { fee, invoiceAmount, incomingLiquidity, outgoingLiquidity } = result;
output({
invoice: result.invoice,
feeSat: fee,
invoiceAmountSat: invoiceAmount,
incomingLiquiditySat: incomingLiquidity,
outgoingLiquiditySat: outgoingLiquidity,
});
});
},
);
Expand Down
70 changes: 25 additions & 45 deletions src/test/e2e/channel-lifecycle.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
waitForChannels,
} from "./helpers";
import type {
BalancesResponse,
Channel,
ListTransactionsResponse,
NodeConnectionInfo,
} from "../../types.js";
Expand Down Expand Up @@ -139,10 +141,10 @@ test("deposits on-chain funds to hub A", { timeout: 120_000 }, async () => {
const balances = await waitForBalances(
HUB_A_URL,
tokenA,
(b) => b.onchain.spendable > 0,
(b) => b.onchain.spendableSat > 0,
120_000,
);
expect(balances.onchain.spendable).toBeGreaterThan(0);
expect(balances.onchain.spendableSat).toBeGreaterThan(0);
});

test("connects hub A as peer to hub B", { timeout: 60_000 }, async () => {
Expand Down Expand Up @@ -282,10 +284,7 @@ test("opens channel from hub A to hub B", { timeout: 120_000 }, async () => {
"list-channels",
]);
expect(listChAResult.status).toBe(0);
const listChA = JSON.parse(listChAResult.stdout) as {
remotePubkey: string;
active: boolean;
}[];
const listChA = JSON.parse(listChAResult.stdout) as Channel[];
expect(Array.isArray(listChA)).toBe(true);
const activeChA = listChA.find(
(c) => c.remotePubkey === hubBConnInfo.pubkey && c.active,
Expand All @@ -301,7 +300,7 @@ test("opens channel from hub A to hub B", { timeout: 120_000 }, async () => {
"list-channels",
]);
expect(listChBResult.status).toBe(0);
const listChB = JSON.parse(listChBResult.stdout) as { active: boolean }[];
const listChB = JSON.parse(listChBResult.stdout) as Channel[];
expect(Array.isArray(listChB)).toBe(true);
expect(listChB.some((c) => c.active)).toBe(true);

Expand Down Expand Up @@ -346,10 +345,8 @@ test("sends sats from hub A to hub B", { timeout: 120_000 }, async () => {
"get-balances",
]);
expect(balancesBeforeResult.status).toBe(0);
const balancesBeforeData = JSON.parse(balancesBeforeResult.stdout) as {
lightning: { totalSpendable: number };
};
const hubASpendableBefore = balancesBeforeData.lightning.totalSpendable;
const balancesBeforeData = JSON.parse(balancesBeforeResult.stdout) as BalancesResponse;
const hubASpendableBefore = balancesBeforeData.lightning.totalSpendableSat;
expect(hubASpendableBefore).toBeGreaterThan(0);

// Hub A pays the invoice
Expand All @@ -372,10 +369,8 @@ test("sends sats from hub A to hub B", { timeout: 120_000 }, async () => {
"get-balances",
]);
expect(hubABalancesAfterResult.status).toBe(0);
const hubABalancesAfterData = JSON.parse(hubABalancesAfterResult.stdout) as {
lightning: { totalSpendable: number };
};
expect(hubABalancesAfterData.lightning.totalSpendable).toBeLessThan(
const hubABalancesAfterData = JSON.parse(hubABalancesAfterResult.stdout) as BalancesResponse;
expect(hubABalancesAfterData.lightning.totalSpendableSat).toBeLessThan(
hubASpendableBefore,
);

Expand All @@ -396,10 +391,8 @@ test("sends sats from hub A to hub B", { timeout: 120_000 }, async () => {
tokenB,
"get-balances",
]);
const hubBBalancesAfterData = JSON.parse(hubBBalancesAfterResult.stdout) as {
lightning: { totalSpendable: number };
};
expect(hubBBalancesAfterData.lightning.totalSpendable).toBeGreaterThan(0);
const hubBBalancesAfterData = JSON.parse(hubBBalancesAfterResult.stdout) as BalancesResponse;
expect(hubBBalancesAfterData.lightning.totalSpendableSat).toBeGreaterThan(0);
});

test("sends sats from hub B back to hub A", { timeout: 120_000 }, async () => {
Expand Down Expand Up @@ -432,9 +425,7 @@ test("sends sats from hub B back to hub A", { timeout: 120_000 }, async () => {
expect(hubABalancesBeforeResult.status).toBe(0);
const hubABalancesBeforeData = JSON.parse(
hubABalancesBeforeResult.stdout,
) as {
lightning: { totalSpendable: number };
};
) as BalancesResponse;

// Record Hub B's balance before payment
const hubBBalancesBeforeResult = runCommand([
Expand All @@ -447,10 +438,8 @@ test("sends sats from hub B back to hub A", { timeout: 120_000 }, async () => {
expect(hubBBalancesBeforeResult.status).toBe(0);
const hubBBalancesBeforeData = JSON.parse(
hubBBalancesBeforeResult.stdout,
) as {
lightning: { totalSpendable: number };
};
const hubBSpendableBefore = hubBBalancesBeforeData.lightning.totalSpendable;
) as BalancesResponse;
const hubBSpendableBefore = hubBBalancesBeforeData.lightning.totalSpendableSat;
expect(hubBSpendableBefore).toBeGreaterThan(0);

// Hub B pays the invoice
Expand All @@ -473,10 +462,8 @@ test("sends sats from hub B back to hub A", { timeout: 120_000 }, async () => {
"get-balances",
]);
expect(hubBBalancesAfterResult.status).toBe(0);
const hubBBalancesAfterData = JSON.parse(hubBBalancesAfterResult.stdout) as {
lightning: { totalSpendable: number };
};
expect(hubBBalancesAfterData.lightning.totalSpendable).toBeLessThan(
const hubBBalancesAfterData = JSON.parse(hubBBalancesAfterResult.stdout) as BalancesResponse;
expect(hubBBalancesAfterData.lightning.totalSpendableSat).toBeLessThan(
hubBSpendableBefore,
);

Expand All @@ -500,11 +487,9 @@ test("sends sats from hub B back to hub A", { timeout: 120_000 }, async () => {
"get-balances",
]);
expect(hubABalancesAfterResult.status).toBe(0);
const hubABalancesAfterData = JSON.parse(hubABalancesAfterResult.stdout) as {
lightning: { totalSpendable: number };
};
expect(hubABalancesAfterData.lightning.totalSpendable).toBeGreaterThan(
hubABalancesBeforeData.lightning.totalSpendable,
const hubABalancesAfterData = JSON.parse(hubABalancesAfterResult.stdout) as BalancesResponse;
expect(hubABalancesAfterData.lightning.totalSpendableSat).toBeGreaterThan(
hubABalancesBeforeData.lightning.totalSpendableSat,
);

// Hub A: list-transactions — should have at least one incoming settled transaction
Expand Down Expand Up @@ -554,10 +539,7 @@ test(
"list-channels",
]);
expect(channelsResult.status).toBe(0);
const channels = JSON.parse(channelsResult.stdout) as {
id: string;
remotePubkey: string;
}[];
const channels = JSON.parse(channelsResult.stdout) as Channel[];
const channel = channels.find(
(c) => c.remotePubkey === hubBConnInfo.pubkey,
);
Expand All @@ -573,10 +555,8 @@ test(
"get-balances",
]);
expect(balancesBeforeResult.status).toBe(0);
const balancesBeforeData = JSON.parse(balancesBeforeResult.stdout) as {
onchain: { spendable: number };
};
const onchainBefore = balancesBeforeData.onchain.spendable;
const balancesBeforeData = JSON.parse(balancesBeforeResult.stdout) as BalancesResponse;
const onchainBefore = balancesBeforeData.onchain.spendableSat;

// Close the channel
const closeResult = runCommand([
Expand Down Expand Up @@ -605,9 +585,9 @@ test(
const balancesAfter = await waitForBalances(
HUB_A_URL,
tokenA,
(b) => b.onchain.spendable > onchainBefore,
(b) => b.onchain.spendableSat > onchainBefore,
120_000,
);
expect(balancesAfter.onchain.spendable).toBeGreaterThan(onchainBefore);
expect(balancesAfter.onchain.spendableSat).toBeGreaterThan(onchainBefore);
},
);
Loading
Loading