diff --git a/client/build.sh b/client/build.sh index 5baad5b..3bbc7d4 100755 --- a/client/build.sh +++ b/client/build.sh @@ -18,7 +18,7 @@ mkdir -p $DEST $DEST/lib $DEST/fonts $DEST/swatch # Static assets cp -r www/* $DEST/ cp -r fonts/node_modules/typeface-* $DEST/fonts/ -cp -r node_modules/bootswatch/dist/!(darkly|litera|minty|sketchy|journal|pulse) $DEST/swatch/ +cp -r node_modules/bootswatch/dist/!(litera|minty|sketchy|journal|pulse) $DEST/swatch/ cp -r swatch/*/ $DEST/swatch/ find $DEST/swatch -type f ! -name '*.min.css' -delete find $DEST/fonts -type f -name "*.md" -delete diff --git a/client/src/intent.js b/client/src/intent.js index 9f45431..bed888d 100644 --- a/client/src/intent.js +++ b/client/src/intent.js @@ -38,7 +38,7 @@ module.exports = ({ DOM, route, conf$, scan$, urihandler$, offerInv$ }) => { // New invoice/offer action , newInv$ = submit('[do=new-invoice]').map(r => ({ label: nanoid() - , msatoshi: r.msatoshi || 'any' + , amount_msat: r.amount_msat || 'any' , description: r.description || '⚡' , reusable_offer: !!r['reusable-offer'] })) , invUseOffer$ = on('[do=new-invoice] [name=reusable-offer]', 'input') diff --git a/client/src/load-theme.js b/client/src/load-theme.js index 83570af..bb53091 100644 --- a/client/src/load-theme.js +++ b/client/src/load-theme.js @@ -1,7 +1,7 @@ // quickly load the theme and set its primary theme-color, without requiring the whole app to load. // post-load theme changes are handled by the app itself. -const theme = localStorage.conf && JSON.parse(localStorage.conf).theme || 'dark' +const theme = localStorage.conf && JSON.parse(localStorage.conf).theme || 'darkly' document.write('') diff --git a/client/src/model.js b/client/src/model.js index 302fb36..add05e4 100644 --- a/client/src/model.js +++ b/client/src/model.js @@ -7,11 +7,11 @@ const msatbtc = big(100000000000) // msat in 1 btc const sumChans = chans => chans.filter(c => c.chan.state === 'CHANNELD_NORMAL') - .reduce((T, c) => T + c.chan.msatoshi_to_us, 0) + .reduce((T, c) => T + c.chan.to_us_msat, 0) , sumOuts = outs => outs.filter(o => o.status === 'confirmed') - .reduce((T, o) => T + o.value*1000, 0) + .reduce((T, o) => T + o.amount_msat, 0) , fmtAlert = (s, unitf) => s.replace(/@\{\{(\d+)\}\}/g, (_, msat) => unitf(msat)) @@ -19,7 +19,7 @@ const , idn = x => x const - themes = 'cerulean cosmo cyborg dark flatly lumen lux materia sandstone simplex slate solar spacelab superhero united yeti'.split(' ') + themes = 'cerulean cosmo cyborg darkly flatly lumen lux materia sandstone simplex slate solar spacelab superhero united yeti'.split(' ') , units = 'sat bits milli BTC USD'.split(' ') , unitprec = { sat: 3, bits: 5, milli: 8, BTC: 11, USD: 6 } , unitrate = { sat: 0.001, bits: 0.00001, milli: 0.00000001, BTC: 0.00000000001 } @@ -37,9 +37,9 @@ module.exports = ({ dismiss$, togExp$, togTheme$, togUnit$, page$, goHome$, goRe // Config options conf = (name, def, list) => savedConf$.first().map(c => c[name] || def).map(list ? idx(list) : idn) - , expert$ = conf('expert', false) .concat(togExp$) .scan(x => !x) - , theme$ = conf('theme', 'dark', themes).concat(togTheme$).scan(n => (n+1) % themes.length).map(n => themes[n]) - , unit$ = conf('unit', 'bits', units).concat(togUnit$) .scan(n => (n+1) % units.length) .map(n => units[n]) + , expert$ = conf('expert', true) .concat(togExp$) .scan(x => !x) + , theme$ = conf('theme', 'darkly', themes).concat(togTheme$).scan(n => (n+1) % themes.length).map(n => themes[n]) + , unit$ = conf('unit', 'sat', units).concat(togUnit$) .scan(n => (n+1) % units.length) .map(n => units[n]) , conf$ = combine({ expert$, theme$, unit$ }) // Currency & unit conversion handling @@ -68,8 +68,8 @@ module.exports = ({ dismiss$, togExp$, togTheme$, togUnit$, page$, goHome$, goRe , alert$ = O.merge( error$.map(err => [ 'danger', ''+err ]) , incoming$.map(i => [ 'success', `Received payment of @{{${recvAmt(i)}}}` ]) - , paySent$.map(p => [ 'success', `Sent payment of @{{${p.msatoshi}}}` ]) - , funded$.map(c => [ 'success', `Opening channel for @{{${c.chan.msatoshi_total}}}, awaiting on-chain confirmation` ]) + , paySent$.map(p => [ 'success', `Sent payment of @{{${p.amount_msat}}}` ]) + , funded$.map(c => [ 'success', `Opening channel for @{{${c.chan.total_msat}}}, awaiting on-chain confirmation` ]) , closed$.map(c => [ 'success', `Channel ${c.chan.short_channel_id || c.chan.channel_id} is closing` ]) , dismiss$.mapTo(null) ) @@ -88,7 +88,7 @@ module.exports = ({ dismiss$, togExp$, togTheme$, togUnit$, page$, goHome$, goRe // Periodically re-sync channel balance from "listpeers", // continuously patch with known channel opening/closing , channels$ = O.merge( - peers$.map(peers => _ => getChannels(peers)) + peers$.map(channels => _ => getChannels(channels)) , funded$.map(chan => S => [ ...S, chan ]) , closed$.map(chan => S => [ ...S.filter(c => c.chan.channel_id != chan.chan.channel_id), chan ]) ).startWith(null).scan((S, mod) => mod(S)) @@ -99,8 +99,8 @@ module.exports = ({ dismiss$, togExp$, togTheme$, togUnit$, page$, goHome$, goRe // continuously patch with known incoming & outgoing payments , cbalance$ = O.merge( channels$.map(chans => _ => sumChans(chans)) - , incoming$.map(inv => N => N + inv.msatoshi_received) - , paySent$.map(pay => N => N - pay.msatoshi_sent) + , incoming$.map(inv => N => N + inv.amount_received_msat) + , paySent$.map(pay => N => N - pay.amount_sent_msat) ).startWith(null).scan((N, mod) => mod(N)).distinctUntilChanged() // Periodically re-sync from listpays, continuously patch with known outgoing @@ -128,7 +128,7 @@ module.exports = ({ dismiss$, togExp$, togTheme$, togUnit$, page$, goHome$, goRe // Chronologically sorted feed of incoming and outgoing payments , feed$ = O.combineLatest(freshInvs$, freshPays$, (invoices, payments) => [ ...invoices.map(i => [ 'in', i.paid_at, recvAmt(i), i ]) - , ...payments.map(p => [ 'out', p.created_at, p.msatoshi, p ]) + , ...payments.map(p => [ 'out', p.created_at, p.amount_msat, p ]) ].sort((a, b) => b[1] - a[1])) // Collapsed payment/invoice on home feed list @@ -145,7 +145,7 @@ module.exports = ({ dismiss$, togExp$, togTheme$, togUnit$, page$, goHome$, goRe , amtMsat$ = amtVal$.withLatestFrom(rate$, (amt, rate) => amt && rate && big(amt).div(rate).toFixed(0) || '') .merge(page$.mapTo(null)).startWith(null) , amtData$ = combine({ - msatoshi: amtMsat$ + amount_msat: amtMsat$ , amount: unit$.withLatestFrom(amtMsat$, rate$, (unit, msat, rate) => formatAmt(msat, rate, unitprec[unit], false)) .merge(goRecv$.merge(offer$).merge(payreq$).mapTo('')) , unit: unit$ @@ -205,8 +205,8 @@ const markFailed = (payments, payment_hash) => payments.map(pay => const pendingPayStub = inv => ({ status: 'pending' , created_at: Date.now()/1000|0 -, msatoshi: inv.custom_msat || inv.msatoshi -, msatoshi_sent: 0 +, amount_msat: inv.custom_msat || inv.amount_msat +, amount_sent_msat: 0 , destination: inv.payee || inv.node_id || inv.destination , ...only(inv, 'payment_hash', 'description', 'offer_id', 'vendor', 'quantity', 'payer_note' ) }) @@ -229,4 +229,4 @@ const unitFormatter = (unit, msatusd) => (msat, as_alt_unit=false, non_breaking= // Check if experimental offers support is enabled // Always considered off in c-lightning <=v0.10.0 because it used an incompatible spec. const checkOffersEnabled = conf => - !!(conf['experimental-offers'] && !/^0\.(9\.|10\.0)/.test(conf['# version'])) \ No newline at end of file + !!(conf['experimental-offers'] && !/^0\.(9\.|10\.0)/.test(conf['# version'])) diff --git a/client/src/rpc.js b/client/src/rpc.js index 837dfa9..52206d1 100644 --- a/client/src/rpc.js +++ b/client/src/rpc.js @@ -34,7 +34,7 @@ exports.parseRes = ({ HTTP, SSE }) => { // Periodic updates , info$: reply('getinfo').map(r => r.body) - , peers$: reply('listpeers').map(r => r.body.peers) + , peers$: reply('listpeerchannels').map(r => r.body.channels) , payments$: reply('_listpays').map(r => r.body.pays) , invoices$: reply('_listinvoices').map(r => r.body.invoices) , funds$: reply('listfunds').map(r => r.body) @@ -69,14 +69,14 @@ exports.makeReq = ({ viewPay$, confPay$, offerPay$, offerRecv$, newInv$, goLogs$ viewPay$.map(paystr => [ '_decodecheck', [ paystr ], { paystr } ]) , confPay$.map(pay => [ '_pay', [ pay.paystr, pay.custom_msat ], { pay, bg: true } ]) , newInv$.map(inv => !inv.reusable_offer - ? [ 'invoice', [ inv.msatoshi, inv.label, inv.description, INVOICE_TTL ], inv ] - : [ 'offer', [ inv.msatoshi, inv.description, null, inv.label ], inv ]) + ? [ 'invoice', [ inv.amount_msat, inv.label, inv.description, INVOICE_TTL ], inv ] + : [ 'offer', [ inv.amount_msat, inv.description, null, inv.label ], inv ]) , offerPay$.map(pay => [ '_fetchinvoice', [ pay.paystr, pay.custom_msat, pay.quantity, pay.payer_note ] ]) , offerRecv$.map(recv => [ 'sendinvoice', [ recv.paystr, recv.label ] ]) , goLogs$.mapTo( [ 'getlog' ] ) -, updChan$.mapTo( [ 'listpeers' ] ) +, updChan$.mapTo( [ 'listpeerchannels' ] ) , openChan$.map(d => [ '_connectfund', [ d.nodeuri, d.channel_capacity_sat, d.feerate ] ]) , closeChan$.map(d => [ '_close', [ d.peerid, d.chanid ] ]) @@ -90,7 +90,7 @@ exports.makeReq = ({ viewPay$, confPay$, offerPay$, offerRecv$, newInv$, goLogs$ , timer(60000).mapTo( [ '_listpays', [], { bg: true } ]) , timer(60000).mapTo( [ 'getinfo', [], { bg: true } ]) , timer(60000).merge(goChan$).throttleTime(2000) - .mapTo( [ 'listpeers', [], { bg: true } ]) + .mapTo( [ 'listpeerchannels', [], { bg: true } ]) , timer(60000).merge(goNewChan$).merge(goDeposit$).throttleTime(2000) .mapTo( [ 'listfunds', [], { bg: true } ]) diff --git a/client/src/util.js b/client/src/util.js index b1c9007..3db69d6 100644 --- a/client/src/util.js +++ b/client/src/util.js @@ -20,18 +20,16 @@ export const parseUri = uri => { // returns the expected invoice amount when its <0.5% different from the actual amount paid, // or the actual amount paid otherwise. this is done to make the UX less confusing when the // sender uses overpayment randomization (https://github.com/ElementsProject/lightning/issues/1089) -export const recvAmt = ({ msatoshi: expected, msatoshi_received: actual }) => +export const recvAmt = ({ amount_msat: expected, amount_received_msat: actual }) => (expected && (actual-expected)/expected<0.005) ? expected : actual -// Parse "listpeers" to get all channels as a list of (peer,channel) tuples -export const getChannels = peers => [].concat(...peers.map(peer => peer.channels.map(chan => ({ peer, chan })))) +// Parse "listpeerchannels" to get all channels as a list of (peer,channel) tuples +export const getChannels = channels => [].concat(...channels.map(chan => ({ chan }))) +// TODO: remove this function and refactor the code that was using it... +// this function is not needed anymore, as a quickfix, it is now Identity // Parse the `sat`-suffixed amount string fields into numbers -export const parsePayment = p => ({ - ...p -, msatoshi: p.msatoshi != null ? p.msatoshi : p.amount_msat ? +p.amount_msat.slice(0, -4) : null -, msatoshi_sent: p.amount_sent_msat ? +p.amount_sent_msat.slice(0, -4) : null -}) +export const parsePayment = p => p export const combine = obj => { const keys = Object.keys(obj).map(k => k.replace(/\$$/, '')) @@ -70,4 +68,4 @@ export const parseRpcCmd = str => { export const only = (obj, ...keys) => keys.reduce((R, k) => { if (obj[k] != null) R[k] = obj[k] return R -}, {}) \ No newline at end of file +}, {}) diff --git a/client/src/views/channels.js b/client/src/views/channels.js index d74287b..c3a758e 100644 --- a/client/src/views/channels.js +++ b/client/src/views/channels.js @@ -5,7 +5,7 @@ const blockInterval = process.env.BLOCK_INTERVAL || 600 const stateGroups = { active: [ 'CHANNELD_NORMAL' ] -, opening: [ 'CHANNELD_AWAITING_LOCKIN', 'OPENINGD' ] +, opening: [ 'CHANNELD_AWAITING_LOCKIN', 'OPENINGD', 'DUALOPEND_AWAITING_LOCKIN' ] , closing: [ 'CLOSINGD_COMPLETE', 'CLOSINGD_SIGEXCHANGE', 'CHANNELD_SHUTTING_DOWN', 'AWAITING_UNILATERAL' ] , closed: [ 'FUNDING_SPEND_SEEN', 'ONCHAIN' ] } @@ -14,12 +14,12 @@ const stateGroups = { const getGroup = state => Object.keys(stateGroups).find(group => stateGroups[group].includes(state)) // Sort by status first, then by amount -const chanSorter = (a, b) => (chanSorting(b) - chanSorting(a)) || (b.chan.msatoshi_total - a.chan.msatoshi_total) +const chanSorter = (a, b) => (chanSorting(b) - chanSorting(a)) || (b.chan.total_msat - a.chan.total_msat) -const chanSorting = ({ peer, chan }) => - peer.connected && chan.state == 'CHANNELD_NORMAL' ? 6 -: peer.connected && stateGroups.opening.includes(chan.state) ? 5 -: !peer.connected && chan.state == 'CHANNELD_NORMAL' ? 4 +const chanSorting = ({ chan }) => + chan.peer_connected && chan.state == 'CHANNELD_NORMAL' ? 6 +: chan.peer_connected && stateGroups.opening.includes(chan.state) ? 5 +: !chan.peer_connected && chan.state == 'CHANNELD_NORMAL' ? 4 : stateGroups.closing.includes(chan.state) ? 3 : stateGroups.opening.includes(chan.state) ? 2 : stateGroups.closed.includes(chan.state) ? 1 @@ -77,19 +77,19 @@ export const newChannel = ({ amtData, fundMaxChan, obalance, unitf, conf: { unit ]) } -const channelRenderer = ({ chanActive, unitf, expert, blockheight }) => ({ chan, peer }) => { +const channelRenderer = ({ chanActive, unitf, expert, blockheight }) => ({ chan }) => { - const bar = (label, color, msatoshi, amtText=unitf(msatoshi)) => + const bar = (label, color, amount_msat, amtText=unitf(amount_msat)) => div(`.progress-bar.bg-${color}`, { attrs: { role: 'progressbar', title: `${label}: ${amtText}` } - , style: { width: `${msatoshi / chan.msatoshi_total * 100}%` } - }, msatoshi/chan.msatoshi_total > 0.05 ? amtText : '') + , style: { width: `${amount_msat / chan.total_msat * 100}%` } + }, amount_msat/chan.total_msat > 0.05 ? amtText : '') const stateGroup = getGroup(chan.state) - , stateLabel = !peer.connected && stateGroup == 'active' ? 'offline' : stateGroup + , stateLabel = !chan.peer_connected && stateGroup == 'active' ? 'offline' : stateGroup , isClosed = [ 'closing', 'closed' ].includes(stateGroup) - , ours = chan.msatoshi_to_us - , theirs = chan.msatoshi_total - ours + , ours = chan.to_us_msat + , theirs = chan.total_msat - ours // the channel reserve fields appear to be sometimes (incorrectly?) missing, // defaulting them to 0 isn't quite right but should work for now , ourReserve = chan.our_channel_reserve_satoshis*1000 || 0 @@ -103,11 +103,11 @@ const channelRenderer = ({ chanActive, unitf, expert, blockheight }) => ({ chan, const visible = chanActive == chan.channel_id , classes = { active: visible, 'list-group-item-action': !visible - , [`c-${stateGroup}`]: true, 'p-online': peer.connected, 'p-offline': !peer.connected } + , [`c-${stateGroup}`]: true, 'p-online': chan.peer_connected, 'p-offline': !chan.peer_connected } return li('.list-group-item', { class: classes, dataset: { chanToggle: chan.channel_id } }, [ header('.d-flex.justify-content-between.mb-2', [ - span('.capacity', unitf(chan.msatoshi_total)) + span('.capacity', unitf(chan.total_msat)) , span('.state', stateLabel) ]) @@ -133,14 +133,14 @@ const channelRenderer = ({ chanActive, unitf, expert, blockheight }) => ({ chan, , isClosed || expert ? li([ strong('Theirs:'), ' ', unitf(theirs) ]) : '' , channelAge ? li([ strong('Age:'), ' ', `${channelAge} blocks (${channelAgeFuz})` ]) : '' - , li([ strong('Peer:'), ' ', small('.break-all', peer.id), ' ', em(`(${peer.connected ? 'connected' : 'disconnected'})`) ]) + , li([ strong('Peer:'), ' ', small('.break-all', chan.peer_id), ' ', em(`(${chan.peer_connected ? 'connected' : 'disconnected'})`) ]) , expert ? li([ strong('Funding TXID:'), ' ', small('.break-all', chan.funding_txid) ]) : '' , expert ? li('.status-text', chan.status.join('\n')) : '' , !isClosed ? li('.text-center' - , button('.btn.btn-link.btn-sm', { dataset: { closeChannel: chan.channel_id, closeChannelPeer: peer.id } }, 'Close channel')) : '' + , button('.btn.btn-link.btn-sm', { dataset: { closeChannel: chan.channel_id, closeChannelPeer: chan.peer_id } }, 'Close channel')) : '' - , expert ? li(yaml({ peer: omitKey('channels', peer), ...omitKey('status', chan) })) : '' + , expert ? li(yaml({ chan: omitKey('status', chan) })) : '' ]) ]) } diff --git a/client/src/views/home.js b/client/src/views/home.js index 2af4655..1b9bde4 100644 --- a/client/src/views/home.js +++ b/client/src/views/home.js @@ -58,8 +58,8 @@ const itemRenderer = ({ feedActive, unitf, expert }) => ([ type, ts, msat, obj ] status == 'pending' ? pendingStatus(obj) : '' , li([ strong(tsLabel), ' ', tsStr ]) , status == 'failed' && msat ? li([ strong('Amount:'), ' ', unitf(msat) ]) : '' - , type == 'in' && obj.msatoshi_received > obj.msatoshi ? li([ strong('Overpayment:'), ' ', unitf(obj.msatoshi_received-obj.msatoshi) ]) : '' - , type == 'out' && status == 'complete' && obj.msatoshi ? li([ strong('Fee:'), ' ', feesText(obj, unitf) ]) : '' + , type == 'in' && obj.amount_received_msat > obj.amount_msat ? li([ strong('Overpayment:'), ' ', unitf(obj.amount_received_msat-obj.amount_msat) ]) : '' + , type == 'out' && status == 'complete' && obj.amount_msat ? li([ strong('Fee:'), ' ', feesText(obj, unitf) ]) : '' , obj.vendor ? li([ strong('Issuer:'), ' ', span('.break-word', obj.vendor) ]) : '' , showDesc(obj) ? li([ strong('Description:'), ' ', span('.break-word', obj.description) ]) : '' , obj.quantity ? li([ strong('Quantity:'), ' ', span('.break-word', obj.quantity) ]) : '' @@ -72,7 +72,7 @@ const itemRenderer = ({ feedActive, unitf, expert }) => ([ type, ts, msat, obj ] ]) } -const feesText = ({ msatoshi: quoted, msatoshi_sent: sent }, unitf) => +const feesText = ({ amount_msat: quoted, amount_sent_msat: sent }, unitf) => `${unitf(sent-quoted)} (${((sent-quoted)/quoted*100).toFixed(2)}%)` const pendingStatus = ({ attempts, number_of_parts }) => { diff --git a/client/src/views/offers.js b/client/src/views/offers.js index 9e7d823..bdb8f5d 100644 --- a/client/src/views/offers.js +++ b/client/src/views/offers.js @@ -15,9 +15,9 @@ const offerPay = offer => ({ unitf, amtData, offerPayQuantity, conf: { expert } , // Bitcoin denominated amount - offer.msatoshi + offer.amount_msat ? p('.toggle-unit', [ offer.quantity_min ? 'Price per unit: ' : 'Amount: ' - , fmtSatAmountWithAlt(offer.msatoshi, unitf) ]) + , fmtSatAmountWithAlt(offer.amount_msat, unitf) ]) // Fiat denominated amount : offer.currency @@ -43,7 +43,7 @@ const offerPay = offer => ({ unitf, amtData, offerPayQuantity, conf: { expert } !offer.currency ? div('.mb-3', 'Do you confirm making this payment?') : '' , div([ button('.btn.btn-lg.btn-primary.mb-1', { attrs: { type: 'submit' } } - , offer.msatoshi ? `Pay ${unitf(mul(offer.msatoshi, offerPayQuantity))}` + , offer.amount_msat ? `Pay ${unitf(mul(offer.amount_msat, offerPayQuantity))}` : offer.currency ? 'Continue' : 'Send Payment') , ' ' @@ -60,7 +60,7 @@ const offerRecv = offer => ({ unitf, conf: { expert} }) => form('.offer-recv', { attrs: { do: 'offer-recv' }, dataset: offer }, [ h2('Receive payment') - , p([ 'You were offered a payment of ', strong('.toggle-unit', unitf(offer.msatoshi)), '. Do you accept it?' ]) + , p([ 'You were offered a payment of ', strong('.toggle-unit', unitf(offer.amount_msat )), '. Do you accept it?' ]) //, expert ? p([ 'Node ID: ', small('.text-muted.break-all', offer.node_id) ]) : '' //, expert ? p([ 'Offer ID: ', small('.text-muted.break-all', offer.offer_id) ]) : '' @@ -71,7 +71,7 @@ const offerRecv = offer => ({ unitf, conf: { expert} }) => , div('.form-buttons', [ button('.btn.btn-lg.btn-primary', { attrs: { type: 'submit' } } - , `Receive ${unitf(offer.msatoshi)}`) + , `Receive ${unitf(offer.amount_msat)}`) , ' ' , a('.btn.btn-lg.btn-secondary', { attrs: { href: '#/' } }, 'Cancel') ]) @@ -91,7 +91,7 @@ export const localOffer = offer => qrinv(offer).then(qr => ({ unitf, conf: { exp div('.row', [ div('.col-sm-6.text-center', [ h2('Receive payment(s)') - , p(`You can receive multiple payments${offer.msatoshi != 'any'?` of ${unitf(offer.msatoshi)} each`:''} using the reusable BOLT12 offer:`) + , p(`You can receive multiple payments${offer.amount_msat != 'any'?` of ${unitf(offer.amount_msat)} each`:''} using the reusable BOLT12 offer:`) , small('.d-none.d-sm-block.text-muted.break-all.mt-3', offer.bolt12_unsigned) ]) , div('.col-sm-6.text-center', [ @@ -102,5 +102,5 @@ export const localOffer = offer => qrinv(offer).then(qr => ({ unitf, conf: { exp , expert ? yaml(omitKey('bolt12', offer)) : '' ])) -const mul = (msatoshi, quantity=1) => - quantity == 1 ? msatoshi : Big(msatoshi).mul(quantity).toFixed(0) +const mul = (amount_msat, quantity=1) => + quantity == 1 ? amount_msat : Big(amount_msat).mul(quantity).toFixed(0) diff --git a/client/src/views/pay.js b/client/src/views/pay.js index 5af6510..bfff8df 100644 --- a/client/src/views/pay.js +++ b/client/src/views/pay.js @@ -43,7 +43,7 @@ const confirmPay = payreq => ({ unitf, amtData, conf: { expert } }) => { // If the original offer had a fiat-denominated amount, exclude the 'msat' field // from being displayed in the changes list. The bitcoin and fiat amounts are // displayed separately. - if (payreq.offer && !payreq.offer.msatoshi && payreq.offer.amount && payreq.msatoshi) { + if (payreq.offer && !payreq.offer.amount_msat && payreq.offer.amount && payreq.amount_msat) { delete payreq.changes.msat } @@ -56,15 +56,15 @@ const confirmPay = payreq => ({ unitf, amtData, conf: { expert } }) => { , // Bitcoin-denominated amount that was previously displayed in fiat - (payreq.msatoshi && payreq.offer && payreq.offer.amount) + (payreq.amount_msat && payreq.offer && payreq.offer.amount) ? div('.form-group', [ - p('.mb-0.toggle-unit', [ 'Final amount: ', fmtSatAmountWithAlt(payreq.msatoshi, unitf) ]) + p('.mb-0.toggle-unit', [ 'Final amount: ', fmtSatAmountWithAlt(payreq.amount_msat, unitf) ]) , div('.form-text.text-muted', [ 'Quoted as: ', strong(fmtOfferFiatAmount(payreq.offer, payreq.quantity)) ]) ]) // Bitcoin denominated amount - : payreq.msatoshi - ? p('.toggle-unit', [ 'Amount: ', fmtSatAmountWithAlt(payreq.msatoshi, unitf) ]) + : payreq.amount_msat + ? p('.toggle-unit', [ 'Amount: ', fmtSatAmountWithAlt(payreq.amount_msat, unitf) ]) // Amount chosen by the payer : formGroup('Enter amount to pay:', amountField(amtData, 'custom_msat', true)) @@ -89,7 +89,7 @@ const confirmPay = payreq => ({ unitf, amtData, conf: { expert } }) => { , div('.form-buttons.mt-4', [ div('.mb-3', 'Do you confirm making this payment?') , button('.btn.btn-lg.btn-primary.mb-1', { attrs: { type: 'submit' } } - , payreq.msatoshi ? `Pay ${unitf(payreq.msatoshi)}` : 'Send Payment') + , payreq.amount_msat ? `Pay ${unitf(payreq.amount_msat)}` : 'Send Payment') , ' ' , a('.btn.btn-lg.btn-secondary.mb-1', { attrs: { href: '#/' } }, 'Cancel') ]) @@ -98,7 +98,7 @@ const confirmPay = payreq => ({ unitf, amtData, conf: { expert } }) => { ]) } -const displayAmountChange = ({ msatoshi: final, changes }, unitf) => { +const displayAmountChange = ({ amount_msat: final, changes }, unitf) => { const original = +changes.msat.slice(0, -4) const klass = final < original ? '.text-success' : '.text-danger' const change = final < original ? `${((1 - (final/original))*100).toFixed(1)}% less` diff --git a/client/src/views/recv.js b/client/src/views/recv.js index 12ede86..f003d0f 100644 --- a/client/src/views/recv.js +++ b/client/src/views/recv.js @@ -8,7 +8,7 @@ const recv = ({ amtData, offersEnabled, invUseOffer }) => : form({ attrs: { do: 'new-invoice' } }, [ h2('Receive payment') - , formGroup('Payment amount', amountField(amtData, 'msatoshi', false)) + , formGroup('Payment amount', amountField(amtData, 'amount_msat', false)) , formGroup('Description' , input('.form-control.form-control-lg', { attrs: { type: 'text', name: 'description', placeholder: '(optional)' } }) @@ -30,7 +30,7 @@ const invoice = inv => qrinv(inv).then(qr => ({ unitf, conf: { expert } }) => div('.row', [ div('.col-sm-6.text-center', [ h2('Receive payment') - , inv.msatoshi !== 'any' ? h3('.toggle-unit', fmtSatAmountWithAlt(inv.msatoshi, unitf)) : '' + , inv.amount_msat !== 'any' ? h3('.toggle-unit', fmtSatAmountWithAlt(inv.amount_msat, unitf)) : '' , small('.d-none.d-sm-block.text-muted.break-all.mt-3', inv.bolt11) ]) , div('.col-sm-6.text-center', [ diff --git a/client/src/views/util.js b/client/src/views/util.js index 14b931e..93c2b88 100644 --- a/client/src/views/util.js +++ b/client/src/views/util.js @@ -29,9 +29,9 @@ export const formGroup = (labelText, control, help) => div('.form-group', [ , help ? small('.form-text.text-muted', help) : '' ]) -export const amountField = ({ msatoshi, amount, step, unit }, msatField, required, placeholder) => +export const amountField = ({ amount_msat, amount, step, unit }, msatField, required, placeholder) => div('.input-group', [ - input({ attrs: { type: 'hidden', name: msatField }, props: { value: msatoshi } }) + input({ attrs: { type: 'hidden', name: msatField }, props: { value: amount_msat } }) , input('.form-control.form-control-lg' , { attrs: { type: 'number', step, min: step, name: 'amount', required, placeholder: placeholder||(required?'':'(optional)') } , props: { value: amount } }) @@ -64,9 +64,9 @@ export const omitKey = (k, { [k]: _, ...rest }) => rest export const pluralize = (strs, n) => `${strs[0]}${n}${strs[1]}${n == 0 || n>1 ? 's' : ''}` -export const fmtSatAmountWithAlt = (msatoshi, unitf) => span([ - strong(unitf(msatoshi)) -, fmtNullable(unitf(msatoshi, true), amt => small(` (${amt})`)) +export const fmtSatAmountWithAlt = (amount_msat, unitf) => span([ + strong(unitf(amount_msat)) +, fmtNullable(unitf(amount_msat, true), amt => small(` (${amt})`)) ]) export const fmtNullable = (value, format, null_format='') => @@ -80,5 +80,5 @@ export const fmtOfferFiatAmount = ({ amount, currency, minor_unit }, quantity=1) return `${amount_fmt} ${currency}` } -export const getPricePerUnit = ({ msatoshi, quantity }) => - Big(msatoshi).div(quantity).toFixed(0) +export const getPricePerUnit = ({ amount_msat, quantity }) => + Big(amount_msat).div(quantity).toFixed(0) diff --git a/client/src/worker.js b/client/src/worker.js index 2e33742..ffd1c23 100644 --- a/client/src/worker.js +++ b/client/src/worker.js @@ -3,7 +3,7 @@ if (process.env.NODE_ENV != 'development') { const cachePath = [ '', 'style.css', 'app.js', 'load-theme.js' , 'lib/instascan.js' - , 'swatch/dark/bootstrap.min.css' + , 'swatch/darkly/bootstrap.min.css' , 'fonts/typeface-open-sans/index.css' , 'fonts/typeface-open-sans/files/open-sans-latin-300.woff2' , 'fonts/typeface-open-sans/files/open-sans-latin-400.woff2' diff --git a/scripts/build.sh b/scripts/build.sh index 46f6713..56fb29c 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -5,7 +5,7 @@ mkdir -p dist rm -rf dist/* # Build server-side code -babel -d dist --ignore src/transport/granax-dep/node_modules src +npx babel -d dist --ignore src/transport/granax-dep/node_modules src # Copy granax-dep (on-demand installation for Tor) cp src/transport/granax-dep/{package,npm-shrinkwrap}.json dist/transport/granax-dep/ diff --git a/scripts/start.sh b/scripts/start.sh index 79ead6d..65d6083 100755 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -8,4 +8,4 @@ export VERBOSE=1 [ -f .env ] && source .env -babel-node src/cli.js "$@" +npx babel-node src/cli.js "$@" diff --git a/src/cmd.js b/src/cmd.js index 9e5f84c..50bf50b 100644 --- a/src/cmd.js +++ b/src/cmd.js @@ -82,7 +82,7 @@ export const commands = { } // Make BOLT12 msat amounts available as an integer, as they are for BOLT11 invoices - if (decoded.msatoshi == null && decoded.amount_msat) decoded.msatoshi = +decoded.amount_msat.slice(0, -4) + //if (decoded.msatoshi == null && decoded.amount_msat) decoded.msatoshi = +decoded.amount_msat.slice(0, -4) return decoded } else { @@ -104,8 +104,8 @@ export const commands = { // Fetch an invoice for the given offer, decode it and return the original offer alongside it // Some parameters are unsupported (recurrence/timeout). -, async _fetchinvoice(bolt12_offer, msatoshi, quantity, payer_note) { - const { invoice: bolt12_invoice, changes } = await this.fetchinvoice(bolt12_offer, msatoshi, quantity, null, null, null, null, payer_note) +, async _fetchinvoice(bolt12_offer, amount_msat, quantity, payer_note) { + const { invoice: bolt12_invoice, changes } = await this.fetchinvoice(bolt12_offer, amount_msat, quantity, null, null, null, null, payer_note) const invoice = await this._decode(bolt12_invoice) assert(invoice.type == 'bolt12 invoice', `Unexpected invoice type ${invoice.type}`) @@ -122,8 +122,8 @@ export const commands = { switch (decoded.type) { case 'bolt12 offer': assert(!decoded.recurrence, 'Offers with recurrence are unsupported') - assert(decoded.quantity_min == null || decoded.msatoshi || decoded.amount, 'Offers with quantity but no payment amount are unsupported') - assert(!decoded.send_invoice || decoded.msatoshi, 'send_invoice offers with no amount are unsupported') + assert(decoded.quantity_min == null || decoded.amount_msat || decoded.amount, 'Offers with quantity but no payment amount are unsupported') + assert(!decoded.send_invoice || decoded.amount_msat, 'send_invoice offers with no amount are unsupported') assert(!decoded.send_invoice || decoded.min_quantity == null, 'send_invoice offers with quantity are unsupported') break case 'bolt11 invoice': @@ -146,10 +146,11 @@ async function getChannel(ln, peerid, chanid) { const peer = await ln.listpeers(peerid).then(r => r.peers[0]) assert(peer, 'cannot find peer') - const chan = peer.channels.find(chan => chan.channel_id == chanid) - assert(chan, 'cannot find channel') + const channels = await ln.listpeerchannels(peerid).then(r => r.channels) + assert(channels, 'cannot find channels') - delete peer.channels + const chan = channels.find(chan => chan.channel_id == chanid) + assert(chan, 'cannot find channel') return { peer, chan } }