2018-07-15 21:56:28 +02:00
import { LegacyWallet } from './legacy-wallet' ;
import Frisbee from 'frisbee' ;
2019-12-21 20:44:35 +01:00
import bolt11 from 'bolt11' ;
2020-05-24 12:27:08 +02:00
import { BitcoinUnit , Chain } from '../../models/bitcoinUnits' ;
2023-10-29 16:48:54 +01:00
2018-07-15 21:56:28 +02:00
export class LightningCustodianWallet extends LegacyWallet {
2018-12-28 16:52:06 +01:00
static type = 'lightningCustodianWallet' ;
static typeReadable = 'Lightning' ;
2021-07-08 19:39:03 +02:00
2019-01-26 00:32:35 +01:00
constructor ( props ) {
super ( props ) ;
2018-12-11 23:52:46 +01:00
this . setBaseURI ( ) ; // no args to init with default value
2018-09-01 01:28:19 +02:00
this . init ( ) ;
this . refresh _token = '' ;
this . access _token = '' ;
this . _refresh _token _created _ts = 0 ;
this . _access _token _created _ts = 0 ;
this . refill _addressess = [ ] ;
this . pending _transactions _raw = [ ] ;
2019-01-10 18:50:33 +01:00
this . user _invoices _raw = [ ] ;
2018-09-01 01:28:19 +02:00
this . info _raw = false ;
2018-12-18 06:58:49 +01:00
this . preferredBalanceUnit = BitcoinUnit . SATS ;
2019-02-26 02:33:29 +01:00
this . chain = Chain . OFFCHAIN ;
2018-09-01 01:28:19 +02:00
}
2018-11-04 22:21:07 +01:00
/ * *
* requires calling init ( ) after setting
*
* @ param URI
* /
setBaseURI ( URI ) {
2021-07-08 19:39:03 +02:00
this . baseURI = URI ;
2018-11-04 22:21:07 +01:00
}
getBaseURI ( ) {
return this . baseURI ;
}
2018-10-09 06:25:36 +02:00
allowSend ( ) {
2019-02-19 01:37:34 +01:00
return true ;
2018-10-09 06:25:36 +02:00
}
2018-09-01 01:28:19 +02:00
getAddress ( ) {
2019-10-01 00:13:22 +02:00
if ( this . refill _addressess . length > 0 ) {
return this . refill _addressess [ 0 ] ;
} else {
return undefined ;
}
2018-09-01 01:28:19 +02:00
}
2019-03-08 22:54:47 +01:00
getSecret ( ) {
return this . secret + '@' + this . baseURI ;
}
2018-09-01 01:28:19 +02:00
timeToRefreshBalance ( ) {
2019-06-01 22:45:01 +02:00
return ( + new Date ( ) - this . _lastBalanceFetch ) / 1000 > 300 ; // 5 min
2018-09-01 01:28:19 +02:00
}
timeToRefreshTransaction ( ) {
2019-06-01 22:45:01 +02:00
return ( + new Date ( ) - this . _lastTxFetch ) / 1000 > 300 ; // 5 min
2018-09-01 01:28:19 +02:00
}
static fromJson ( param ) {
2020-06-01 14:54:23 +02:00
const obj = super . fromJson ( param ) ;
2018-09-01 01:28:19 +02:00
obj . init ( ) ;
return obj ;
}
2021-10-04 04:53:50 +02:00
async init ( ) {
2023-06-10 16:19:14 +02:00
// un-cache refill onchain addresses on cold start. should help for cases when certain lndhub
// is turned off permanently, so users cant pull refill address from cache and send money to a black hole
this . refill _addressess = [ ] ;
2018-07-15 21:56:28 +02:00
this . _api = new Frisbee ( {
2018-11-04 22:21:07 +01:00
baseURI : this . baseURI ,
2018-07-15 21:56:28 +02:00
} ) ;
}
2018-09-01 01:28:19 +02:00
accessTokenExpired ( ) {
return ( + new Date ( ) - this . _access _token _created _ts ) / 1000 >= 3600 * 2 ; // 2h
}
refreshTokenExpired ( ) {
return ( + new Date ( ) - this . _refresh _token _created _ts ) / 1000 >= 3600 * 24 * 7 ; // 7d
}
generate ( ) {
// nop
}
2018-09-04 00:44:45 +02:00
async createAccount ( isTest ) {
2020-06-01 14:54:23 +02:00
const response = await this . _api . post ( '/create' , {
2018-09-04 00:44:45 +02:00
body : { partnerid : 'bluewallet' , accounttype : ( isTest && 'test' ) || 'common' } ,
2018-09-01 01:28:19 +02:00
headers : { 'Access-Control-Allow-Origin' : '*' , 'Content-Type' : 'application/json' } ,
} ) ;
2020-06-01 14:54:23 +02:00
const json = response . body ;
2018-09-01 01:28:19 +02:00
if ( typeof json === 'undefined' ) {
throw new Error ( 'API failure: ' + response . err + ' ' + JSON . stringify ( response . body ) ) ;
}
2018-07-15 21:56:28 +02:00
2018-09-01 01:28:19 +02:00
if ( json && json . error ) {
2018-12-11 23:52:46 +01:00
throw new Error ( 'API error: ' + ( json . message ? json . message : json . error ) + ' (code ' + json . code + ')' ) ;
2018-09-01 01:28:19 +02:00
}
2018-07-15 21:56:28 +02:00
2018-09-01 01:28:19 +02:00
if ( ! json . login || ! json . password ) {
throw new Error ( 'API unexpected response: ' + JSON . stringify ( response . body ) ) ;
}
2018-07-15 21:56:28 +02:00
2018-12-11 23:52:46 +01:00
this . secret = 'lndhub://' + json . login + ':' + json . password ;
2018-09-01 01:28:19 +02:00
}
2018-07-15 21:56:28 +02:00
2019-01-05 17:29:13 +01:00
async payInvoice ( invoice , freeAmount = 0 ) {
2020-06-01 14:54:23 +02:00
const response = await this . _api . post ( '/payinvoice' , {
2022-10-31 13:25:26 +01:00
body : { invoice , amount : freeAmount } ,
2018-09-01 01:28:19 +02:00
headers : {
'Access-Control-Allow-Origin' : '*' ,
'Content-Type' : 'application/json' ,
Authorization : 'Bearer' + ' ' + this . access _token ,
} ,
} ) ;
2019-02-14 00:19:59 +01:00
if ( response . originalResponse && typeof response . originalResponse === 'string' ) {
try {
response . originalResponse = JSON . parse ( response . originalResponse ) ;
} catch ( _ ) { }
}
if ( response . originalResponse && response . originalResponse . status && response . originalResponse . status === 503 ) {
throw new Error ( 'Payment is in transit' ) ;
}
2020-06-01 14:54:23 +02:00
const json = response . body ;
2018-09-01 01:28:19 +02:00
if ( typeof json === 'undefined' ) {
throw new Error ( 'API failure: ' + response . err + ' ' + JSON . stringify ( response . originalResponse ) ) ;
}
2018-07-15 21:56:28 +02:00
2018-09-01 01:28:19 +02:00
if ( json && json . error ) {
throw new Error ( 'API error: ' + json . message + ' (code ' + json . code + ')' ) ;
}
2018-07-15 21:56:28 +02:00
2018-09-01 01:28:19 +02:00
this . last _paid _invoice _result = json ;
}
2018-09-04 00:44:45 +02:00
/ * *
* Returns list of LND invoices created by user
*
* @ return { Promise . < Array > }
* /
2019-01-10 18:50:33 +01:00
async getUserInvoices ( limit = false ) {
let limitString = '' ;
2023-07-25 15:50:04 +02:00
if ( limit ) limitString = '?limit=' + parseInt ( limit , 10 ) ;
2020-06-01 14:54:23 +02:00
const response = await this . _api . get ( '/getuserinvoices' + limitString , {
2018-09-04 00:44:45 +02:00
headers : {
'Access-Control-Allow-Origin' : '*' ,
'Content-Type' : 'application/json' ,
Authorization : 'Bearer' + ' ' + this . access _token ,
} ,
} ) ;
2020-06-01 14:54:23 +02:00
const json = response . body ;
2018-09-04 00:44:45 +02:00
if ( typeof json === 'undefined' ) {
throw new Error ( 'API failure: ' + response . err + ' ' + JSON . stringify ( response . originalResponse ) ) ;
}
if ( json && json . error ) {
throw new Error ( 'API error: ' + json . message + ' (code ' + json . code + ')' ) ;
}
2018-12-25 20:07:43 +01:00
2019-01-10 18:50:33 +01:00
if ( limit ) {
// need to merge existing invoices with the ones that arrived
// but the ones received later should overwrite older ones
2020-06-01 14:54:23 +02:00
for ( const oldInvoice of this . user _invoices _raw ) {
2019-01-10 18:50:33 +01:00
// iterate all OLD invoices
let found = false ;
2020-06-01 14:54:23 +02:00
for ( const newInvoice of json ) {
2019-01-10 18:50:33 +01:00
// iterate all NEW invoices
if ( newInvoice . payment _request === oldInvoice . payment _request ) found = true ;
}
if ( ! found ) {
// if old invoice is not found in NEW array, we simply add it:
json . push ( oldInvoice ) ;
}
}
}
2020-06-01 14:54:23 +02:00
this . user _invoices _raw = json . sort ( function ( a , b ) {
2019-01-10 18:50:33 +01:00
return a . timestamp - b . timestamp ;
} ) ;
return this . user _invoices _raw ;
2018-09-04 00:44:45 +02:00
}
2018-12-25 20:07:43 +01:00
/ * *
* Basically the same as this . getUserInvoices ( ) but saves invoices list
* to internal variable
*
* @ returns { Promise < void > }
* /
async fetchUserInvoices ( ) {
await this . getUserInvoices ( ) ;
}
2019-08-25 03:14:26 +02:00
isInvoiceGeneratedByWallet ( paymentRequest ) {
return this . user _invoices _raw . some ( invoice => invoice . payment _request === paymentRequest ) ;
}
2020-03-30 20:13:48 +02:00
weOwnAddress ( address ) {
return this . refill _addressess . some ( refillAddress => address === refillAddress ) ;
}
2018-09-04 00:44:45 +02:00
async addInvoice ( amt , memo ) {
2020-06-01 14:54:23 +02:00
const response = await this . _api . post ( '/addinvoice' , {
2022-10-31 13:25:26 +01:00
body : { amt : amt + '' , memo } ,
2018-09-04 00:44:45 +02:00
headers : {
'Access-Control-Allow-Origin' : '*' ,
'Content-Type' : 'application/json' ,
Authorization : 'Bearer' + ' ' + this . access _token ,
} ,
} ) ;
2020-06-01 14:54:23 +02:00
const json = response . body ;
2018-09-04 00:44:45 +02:00
if ( typeof json === 'undefined' ) {
throw new Error ( 'API failure: ' + response . err + ' ' + JSON . stringify ( response . originalResponse ) ) ;
}
if ( json && json . error ) {
throw new Error ( 'API error: ' + json . message + ' (code ' + json . code + ')' ) ;
}
2018-09-22 13:40:47 +02:00
if ( ! json . r _hash || ! json . pay _req ) {
throw new Error ( 'API unexpected response: ' + JSON . stringify ( response . body ) ) ;
}
return json . pay _req ;
2018-09-04 00:44:45 +02:00
}
2018-09-01 01:28:19 +02:00
/ * *
* Uses login & pass stored in ` this.secret ` to authorize
* and set internal ` access_token ` & ` refresh_token `
*
* @ return { Promise . < void > }
* /
async authorize ( ) {
2018-12-11 23:52:46 +01:00
let login , password ;
if ( this . secret . indexOf ( 'blitzhub://' ) !== - 1 ) {
login = this . secret . replace ( 'blitzhub://' , '' ) . split ( ':' ) [ 0 ] ;
password = this . secret . replace ( 'blitzhub://' , '' ) . split ( ':' ) [ 1 ] ;
} else {
login = this . secret . replace ( 'lndhub://' , '' ) . split ( ':' ) [ 0 ] ;
password = this . secret . replace ( 'lndhub://' , '' ) . split ( ':' ) [ 1 ] ;
}
2020-06-01 14:54:23 +02:00
const response = await this . _api . post ( '/auth?type=auth' , {
2022-10-31 13:25:26 +01:00
body : { login , password } ,
2018-09-01 01:28:19 +02:00
headers : { 'Access-Control-Allow-Origin' : '*' , 'Content-Type' : 'application/json' } ,
} ) ;
2020-06-01 14:54:23 +02:00
const json = response . body ;
2018-09-01 01:28:19 +02:00
if ( typeof json === 'undefined' ) {
throw new Error ( 'API failure: ' + response . err + ' ' + JSON . stringify ( response . body ) ) ;
}
if ( json && json . error ) {
throw new Error ( 'API error: ' + json . message + ' (code ' + json . code + ')' ) ;
}
if ( ! json . access _token || ! json . refresh _token ) {
throw new Error ( 'API unexpected response: ' + JSON . stringify ( response . body ) ) ;
}
this . refresh _token = json . refresh _token ;
this . access _token = json . access _token ;
this . _refresh _token _created _ts = + new Date ( ) ;
this . _access _token _created _ts = + new Date ( ) ;
}
async checkLogin ( ) {
if ( this . accessTokenExpired ( ) && this . refreshTokenExpired ( ) ) {
// all tokens expired, only option is to login with login and password
return this . authorize ( ) ;
}
if ( this . accessTokenExpired ( ) ) {
// only access token expired, so only refreshing it
let refreshedOk = true ;
try {
await this . refreshAcessToken ( ) ;
} catch ( Err ) {
refreshedOk = false ;
}
if ( ! refreshedOk ) {
// something went wrong, lets try to login regularly
return this . authorize ( ) ;
}
}
}
async refreshAcessToken ( ) {
2020-06-01 14:54:23 +02:00
const response = await this . _api . post ( '/auth?type=refresh_token' , {
2018-09-01 01:28:19 +02:00
body : { refresh _token : this . refresh _token } ,
headers : { 'Access-Control-Allow-Origin' : '*' , 'Content-Type' : 'application/json' } ,
} ) ;
2020-06-01 14:54:23 +02:00
const json = response . body ;
2018-09-01 01:28:19 +02:00
if ( typeof json === 'undefined' ) {
throw new Error ( 'API failure: ' + response . err + ' ' + JSON . stringify ( response . body ) ) ;
}
if ( json && json . error ) {
throw new Error ( 'API error: ' + json . message + ' (code ' + json . code + ')' ) ;
}
if ( ! json . access _token || ! json . refresh _token ) {
throw new Error ( 'API unexpected response: ' + JSON . stringify ( response . body ) ) ;
}
this . refresh _token = json . refresh _token ;
this . access _token = json . access _token ;
this . _refresh _token _created _ts = + new Date ( ) ;
this . _access _token _created _ts = + new Date ( ) ;
}
async fetchBtcAddress ( ) {
2020-06-01 14:54:23 +02:00
const response = await this . _api . get ( '/getbtc' , {
2018-09-01 01:28:19 +02:00
headers : {
'Access-Control-Allow-Origin' : '*' ,
'Content-Type' : 'application/json' ,
Authorization : 'Bearer' + ' ' + this . access _token ,
} ,
} ) ;
2020-06-01 14:54:23 +02:00
const json = response . body ;
2018-09-01 01:28:19 +02:00
if ( typeof json === 'undefined' ) {
throw new Error ( 'API failure: ' + response . err + ' ' + JSON . stringify ( response . body ) ) ;
}
if ( json && json . error ) {
throw new Error ( 'API error: ' + json . message + ' (code ' + json . code + ')' ) ;
}
this . refill _addressess = [ ] ;
2020-06-01 14:54:23 +02:00
for ( const arr of json ) {
2018-09-01 01:28:19 +02:00
this . refill _addressess . push ( arr . address ) ;
}
}
2018-07-15 21:56:28 +02:00
2019-10-01 00:13:22 +02:00
async getAddressAsync ( ) {
2020-04-17 16:41:35 +02:00
await this . fetchBtcAddress ( ) ;
return this . getAddress ( ) ;
2019-10-01 00:13:22 +02:00
}
2019-12-14 07:52:14 +01:00
async allowOnchainAddress ( ) {
2021-09-30 18:05:51 +02:00
if ( this . getAddress ( ) !== undefined && this . getAddress ( ) !== null ) {
2019-12-14 07:52:14 +01:00
return true ;
} else {
await this . fetchBtcAddress ( ) ;
2021-09-30 18:05:51 +02:00
return this . getAddress ( ) !== undefined && this . getAddress ( ) !== null ;
2019-12-14 07:52:14 +01:00
}
}
2018-08-19 16:54:15 +02:00
getTransactions ( ) {
2018-09-01 01:28:19 +02:00
let txs = [ ] ;
this . pending _transactions _raw = this . pending _transactions _raw || [ ] ;
2018-12-25 20:07:43 +01:00
this . user _invoices _raw = this . user _invoices _raw || [ ] ;
2018-09-01 01:28:19 +02:00
this . transactions _raw = this . transactions _raw || [ ] ;
2018-12-25 20:07:43 +01:00
txs = txs . concat ( this . pending _transactions _raw . slice ( ) , this . transactions _raw . slice ( ) . reverse ( ) , this . user _invoices _raw . slice ( ) ) ; // slice so array is cloned
2018-09-01 01:28:19 +02:00
// transforming to how wallets/list screen expects it
2020-06-01 14:54:23 +02:00
for ( const tx of txs ) {
2021-06-17 23:24:00 +02:00
tx . walletID = this . getID ( ) ;
2018-09-26 23:33:51 +02:00
if ( tx . amount ) {
// pending tx
tx . amt = tx . amount * - 100000000 ;
tx . fee = 0 ;
tx . timestamp = tx . time ;
tx . memo = 'On-chain transaction' ;
}
2018-09-05 00:18:24 +02:00
2018-09-22 13:40:47 +02:00
if ( typeof tx . amt !== 'undefined' && typeof tx . fee !== 'undefined' ) {
2018-09-05 00:18:24 +02:00
// lnd tx outgoing
2023-07-25 15:50:04 +02:00
tx . value = parseInt ( ( tx . amt * 1 + tx . fee * 1 ) * - 1 , 10 ) ;
2018-09-22 13:40:47 +02:00
}
2018-09-26 23:16:52 +02:00
if ( tx . type === 'paid_invoice' ) {
tx . memo = tx . memo || 'Lightning payment' ;
2019-01-09 14:31:34 +01:00
if ( tx . value > 0 ) tx . value = tx . value * - 1 ; // value already includes fee in it (see lndhub)
2018-12-29 00:36:47 +01:00
// outer code expects spending transactions to of negative value
2018-09-26 23:16:52 +02:00
}
if ( tx . type === 'bitcoind_tx' ) {
tx . memo = 'On-chain transaction' ;
2018-09-05 00:18:24 +02:00
}
2018-09-22 13:40:47 +02:00
2018-12-25 20:07:43 +01:00
if ( tx . type === 'user_invoice' ) {
// incoming ln tx
2023-07-25 15:50:04 +02:00
tx . value = parseInt ( tx . amt , 10 ) ;
2018-12-25 20:07:43 +01:00
tx . memo = tx . description || 'Lightning invoice' ;
}
tx . received = new Date ( tx . timestamp * 1000 ) . toString ( ) ;
2018-09-01 01:28:19 +02:00
}
2020-06-01 14:54:23 +02:00
return txs . sort ( function ( a , b ) {
2018-09-26 23:16:52 +02:00
return b . timestamp - a . timestamp ;
} ) ;
2018-08-19 16:54:15 +02:00
}
2018-09-01 01:28:19 +02:00
async fetchPendingTransactions ( ) {
2020-06-01 14:54:23 +02:00
const response = await this . _api . get ( '/getpending' , {
2018-09-01 01:28:19 +02:00
headers : {
'Access-Control-Allow-Origin' : '*' ,
'Content-Type' : 'application/json' ,
Authorization : 'Bearer' + ' ' + this . access _token ,
} ,
} ) ;
2020-06-01 14:54:23 +02:00
const json = response . body ;
2018-09-01 01:28:19 +02:00
if ( typeof json === 'undefined' ) {
2018-09-25 23:23:56 +02:00
throw new Error ( 'API failure: ' + response . err + ' ' + JSON . stringify ( response ) ) ;
2018-09-01 01:28:19 +02:00
}
if ( json && json . error ) {
throw new Error ( 'API error: ' + json . message + ' (code ' + json . code + ')' ) ;
}
this . pending _transactions _raw = json ;
2018-08-19 16:54:15 +02:00
}
2018-07-15 21:56:28 +02:00
2018-09-01 01:28:19 +02:00
async fetchTransactions ( ) {
// TODO: iterate over all available pages
const limit = 10 ;
let queryRes = '' ;
2020-06-01 14:54:23 +02:00
const offset = 0 ;
2018-09-01 01:28:19 +02:00
queryRes += '?limit=' + limit ;
queryRes += '&offset=' + offset ;
2020-06-01 14:54:23 +02:00
const response = await this . _api . get ( '/gettxs' + queryRes , {
2018-09-01 01:28:19 +02:00
headers : {
'Access-Control-Allow-Origin' : '*' ,
'Content-Type' : 'application/json' ,
Authorization : 'Bearer' + ' ' + this . access _token ,
} ,
} ) ;
2020-06-01 14:54:23 +02:00
const json = response . body ;
2018-09-01 01:28:19 +02:00
if ( typeof json === 'undefined' ) {
throw new Error ( 'API failure: ' + response . err + ' ' + JSON . stringify ( response . body ) ) ;
}
if ( json && json . error ) {
throw new Error ( 'API error: ' + json . message + ' (code ' + json . code + ')' ) ;
}
2018-09-22 13:40:47 +02:00
if ( ! Array . isArray ( json ) ) {
2018-09-01 01:28:19 +02:00
throw new Error ( 'API unexpected response: ' + JSON . stringify ( response . body ) ) ;
}
2019-02-10 14:20:04 +01:00
this . _lastTxFetch = + new Date ( ) ;
2018-09-22 13:40:47 +02:00
this . transactions _raw = json ;
2018-09-01 01:28:19 +02:00
}
2018-07-15 21:56:28 +02:00
2018-08-19 16:54:15 +02:00
getBalance ( ) {
2019-05-02 22:33:03 +02:00
return this . balance ;
2018-09-01 01:28:19 +02:00
}
2018-09-22 13:40:47 +02:00
async fetchBalance ( noRetry ) {
2018-09-01 01:28:19 +02:00
await this . checkLogin ( ) ;
2020-06-01 14:54:23 +02:00
const response = await this . _api . get ( '/balance' , {
2018-09-01 01:28:19 +02:00
headers : {
'Access-Control-Allow-Origin' : '*' ,
'Content-Type' : 'application/json' ,
Authorization : 'Bearer' + ' ' + this . access _token ,
} ,
} ) ;
2020-06-01 14:54:23 +02:00
const json = response . body ;
2018-09-01 01:28:19 +02:00
if ( typeof json === 'undefined' ) {
throw new Error ( 'API failure: ' + response . err + ' ' + JSON . stringify ( response . body ) ) ;
}
if ( json && json . error ) {
2018-09-22 13:40:47 +02:00
if ( json . code * 1 === 1 && ! noRetry ) {
await this . authorize ( ) ;
return this . fetchBalance ( true ) ;
}
2018-09-01 01:28:19 +02:00
throw new Error ( 'API error: ' + json . message + ' (code ' + json . code + ')' ) ;
}
if ( ! json . BTC || typeof json . BTC . AvailableBalance === 'undefined' ) {
throw new Error ( 'API unexpected response: ' + JSON . stringify ( response . body ) ) ;
}
this . balance _raw = json ;
this . balance = json . BTC . AvailableBalance ;
this . _lastBalanceFetch = + new Date ( ) ;
}
/ * *
* Example return :
* { destination : '03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f' ,
* payment _hash : 'faf996300a468b668c58ca0702a12096475a0dd2c3dde8e812f954463966bcf4' ,
2019-12-21 20:44:35 +01:00
* num _satoshis : '100' ,
2018-09-01 01:28:19 +02:00
* timestamp : '1535116657' ,
* expiry : '3600' ,
* description : 'hundredSatoshis blitzhub' ,
* description _hash : '' ,
* fallback _addr : '' ,
* cltv _expiry : '10' ,
* route _hints : [ ] }
*
* @ param invoice BOLT invoice string
2021-09-09 13:00:11 +02:00
* @ return { payment _hash : string }
2018-09-01 01:28:19 +02:00
* /
2019-12-21 20:44:35 +01:00
decodeInvoice ( invoice ) {
2020-06-01 14:54:23 +02:00
const { payeeNodeKey , tags , satoshis , millisatoshis , timestamp } = bolt11 . decode ( invoice ) ;
2019-12-21 20:44:35 +01:00
2020-06-01 14:54:23 +02:00
const decoded = {
2019-12-21 20:44:35 +01:00
destination : payeeNodeKey ,
2020-01-12 01:00:33 +01:00
num _satoshis : satoshis ? satoshis . toString ( ) : '0' ,
num _millisatoshis : millisatoshis ? millisatoshis . toString ( ) : '0' ,
2019-12-21 20:44:35 +01:00
timestamp : timestamp . toString ( ) ,
fallback _addr : '' ,
2020-01-12 01:00:33 +01:00
route _hints : [ ] ,
2019-12-21 20:44:35 +01:00
} ;
for ( let i = 0 ; i < tags . length ; i ++ ) {
2020-06-01 14:54:23 +02:00
const { tagName , data } = tags [ i ] ;
2019-12-21 20:44:35 +01:00
switch ( tagName ) {
case 'payment_hash' :
2020-01-12 01:00:33 +01:00
decoded . payment _hash = data ;
break ;
2019-12-21 20:44:35 +01:00
case 'purpose_commit_hash' :
2020-01-12 01:00:33 +01:00
decoded . description _hash = data ;
break ;
2019-12-21 20:44:35 +01:00
case 'min_final_cltv_expiry' :
2020-01-12 01:00:33 +01:00
decoded . cltv _expiry = data . toString ( ) ;
break ;
2019-12-21 20:44:35 +01:00
case 'expire_time' :
2020-01-12 01:00:33 +01:00
decoded . expiry = data . toString ( ) ;
break ;
2019-12-21 20:44:35 +01:00
case 'description' :
2020-01-12 01:00:33 +01:00
decoded . description = data ;
break ;
2019-12-21 20:44:35 +01:00
}
2018-09-01 01:28:19 +02:00
}
2020-01-12 19:08:56 +01:00
if ( ! decoded . expiry ) decoded . expiry = '3600' ; // default
2023-07-25 15:50:04 +02:00
if ( parseInt ( decoded . num _satoshis , 10 ) === 0 && decoded . num _millisatoshis > 0 ) {
2020-01-19 21:55:10 +01:00
decoded . num _satoshis = ( decoded . num _millisatoshis / 1000 ) . toString ( ) ;
}
2019-12-21 20:44:35 +01:00
return ( this . decoded _invoice _raw = decoded ) ;
2018-09-01 01:28:19 +02:00
}
async fetchInfo ( ) {
2020-06-01 14:54:23 +02:00
const response = await this . _api . get ( '/getinfo' , {
2018-09-01 01:28:19 +02:00
headers : {
'Access-Control-Allow-Origin' : '*' ,
'Content-Type' : 'application/json' ,
Authorization : 'Bearer' + ' ' + this . access _token ,
} ,
} ) ;
2020-06-01 14:54:23 +02:00
const json = response . body ;
2018-09-01 01:28:19 +02:00
if ( typeof json === 'undefined' ) {
throw new Error ( 'API failure: ' + response . err + ' ' + JSON . stringify ( response . body ) ) ;
}
if ( json && json . error ) {
throw new Error ( 'API error: ' + json . message + ' (code ' + json . code + ')' ) ;
}
if ( ! json . identity _pubkey ) {
throw new Error ( 'API unexpected response: ' + JSON . stringify ( response . body ) ) ;
}
this . info _raw = json ;
2018-08-19 16:54:15 +02:00
}
2018-07-15 21:56:28 +02:00
2019-03-08 22:54:47 +01:00
static async isValidNodeAddress ( address ) {
2023-12-15 16:56:41 +01:00
const apiCall = new Frisbee ( {
baseURI : address ,
} ) ;
2020-06-01 14:54:23 +02:00
const response = await apiCall . get ( '/getinfo' , {
2019-03-08 22:54:47 +01:00
headers : {
'Access-Control-Allow-Origin' : '*' ,
'Content-Type' : 'application/json' ,
} ,
} ) ;
2020-06-01 14:54:23 +02:00
const json = response . body ;
2019-03-08 22:54:47 +01:00
if ( typeof json === 'undefined' ) {
throw new Error ( 'API failure: ' + response . err + ' ' + JSON . stringify ( response . body ) ) ;
}
if ( json && json . code && json . code !== 1 ) {
throw new Error ( 'API error: ' + json . message + ' (code ' + json . code + ')' ) ;
}
return true ;
}
2018-09-01 01:28:19 +02:00
allowReceive ( ) {
2018-12-25 17:34:51 +01:00
return true ;
2018-09-01 01:28:19 +02:00
}
2020-01-12 01:00:33 +01:00
2021-03-23 13:16:32 +01:00
allowSignVerifyMessage ( ) {
return false ;
}
2020-01-12 01:00:33 +01:00
/ * *
* Example return :
* { destination : '03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f' ,
* payment _hash : 'faf996300a468b668c58ca0702a12096475a0dd2c3dde8e812f954463966bcf4' ,
* num _satoshis : '100' ,
* timestamp : '1535116657' ,
* expiry : '3600' ,
* description : 'hundredSatoshis blitzhub' ,
* description _hash : '' ,
* fallback _addr : '' ,
* cltv _expiry : '10' ,
* route _hints : [ ] }
*
* @ param invoice BOLT invoice string
* @ return { Promise . < Object > }
* /
async decodeInvoiceRemote ( invoice ) {
await this . checkLogin ( ) ;
2020-06-01 14:54:23 +02:00
const response = await this . _api . get ( '/decodeinvoice?invoice=' + invoice , {
2020-01-12 01:00:33 +01:00
headers : {
'Access-Control-Allow-Origin' : '*' ,
'Content-Type' : 'application/json' ,
Authorization : 'Bearer' + ' ' + this . access _token ,
} ,
} ) ;
2020-06-01 14:54:23 +02:00
const json = response . body ;
2020-01-12 01:00:33 +01:00
if ( typeof json === 'undefined' ) {
throw new Error ( 'API failure: ' + response . err + ' ' + JSON . stringify ( response . body ) ) ;
}
if ( json && json . error ) {
throw new Error ( 'API error: ' + json . message + ' (code ' + json . code + ')' ) ;
}
if ( ! json . payment _hash ) {
throw new Error ( 'API unexpected response: ' + JSON . stringify ( response . body ) ) ;
}
return ( this . decoded _invoice _raw = json ) ;
}
2020-08-10 16:17:50 +02:00
weOwnTransaction ( txid ) {
for ( const tx of this . getTransactions ( ) ) {
if ( tx && tx . payment _hash && tx . payment _hash === txid ) return true ;
}
return false ;
}
2022-02-11 15:18:56 +01:00
authenticate ( lnurl ) {
return lnurl . authenticate ( this . secret ) ;
}
2018-07-15 21:56:28 +02:00
}
2018-09-01 01:28:19 +02:00
/ *
pending tx :
[ { amount : 0.00078061 ,
account : '521172' ,
address : '3F9seBGCJZQ4WJJHwGhrxeGXCGbrm5SNpF' ,
category : 'receive' ,
confirmations : 0 ,
blockhash : '' ,
blockindex : 0 ,
blocktime : 0 ,
txid : '28a74277e47c2d772ee8a40464209c90dce084f3b5de38a2f41b14c79e3bfc62' ,
walletconflicts : [ ] ,
time : 1535024434 ,
timereceived : 1535024434 } ]
tx :
[ { amount : 0.00078061 ,
account : '521172' ,
address : '3F9seBGCJZQ4WJJHwGhrxeGXCGbrm5SNpF' ,
category : 'receive' ,
confirmations : 5 ,
blockhash : '0000000000000000000edf18e9ece18e449c6d8eed1f729946b3531c32ee9f57' ,
blockindex : 693 ,
blocktime : 1535024914 ,
txid : '28a74277e47c2d772ee8a40464209c90dce084f3b5de38a2f41b14c79e3bfc62' ,
walletconflicts : [ ] ,
time : 1535024434 ,
timereceived : 1535024434 } ]
* /