lightning-backends
Advanced tools
| const assert = require('assert'); | ||
| const HttpLightningBackend = require('../HttpLightningBackend'); | ||
| // https://dev.blink.sv/ | ||
| class Backend extends HttpLightningBackend { | ||
| static name = 'blink'; | ||
| constructor(options) { | ||
| options = options || {}; | ||
| super(Backend.name, options, { | ||
| defaultOptions: { | ||
| baseUrl: null, | ||
| hostname: null, | ||
| protocol: 'https', | ||
| requestContentType: 'json', | ||
| }, | ||
| requiredOptions: ['connectionString'], | ||
| }); | ||
| Object.assign(this.options, this.parseConnectionString(this.options.connectionString)); | ||
| this.options.headers['X-API-KEY'] = encodeURIComponent(this.options.apiKey); | ||
| } | ||
| checkOptions(options) { | ||
| assert.strictEqual(typeof options.connectionString, 'string', 'Invalid option ("connectionString"): String expected'); | ||
| Object.assign(options, this.parseConnectionString(options.connectionString)); | ||
| HttpLightningBackend.prototype.checkOptions.call(this, options); | ||
| } | ||
| parseConnectionString(connectionString) { | ||
| let values = {}; | ||
| connectionString.split(';').forEach(line => { | ||
| const [ key, value ] = line.split('='); | ||
| values[key] = value; | ||
| }); | ||
| const baseUrl = values['server'] || null; | ||
| const apiKey = values['api-key'] || null; | ||
| const walletId = values['wallet-id'] || null; | ||
| try { | ||
| assert.ok(values['type'], 'Missing "type"'); | ||
| assert.strictEqual(values['type'], 'blink', 'Invalid type: Expected "blink"'); | ||
| assert.ok(baseUrl, 'Missing "server"'); | ||
| assert.ok(apiKey, 'Missing "api-key"'); | ||
| assert.ok(walletId, 'Missing "wallet-id"'); | ||
| } catch (error) { | ||
| throw new Error(`Invalid option ("connectionString"): ${error.message}`); | ||
| } | ||
| return { baseUrl, apiKey, walletId }; | ||
| } | ||
| payInvoice(invoice) { | ||
| const query = 'mutation LnInvoicePaymentSend($input: LnInvoicePaymentInput!) { lnInvoicePaymentSend(input: $input) { status errors { message path code } } }'; | ||
| const variables = { | ||
| input: { | ||
| paymentRequest: invoice, | ||
| walletId: this.options.walletId, | ||
| }, | ||
| }; | ||
| return this.doGraphQLQuery(query, variables).then(result => { | ||
| const { preimage } = Object.values(result.data)[0] || {}; | ||
| return { id: null, preimage }; | ||
| }); | ||
| } | ||
| addInvoice(amountMSats, extra) { | ||
| return this.getWalletInfo().then(wallet => { | ||
| const { walletCurrency } = wallet; | ||
| const amount = Math.floor(amountMSats / 1000); | ||
| const { descriptionHash } = extra; | ||
| const recipientWalletId = this.options.walletId; | ||
| let query; | ||
| const variables = { input: { amount, descriptionHash, recipientWalletId } }; | ||
| if (walletCurrency === 'USD') { | ||
| query = 'mutation lnUsdInvoiceBtcDenominatedCreateOnBehalfOfRecipient($input: LnUsdInvoiceBtcDenominatedCreateOnBehalfOfRecipientInput!) { lnUsdInvoiceBtcDenominatedCreateOnBehalfOfRecipient(input: $input) { errors { message } invoice { paymentRequest paymentHash paymentSecret satoshis } } }'; | ||
| } else if (walletCurrency === 'BTC') { | ||
| query = 'mutation lnInvoiceCreateOnBehalfOfRecipient($input: LnInvoiceCreateOnBehalfOfRecipientInput!) { lnInvoiceCreateOnBehalfOfRecipient(input: $input) { errors { message } invoice { paymentRequest paymentHash paymentSecret satoshis } } }'; | ||
| } else { | ||
| throw new Error(`Unsupported Blink wallet currency: "${walletCurrency}"`); | ||
| } | ||
| return this.doGraphQLQuery(query, variables); | ||
| }).then(result => { | ||
| const { paymentRequest } = Object.values(result.data)[0].invoice || {}; | ||
| return { id: null, invoice: paymentRequest }; | ||
| }); | ||
| } | ||
| getInvoiceStatus(paymentHash) { | ||
| const query = 'query lnInvoicePaymentStatusByHash($input: LnInvoicePaymentStatusByHashInput!) { lnInvoicePaymentStatusByHash(input: $input) { paymentHash paymentRequest status } }'; | ||
| const variables = { input: { paymentHash } }; | ||
| return this.doGraphQLQuery(query, variables).then(result => { | ||
| const { status, paymentHash, paymentRequest } = Object.values(result.data)[0] || {}; | ||
| assert.ok(paymentHash && paymentRequest, 'Invoice not found'); | ||
| assert.ok(status, 'Unexpected JSON response'); | ||
| const preimage = null;// This backend doesn't provide the preimage. | ||
| const settled = status === 'PAID'; | ||
| return { preimage, settled }; | ||
| }); | ||
| } | ||
| getBalance() { | ||
| return this.getWalletInfo().then(wallet => { | ||
| return parseInt(wallet.balance) * 1000;// msat | ||
| }); | ||
| } | ||
| getWalletInfo() { | ||
| return this.getWalletInfoById(this.options.walletId); | ||
| } | ||
| getWalletInfoById(walletId) { | ||
| return Promise.resolve().then(() => { | ||
| assert.ok(walletId, 'Missing required argument: "walletId"'); | ||
| const query = 'query me { me { defaultAccount { wallets { id walletCurrency balance }}}}'; | ||
| const variables = {}; | ||
| return this.doGraphQLQuery(query, variables).then(result => { | ||
| const wallets = result.data.me && result.data.me.defaultAccount && result.data.me.defaultAccount.wallets || []; | ||
| assert.ok(wallets, 'Unexpected JSON response'); | ||
| const wallet = wallets.find(_wallet => _wallet.id === walletId); | ||
| assert.ok(wallet, 'Wallet info not found'); | ||
| return wallet; | ||
| }); | ||
| }); | ||
| } | ||
| getNodeUri() { | ||
| return Promise.reject(new Error('Not supported by this LN service.')); | ||
| } | ||
| openChannel(remoteId, localAmt, pushAmt, makePrivate) { | ||
| return Promise.reject(new Error('Not supported by this LN service.')); | ||
| } | ||
| doGraphQLQuery(query, variables) { | ||
| return this.request('post', '', { query, variables }).then(result => { | ||
| assert.ok(result && result.data, 'Unexpected JSON response: ' + JSON.stringify(result)); | ||
| const { errors } = Object.values(result.data)[0] || {}; | ||
| assert.ok(!errors || errors.length === 0, JSON.stringify(errors)); | ||
| return result; | ||
| }); | ||
| } | ||
| }; | ||
| Backend.prototype.checkMethodErrorMessages = { | ||
| payInvoice: { | ||
| ok: [ | ||
| 'Unable to find a route for payment.', | ||
| 'ROUTE_FINDING_ERROR', | ||
| 'INSUFFICIENT_BALANCE', | ||
| ], | ||
| notOk: [ | ||
| 'Authorization Required', | ||
| ], | ||
| }, | ||
| }; | ||
| Backend.form = { | ||
| label: 'Blink', | ||
| inputs: [ | ||
| { | ||
| name: 'connectionString', | ||
| label: 'BTCPay Connection String', | ||
| help: 'Sign-in to your Blink account in a browser. Go to API Keys. Click the plus symbol (+) in the upper right corner to create a new API key. Copy the BTCPay connection string for your wallet.', | ||
| type: 'password', | ||
| placeholder: 'xxx', | ||
| default: '', | ||
| required: true, | ||
| }, | ||
| ], | ||
| }; | ||
| module.exports = Backend; |
+4
-0
| # Changelog | ||
| * v1.7.0: | ||
| * Added Blink backend | ||
| * Upgrade dependencies | ||
| * LNBits: insufficient balance ok error when checking if credentials work | ||
| * v1.6.5: | ||
@@ -4,0 +8,0 @@ * Fix (again) GetAlby backend |
@@ -11,2 +11,3 @@ const assert = require('assert'); | ||
| constructor(options) { | ||
| options = options || {}; | ||
| super(Backend.name, options, { | ||
@@ -24,2 +25,3 @@ defaultOptions: { | ||
| this.requestCounters = {}; | ||
| this.options.preimage = this.normalizePreimage(options.preimage); | ||
| } | ||
@@ -104,2 +106,9 @@ | ||
| normalizePreimage(preimage) { | ||
| if (preimage && typeof preimage === 'object' && preimage.type === 'Buffer' && preimage.data) { | ||
| preimage = Buffer.from(preimage.data); | ||
| } | ||
| return preimage; | ||
| } | ||
| getBalance() { | ||
@@ -106,0 +115,0 @@ return Promise.resolve(this.options.balance); |
@@ -86,6 +86,6 @@ const assert = require('assert'); | ||
| 'is not reachable directly and all routehints were unusable', | ||
| 'Insufficient balance', | ||
| ], | ||
| notOk: [ | ||
| 'Socks5 proxy rejected connection - Failure', | ||
| 'Insufficient balance', | ||
| 'Wallet does not exist', | ||
@@ -92,0 +92,0 @@ ], |
@@ -36,3 +36,3 @@ const assert = require('assert'); | ||
| let paymentSecret; | ||
| if (backend === 'lnbits') { | ||
| if (['blink', 'lnbits'].includes(backend)) { | ||
| // !! IMPORTANT !! | ||
@@ -39,0 +39,0 @@ // "payment_secret" must be 32 bytes. |
@@ -15,3 +15,3 @@ const assert = require('assert'); | ||
| this.checkOptions && this.checkOptions(options); | ||
| this.options = options; | ||
| this.options = JSON.parse(JSON.stringify(options || {})); | ||
| this.prepareCheckMethodErrorRegEx(); | ||
@@ -18,0 +18,0 @@ } |
+4
-4
| { | ||
| "name": "lightning-backends", | ||
| "version": "1.6.5", | ||
| "version": "1.7.0", | ||
| "description": "Module to integrate with various Lightning Network node software and service providers", | ||
@@ -46,11 +46,11 @@ "keywords": [ | ||
| "@bleskomat/form": "1.3.2", | ||
| "async": "3.2.5", | ||
| "async": "3.2.6", | ||
| "bolt11": "1.4.1", | ||
| "secp256k1": "5.0.0", | ||
| "socks-proxy-agent": "8.0.3" | ||
| "socks-proxy-agent": "8.0.4" | ||
| }, | ||
| "devDependencies": { | ||
| "dotenv": "16.4.5", | ||
| "mocha": "10.4.0" | ||
| "mocha": "10.7.3" | ||
| } | ||
| } |
+5
-0
@@ -23,2 +23,3 @@ # lightning-backends | ||
| * [LNBits](https://github.com/lnbits/lnbits) | ||
| * [Blink](https://dev.blink.sv/) | ||
| * [GetAlby](https://github.com/getAlby/lndhub.go) | ||
@@ -140,2 +141,6 @@ * [LndHub](https://github.com/BlueWallet/LndHub) - first implemented by BlueWallet, but now used as a standard interface by other wallets and services | ||
| Blink: | ||
| * __connectionString__ - Contains the server URL, account API key, and wallet ID. Example: | ||
| * `type=blink;server=https://api.blink.sv/graphql;api-key=blink_XXX;wallet-id=xxx-yyyy-zzzz-0000-xyz123` | ||
| LNBits: | ||
@@ -142,0 +147,0 @@ * __baseUrl__ - The URL of the LNBits instance. Example: |
Network access
Supply chain riskThis module accesses the network.
Found 4 instances in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 4 instances in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
83397
9%26
4%1985
8.95%257
1.98%+ Added
+ Added
- Removed
- Removed
Updated
Updated