Initial Commit
This commit is contained in:
commit
2d858727b0
10 changed files with 1128 additions and 0 deletions
172
examples/portalbtc-cli.ts
Normal file
172
examples/portalbtc-cli.ts
Normal file
|
@ -0,0 +1,172 @@
|
|||
#!/usr/bin/env bun
|
||||
|
||||
// Example CLI for interacting with PortalBtcWallet
|
||||
// Usage examples:
|
||||
// bun run examples/portalbtc-cli.ts balance
|
||||
// bun run examples/portalbtc-cli.ts invoice 1000
|
||||
// bun run examples/portalbtc-cli.ts pay <destination> [amountSat]
|
||||
// bun run examples/portalbtc-cli.ts redeem-token <cashu-token>
|
||||
// bun run examples/portalbtc-cli.ts list [limit] [offset]
|
||||
//
|
||||
// Environment variables required:
|
||||
// MNEMONIC – BIP39 seed words for the wallet (string, required)
|
||||
// BREEZ_API_KEY – Breez SDK API key (string, required)
|
||||
// NETWORK – "mainnet" | "testnet" (default: "testnet")
|
||||
|
||||
import PortalBtcWallet from "../index";
|
||||
import { PaymentStatus } from "../paymentStatus";
|
||||
import type { CashuStore, CashuTxn } from "../paymentStatus";
|
||||
import type { Proof } from "@cashu/cashu-ts";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
interface CashuPersistenceFile {
|
||||
proofs: Proof[];
|
||||
txns: CashuTxn[];
|
||||
lastRedeemedCashuQuoteTimestamp: number;
|
||||
}
|
||||
|
||||
class FileCashuStore implements CashuStore {
|
||||
private data: CashuPersistenceFile;
|
||||
|
||||
constructor(private filePath: string) {
|
||||
if (fs.existsSync(filePath)) {
|
||||
this.data = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
||||
} else {
|
||||
this.data = {
|
||||
proofs: [],
|
||||
txns: [],
|
||||
lastRedeemedCashuQuoteTimestamp: 0,
|
||||
};
|
||||
this.flush();
|
||||
}
|
||||
}
|
||||
|
||||
private flush() {
|
||||
fs.writeFileSync(this.filePath, JSON.stringify(this.data, null, 2));
|
||||
}
|
||||
|
||||
async getProofs(): Promise<Proof[]> {
|
||||
return this.data.proofs;
|
||||
}
|
||||
|
||||
async getTxns(): Promise<CashuTxn[]> {
|
||||
return this.data.txns;
|
||||
}
|
||||
|
||||
async getLastRedeemedCashuQuoteTimestamp(): Promise<number> {
|
||||
return this.data.lastRedeemedCashuQuoteTimestamp;
|
||||
}
|
||||
|
||||
async persistProofs(proofs: Proof[]): Promise<void> {
|
||||
this.data.proofs = proofs;
|
||||
this.flush();
|
||||
}
|
||||
|
||||
async persistTxns(txns: CashuTxn[]): Promise<void> {
|
||||
this.data.txns = txns;
|
||||
this.flush();
|
||||
}
|
||||
|
||||
async persistLastRedeemedCashuQuoteTimestamp(
|
||||
timestamp: number,
|
||||
): Promise<void> {
|
||||
this.data.lastRedeemedCashuQuoteTimestamp = timestamp;
|
||||
this.flush();
|
||||
}
|
||||
}
|
||||
|
||||
function usage() {
|
||||
console.log(`Usage:
|
||||
balance – Show wallet balances (on-chain + Cashu)
|
||||
invoice <amountSat> – Generate a Lightning invoice
|
||||
pay <destination> [amountSat] – Pay a destination (invoice / LNURL / address)
|
||||
redeem-token <cashu-token> – Redeem a Cashu token
|
||||
list [limit] [offset] – List recent payments (default limit 20)
|
||||
`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const [cmd, ...rest] = process.argv.slice(2);
|
||||
if (!cmd) usage();
|
||||
|
||||
const mnemonic = process.env.MNEMONIC;
|
||||
const network = (process.env.NETWORK ?? "testnet") as "mainnet" | "testnet";
|
||||
|
||||
if (!mnemonic) {
|
||||
console.error(
|
||||
"Error: MNEMONIC environment variable is required.",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const statePath = path.resolve(
|
||||
path.dirname(new URL(import.meta.url).pathname),
|
||||
".portalbtc_state.json",
|
||||
);
|
||||
const cashuStore = new FileCashuStore(statePath);
|
||||
|
||||
const wallet = await PortalBtcWallet.create(
|
||||
mnemonic,
|
||||
cashuStore,
|
||||
false,
|
||||
network,
|
||||
);
|
||||
|
||||
switch (cmd) {
|
||||
case "balance": {
|
||||
console.log("Balance (sats):", wallet.balance);
|
||||
console.log(" – Cashu balance:", wallet.cashuBalance);
|
||||
console.log(" – Pending balance:", wallet.pendingBalance);
|
||||
console.log(" – Lightning address:", wallet.lightningAddress);
|
||||
break;
|
||||
}
|
||||
case "invoice": {
|
||||
const amountSat = Number.parseInt(rest[0] ?? "0", 10);
|
||||
if (!amountSat) {
|
||||
console.error("Amount (sat) required for invoice command");
|
||||
process.exit(1);
|
||||
}
|
||||
const invoice = await wallet.generateBolt11Invoice(amountSat);
|
||||
console.log("Generated invoice:", invoice);
|
||||
break;
|
||||
}
|
||||
case "pay": {
|
||||
const destination = rest[0];
|
||||
const amountArg = rest[1] ? Number.parseInt(rest[1], 10) : 0;
|
||||
if (!destination) {
|
||||
console.error("Destination required for pay command");
|
||||
process.exit(1);
|
||||
}
|
||||
for await (const status of wallet.pay(destination, amountArg)) {
|
||||
console.log("→", PaymentStatus[status.status], status);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "redeem-token": {
|
||||
const token = rest[0];
|
||||
if (!token) {
|
||||
console.error("Cashu token required for redeem-token command");
|
||||
process.exit(1);
|
||||
}
|
||||
await wallet.redeemToken(token);
|
||||
console.log("Token redeemed successfully");
|
||||
break;
|
||||
}
|
||||
case "list": {
|
||||
const limit = rest[0] ? Number.parseInt(rest[0], 10) : 20;
|
||||
const offset = rest[1] ? Number.parseInt(rest[1], 10) : 0;
|
||||
const payments = await wallet.listPayments(limit, offset);
|
||||
console.log(payments);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue