feat: basic wallet
This commit is contained in:
134
packages/app/src/Wallet/LNDHub.ts
Normal file
134
packages/app/src/Wallet/LNDHub.ts
Normal 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;
|
||||
}
|
81
packages/app/src/Wallet/index.ts
Normal file
81
packages/app/src/Wallet/index.ts
Normal 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;
|
||||
}
|
Reference in New Issue
Block a user