feat: basic wallet

This commit is contained in:
2023-01-31 18:56:31 +00:00
parent dc31ba9ad3
commit 7db8960914
8 changed files with 358 additions and 0 deletions

View File

@ -0,0 +1,134 @@
import {
InvoiceRequest,
LNWallet,
Sats,
UnknownWalletError,
WalletError,
WalletInfo,
WalletInvoice,
WalletInvoiceState,
} from "Wallet";
const defaultHeaders = {
Accept: "application/json",
"Content-Type": "application/json",
};
export default class LNDHubWallet implements LNWallet {
url: string;
user: string;
password: string;
auth?: AuthResponse;
constructor(url: string) {
const regex = /^lndhub:\/\/([\S-]+):([\S-]+)@(.*)$/i;
const parsedUrl = url.match(regex);
if (!parsedUrl || parsedUrl.length !== 4) {
throw new Error("Invalid LNDHUB config");
}
this.url = new URL(parsedUrl[3]).toString();
this.user = parsedUrl[1];
this.password = parsedUrl[2];
}
async createAccount() {
return Promise.resolve(UnknownWalletError);
}
async getInfo() {
return await this.getJson<WalletInfo>("GET", "/getinfo");
}
async login() {
const rsp = await this.getJson<AuthResponse>("POST", "/auth?type=auth", {
login: this.user,
password: this.password,
});
if ("error" in rsp) {
return rsp as WalletError;
}
this.auth = rsp as AuthResponse;
return true;
}
async getBalance(): Promise<Sats | WalletError> {
let rsp = await this.getJson<GetBalanceResponse>("GET", "/balance");
if ("error" in rsp) {
return rsp as WalletError;
}
let bal = Math.floor((rsp as GetBalanceResponse).BTC.AvailableBalance);
return bal as Sats;
}
async createInvoice(req: InvoiceRequest) {
return Promise.resolve(UnknownWalletError);
}
async payInvoice(pr: string) {
return Promise.resolve(UnknownWalletError);
}
async getInvoices(): Promise<WalletInvoice[] | WalletError> {
let rsp = await this.getJson<GetUserInvoicesResponse[]>(
"GET",
"/getuserinvoices"
);
if ("error" in rsp) {
return rsp as WalletError;
}
return (rsp as GetUserInvoicesResponse[]).map((a) => {
return {
memo: a.description,
amount: Math.floor(a.amt),
timestamp: a.timestamp,
state: a.ispaid ? WalletInvoiceState.Paid : WalletInvoiceState.Pending,
pr: a.payment_request,
paymentHash: a.payment_hash,
} as WalletInvoice;
});
}
private async getJson<T>(
method: "GET" | "POST",
path: string,
body?: any
): Promise<T | WalletError> {
const rsp = await fetch(`${this.url}${path}`, {
method: method,
body: body ? JSON.stringify(body) : undefined,
headers: {
...defaultHeaders,
Authorization: `Bearer ${this.auth?.access_token}`,
},
});
const json = await rsp.json();
if ("error" in json) {
return json as WalletError;
}
return json as T;
}
}
interface AuthResponse {
refresh_token?: string;
access_token?: string;
token_type?: string;
}
interface GetBalanceResponse {
BTC: {
AvailableBalance: number;
};
}
interface GetUserInvoicesResponse {
amt: number;
description: string;
ispaid: boolean;
type: string;
timestamp: number;
pay_req: string;
payment_hash: string;
payment_request: string;
}

View File

@ -0,0 +1,81 @@
import LNDHubWallet from "./LNDHub";
export enum WalletErrorCode {
BadAuth = 1,
NotEnoughBalance = 2,
BadPartner = 3,
InvalidInvoice = 4,
RouteNotFound = 5,
GeneralError = 6,
NodeFailure = 7,
}
export interface WalletError {
code: WalletErrorCode;
message: string;
}
export const UnknownWalletError = {
code: WalletErrorCode.GeneralError,
message: "Unknown error",
} as WalletError;
export interface WalletInfo {
fee: number;
nodePubKey: string;
alias: string;
pendingChannels: number;
activeChannels: number;
peers: number;
blockHeight: number;
blockHash: string;
synced: boolean;
chains: string[];
version: string;
}
export interface Login {
service: string;
save: () => Promise<void>;
load: () => Promise<void>;
}
export interface InvoiceRequest {
amount: number;
memo?: string;
expiry?: number;
}
export enum WalletInvoiceState {
Pending = 0,
Paid = 1,
Expired = 2,
}
export interface WalletInvoice {
pr: string;
paymentHash: string;
memo: string;
amount: number;
fees: number;
timestamp: number;
state: WalletInvoiceState;
}
export type Sats = number;
export interface LNWallet {
createAccount: () => Promise<Login | WalletError>;
getInfo: () => Promise<WalletInfo | WalletError>;
login: () => Promise<boolean | WalletError>;
getBalance: () => Promise<Sats | WalletError>;
createInvoice: (req: InvoiceRequest) => Promise<WalletInvoice | WalletError>;
payInvoice: (pr: string) => Promise<WalletInvoice | WalletError>;
getInvoices: () => Promise<WalletInvoice[] | WalletError>;
}
export async function openWallet(config: string) {
let wallet = new LNDHubWallet(config);
await wallet.login();
return wallet;
}