From 9cb04379d3807c69d000db240566df39ba33c297 Mon Sep 17 00:00:00 2001 From: Divyank Aggarwal Date: Thu, 1 Dec 2022 23:04:41 +0530 Subject: [PATCH] Added an interface for webln --- dist/index.js | 4 +- dist/lib.js | 30 ----- dist/lib_webln.d.ts | 17 +++ dist/lib_webln.js | 274 ++++++++++++++++++++++++++++++++++++++++++++ package.json | 3 +- src/index.ts | 4 +- src/lib_webln.ts | 162 ++++++++++++++++++++++++++ 7 files changed, 461 insertions(+), 33 deletions(-) create mode 100644 dist/lib_webln.d.ts create mode 100644 dist/lib_webln.js create mode 100644 src/lib_webln.ts diff --git a/dist/index.js b/dist/index.js index d3b8d66..27bbad3 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,5 +1,7 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var lib_1 = require("./lib"); +var lib_webln_1 = require("./lib_webln"); var sphinx = new lib_1.default(); -module.exports = sphinx; +var webLn = new lib_webln_1.default(); +module.exports = { sphinx: sphinx, webLn: webLn }; diff --git a/dist/lib.js b/dist/lib.js index 3da0914..3528387 100644 --- a/dist/lib.js +++ b/dist/lib.js @@ -453,36 +453,6 @@ var Sphinx = /** @class */ (function () { }); }); }; - Sphinx.prototype.verifyMessage = function (signature, message) { - return __awaiter(this, void 0, void 0, function () { - var r, e_12; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - if (this.logging) - console.log("=> VERIFY MESSAGE"); - if (!this.isEnabled) - return [2 /*return*/, null]; - _a.label = 1; - case 1: - _a.trys.push([1, 3, , 4]); - return [4 /*yield*/, this.postMsg(MSG_TYPE.SIGN, { - signature: signature, - message: message, - })]; - case 2: - r = _a.sent(); - return [2 /*return*/, r]; - case 3: - e_12 = _a.sent(); - if (this.logging) - console.log(e_12); - return [2 /*return*/, null]; - case 4: return [2 /*return*/]; - } - }); - }); - }; Sphinx.prototype.reload = function (password) { return __awaiter(this, void 0, void 0, function () { var r, hasBudget, e_13; diff --git a/dist/lib_webln.d.ts b/dist/lib_webln.d.ts new file mode 100644 index 0000000..d5cada2 --- /dev/null +++ b/dist/lib_webln.d.ts @@ -0,0 +1,17 @@ +import { GetInfoResponse, KeysendArgs, SendPaymentResponse, WebLNProvider, RequestInvoiceArgs, RequestInvoiceResponse, SignMessageResponse } from "webln"; +export default class Sphinx_webln implements WebLNProvider { + private isEnabled; + private active; + private budget; + private pubkey; + private alias; + private logging; + enable(): Promise; + getInfo(): Promise; + sendPayment(paymentRequest: string): Promise; + keysend(args: KeysendArgs): Promise; + makeInvoice(args: string | number | RequestInvoiceArgs): Promise; + signMessage(message: string): Promise; + verifyMessage(signature: string, message: string): Promise; + private postMsg; +} diff --git a/dist/lib_webln.js b/dist/lib_webln.js new file mode 100644 index 0000000..30d0a55 --- /dev/null +++ b/dist/lib_webln.js @@ -0,0 +1,274 @@ +"use strict"; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var postMessage_1 = require("./postMessage"); +var lib_1 = require("./lib"); +var webln_1 = require("webln"); +var APP_NAME = "Sphinx"; +var Sphinx_webln = /** @class */ (function () { + function Sphinx_webln() { + this.isEnabled = false; + this.active = null; + this.budget = 0; + this.pubkey = ""; + this.alias = "User"; + this.logging = false; + } + Sphinx_webln.prototype.enable = function () { + return __awaiter(this, void 0, void 0, function () { + var r, hasBudget, e_1; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (this.logging) + console.log("=> ENABLE!"); + if (this.isEnabled) { + return [2 /*return*/]; + } + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + return [4 /*yield*/, this.postMsg(lib_1.MSG_TYPE.AUTHORIZE)]; + case 2: + r = _a.sent(); + hasBudget = r.budget || r.budget === 0; + if (hasBudget && r.pubkey) { + this.isEnabled = true; + this.budget = r.budget; + this.pubkey = r.pubkey; + return [2 /*return*/]; + } + else { + throw Error("Budget or pubkey not detected"); + } + return [3 /*break*/, 4]; + case 3: + e_1 = _a.sent(); + if (this.logging) + console.log(e_1); + return [2 /*return*/, Promise.reject(e_1)]; + case 4: return [2 /*return*/]; + } + }); + }); + }; + Sphinx_webln.prototype.getInfo = function () { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + if (!this.isEnabled) { + return [2 /*return*/, Promise.reject("WebLn is not enabled")]; + } + else { + return [2 /*return*/, { + node: { + alias: this.alias, + pubkey: this.pubkey + } + }]; + } + return [2 /*return*/]; + }); + }); + }; + Sphinx_webln.prototype.sendPayment = function (paymentRequest) { + return __awaiter(this, void 0, void 0, function () { + var r, e_2; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (this.logging) + console.log("=> SEND PAYMENT"); + if (!this.isEnabled) + Promise.reject("WebLn is not enabled"); + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + return [4 /*yield*/, this.postMsg(lib_1.MSG_TYPE.PAYMENT, { paymentRequest: paymentRequest })]; + case 2: + r = _a.sent(); + return [2 /*return*/, { + preimage: r.preimage + }]; + case 3: + e_2 = _a.sent(); + if (this.logging) + console.log(e_2); + return [2 /*return*/, Promise.reject("Payment failed for payment request: ${payment_request}")]; + case 4: return [2 /*return*/]; + } + }); + }); + }; + Sphinx_webln.prototype.keysend = function (args) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + throw new webln_1.UnsupportedMethodError('Kesend is not supported yet!'); + }); + }); + }; + Sphinx_webln.prototype.makeInvoice = function (args) { + return __awaiter(this, void 0, void 0, function () { + var arg, r, e_3; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (this.logging) + console.log("=> MAKE INVOICE"); + if (!this.isEnabled) + return [2 /*return*/, Promise.reject("WebLn is not enabled")]; + arg = args; + if (!arg.amount || !arg.defaultMemo) { + return [2 /*return*/, Promise.reject("WebLn makeInvoice: Incorrect arguments")]; + } + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + return [4 /*yield*/, this.postMsg(lib_1.MSG_TYPE.INVOICE, { + amt: +arg.amount, + memo: arg.defaultMemo, + })]; + case 2: + r = _a.sent(); + return [2 /*return*/, r]; + case 3: + e_3 = _a.sent(); + if (this.logging) + console.log(e_3); + return [2 /*return*/, Promise.reject(e_3)]; + case 4: return [2 /*return*/]; + } + }); + }); + }; + Sphinx_webln.prototype.signMessage = function (message) { + return __awaiter(this, void 0, void 0, function () { + var r, e_4; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (this.logging) + console.log("=> SIGN MESSAGE"); + if (!this.isEnabled) + return [2 /*return*/, Promise.reject("WebLn is not enabled")]; + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + return [4 /*yield*/, this.postMsg(lib_1.MSG_TYPE.SIGN, { message: message })]; + case 2: + r = _a.sent(); + return [2 /*return*/, { signature: r.signature, message: message }]; + case 3: + e_4 = _a.sent(); + if (this.logging) + console.log(e_4); + return [2 /*return*/, Promise.reject(e_4)]; + case 4: return [2 /*return*/]; + } + }); + }); + }; + Sphinx_webln.prototype.verifyMessage = function (signature, message) { + return __awaiter(this, void 0, void 0, function () { + var r, e_5; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (this.logging) + console.log("=> VERIFY MESSAGE"); + if (!this.isEnabled) + return [2 /*return*/, Promise.reject("WebLn is not enabled")]; + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + return [4 /*yield*/, this.postMsg(lib_1.MSG_TYPE.SIGN, { + signature: signature, + message: message, + })]; + case 2: + r = _a.sent(); + return [3 /*break*/, 4]; + case 3: + e_5 = _a.sent(); + if (this.logging) + console.log(e_5); + return [2 /*return*/, Promise.reject(e_5)]; + case 4: return [2 /*return*/]; + } + }); + }); + }; + // Internal prompt handler + Sphinx_webln.prototype.postMsg = function (type, args) { + var self = this; + if (self.active) { + Promise.reject(new Error("User is busy")); + } + self.active = type; + return new Promise(function (resolve, reject) { + postMessage_1.postMessage(__assign({ application: APP_NAME, type: type }, (args || {}))); + function handleWindowMessage(ev) { + if (!ev.data || ev.data.application !== APP_NAME) { + return; + } + if (ev.data.error) { + self.active = null; + reject(ev.data.error); + } + else { + self.active = null; + resolve(ev.data); + } + postMessage_1.removeEventer(handleWindowMessage); + } + postMessage_1.addEventer(handleWindowMessage); + }); + }; + return Sphinx_webln; +}()); +exports.default = Sphinx_webln; diff --git a/package.json b/package.json index c15741c..6014f1e 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "webpack-cli": "^3.3.12" }, "dependencies": { - "invoices": "^2.1.0" + "invoices": "^2.1.0", + "webln": "^0.3.0" } } diff --git a/src/index.ts b/src/index.ts index ab94124..89b729e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,7 @@ import Sphinx from './lib' +import Sphinx_webln from './lib_webln' const sphinx = new Sphinx() +const webLn = new Sphinx_webln() -module.exports = sphinx \ No newline at end of file +module.exports = {sphinx,webLn}; \ No newline at end of file diff --git a/src/lib_webln.ts b/src/lib_webln.ts new file mode 100644 index 0000000..bd3343b --- /dev/null +++ b/src/lib_webln.ts @@ -0,0 +1,162 @@ + +import { postMessage, addEventer, removeEventer } from "./postMessage"; + // @ts-ignore +import * as invoices from "invoices"; +import { MSG_TYPE } from "./lib"; +import { GetInfoResponse, KeysendArgs, SendPaymentResponse, WebLNProvider, UnsupportedMethodError, RequestInvoiceArgs, RequestInvoiceResponse, SignMessageResponse } from "webln"; +import {VerifyMessageArgs, InvoiceArgs, SignMessageRes, SignMessageArgs} from "./provider"; +import { EnableRes, InvoiceRes, SendPaymentRes } from "./provider"; + + + + const APP_NAME = "Sphinx"; + + export default class Sphinx_webln implements WebLNProvider { + private isEnabled: boolean = false; + private active: MSG_TYPE | null = null; + private budget: number = 0; + private pubkey: string = ""; + private alias: string = "User" + private logging: boolean = false; + + async enable() { + if (this.logging) console.log("=> ENABLE!"); + if (this.isEnabled) { + return ; + } + try { + const r = await this.postMsg(MSG_TYPE.AUTHORIZE); + const hasBudget = r.budget || r.budget === 0; + if (hasBudget && r.pubkey) { + this.isEnabled = true; + this.budget = r.budget; + this.pubkey = r.pubkey; + return; + } else { + throw Error("Budget or pubkey not detected"); + } + } catch (e) { + if (this.logging) console.log(e); + return Promise.reject(e); + } + } + + async getInfo(): Promise { + if (!this.isEnabled) { + return Promise.reject("WebLn is not enabled") + } else { + return { + node: { + alias: this.alias, + pubkey: this.pubkey + } + } + } + } + + + async sendPayment(paymentRequest: string): Promise { + if (this.logging) console.log("=> SEND PAYMENT"); + if (!this.isEnabled) Promise.reject("WebLn is not enabled"); + try { + const r = await this.postMsg( + MSG_TYPE.PAYMENT, + { paymentRequest } + ); + return { + preimage: r.preimage + } + } catch (e) { + if (this.logging) console.log(e); + return Promise.reject("Payment failed for payment request: ${payment_request}") + } + } + + async keysend(args: KeysendArgs): Promise { + throw new UnsupportedMethodError('Kesend is not supported yet!') + } + + + async makeInvoice(args: string | number | RequestInvoiceArgs): Promise { + if (this.logging) console.log("=> MAKE INVOICE"); + if (!this.isEnabled) return Promise.reject("WebLn is not enabled"); + const arg = args as RequestInvoiceArgs; + if (!arg.amount || !arg.defaultMemo) { + return Promise.reject("WebLn makeInvoice: Incorrect arguments") + } + try { + const r = await this.postMsg(MSG_TYPE.INVOICE, { + amt: + arg.amount, + memo: arg.defaultMemo, + }); + return r; + } catch (e) { + if (this.logging) console.log(e); + return Promise.reject(e); + } + } + + async signMessage(message: string): Promise { + if (this.logging) console.log("=> SIGN MESSAGE"); + if (!this.isEnabled) return Promise.reject("WebLn is not enabled"); + try { + const r = await this.postMsg( + MSG_TYPE.SIGN, + { message } + ); + return {signature: r.signature, message} + } catch (e) { + if (this.logging) console.log(e); + return Promise.reject(e) + } + } + + async verifyMessage(signature: string, message: string) { + if (this.logging) console.log("=> VERIFY MESSAGE"); + if (!this.isEnabled) return Promise.reject("WebLn is not enabled") + try { + const r = await this.postMsg(MSG_TYPE.SIGN, { + signature, + message, + }); + } catch (e) { + if (this.logging) console.log(e); + return Promise.reject(e) + } + } + + // Internal prompt handler + private postMsg( + type: MSG_TYPE, + args?: T + ): Promise { + var self = this; + if (self.active) { + Promise.reject(new Error("User is busy")); + } + self.active = type; + return new Promise((resolve, reject) => { + postMessage({ + application: APP_NAME, + type, + ...(args || {}), + }); + function handleWindowMessage(ev: MessageEvent) { + if (!ev.data || ev.data.application !== APP_NAME) { + return; + } + if (ev.data.error) { + self.active = null; + reject(ev.data.error); + } else { + self.active = null; + resolve(ev.data); + } + removeEventer(handleWindowMessage); + } + + addEventer(handleWindowMessage); + }); + } + } + \ No newline at end of file