Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add on-chain withdraw UI #69

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Changelog

- ui: Add a new interface for withdrawing on-chain funds

## 0.2.5 - 2019-02-24

- Use the compact alphanumeric QR encoding mode for bech32 addresses
Expand Down
14 changes: 12 additions & 2 deletions client/src/intent.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ module.exports = ({ DOM, route, conf$, scan$, urihandler$ }) => {
, goNewChan$ = route('/channels/new')
, goDeposit$ = route('/deposit').mapTo('bech32')
.merge(click('[data-newaddr-type]').map(e => e.ownerTarget.dataset.newaddrType))
, goWithdraw$ = route('/withdraw')

// Display and confirm payment requests (from QR, lightning: URIs and manual entry)
, viewPay$ = O.merge(scan$, urihandler$).map(parseUri).filter(x => !!x)
Expand Down Expand Up @@ -71,18 +72,27 @@ module.exports = ({ DOM, route, conf$, scan$, urihandler$ }) => {
.map(d => ({ ...d, channel_capacity_sat: toSatCapacity(d.channel_capacity_msat) }))
, fundMaxChan$ = on('[name=channel-fund-max]', 'input')
.map(e => e.target.checked)
.merge(goNewChan$.mapTo(false))
.merge(goNewChan$.mapTo(false))
.startWith(false)

// Withdraw
, execWithdraw$ = submit('[do=exec-withdraw]')
.map(d => ({ ...d, amount_sat: toSatCapacity(d.amount_sat) }))
, withdrawAll$ = on('[name=withdraw-all]', 'input')
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps fundMaxChan$ and withdrawAll$ could be unified to a single stream of fundMax$, used both in the channel creation and withdraw pages?

Copy link
Author

@yoss1x yoss1x Mar 6, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a better way?

, fundMax$ = O.merge(

.map(e => e.target.checked)
.merge(goWithdraw$.mapTo(false))
.startWith(false)

return { conf$, page$
, goHome$, goScan$, goSend$, goRecv$, goNode$, goLogs$, goRpc$, goDeposit$
, goHome$, goScan$, goSend$, goRecv$, goNode$, goLogs$, goRpc$, goDeposit$, goWithdraw$
, goChan$, goNewChan$
, viewPay$, confPay$
, execRpc$, clrHist$
, newInv$, amtVal$
, togExp$, togTheme$, togUnit$
, feedStart$, togFeed$
, togChan$, updChan$, openChan$, closeChan$, fundMaxChan$
, execWithdraw$, withdrawAll$
, dismiss$
}
}
Expand Down
6 changes: 4 additions & 2 deletions client/src/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ const

module.exports = ({ dismiss$, togExp$, togTheme$, togUnit$, page$, goHome$, goRecv$, goChan$
, amtVal$, execRpc$, execRes$, clrHist$, feedStart$: feedStart_$, togFeed$, togChan$
, fundMaxChan$
, fundMaxChan$, withdrawAll$
, conf$: savedConf$
, req$$, error$, invoice$, incoming$, outgoing$, payments$, invoices$, funds$
, funded$, closed$
, withdrawn$
, btcusd$, info$, peers$ }) => {
const

Expand Down Expand Up @@ -68,6 +69,7 @@ module.exports = ({ dismiss$, togExp$, togTheme$, togUnit$, page$, goHome$, goRe
, outgoing$.map(p => [ 'success', `Sent payment of @{{${p.msatoshi}}}` ])
, funded$.map(c => [ 'success', `Opening channel for @{{${c.chan.msatoshi_total}}}, awaiting on-chain confirmation` ])
, closed$.map(c => [ 'success', `Channel ${c.chan.short_channel_id || c.chan.channel_id} is closing` ])
, withdrawn$.map(w => [ 'success', `Withdraw completed. t word-wrap: break-word;word-wrap: break-word; xid: ${w.txid}` ])
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like something slipped in here?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Completed" could imply "confirmed", which isn't true at this point. How about "sent" instead "completed"?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both fixed

, dismiss$.mapTo(null)
)
// hide "connection lost" errors when we get back online
Expand Down Expand Up @@ -171,7 +173,7 @@ module.exports = ({ dismiss$, togExp$, togTheme$, togUnit$, page$, goHome$, goRe
, info$: info$.startWith(null), peers$: peers$.startWith(null), channels$: channels$.startWith(null)
, feed$: feed$.startWith(null), feedStart$, feedActive$
, amtData$, chanActive$, rpcHist$
, fundMaxChan$
, fundMaxChan$, withdrawAll$
, msatusd$, btcusd$: btcusd$.startWith(null)
}).shareReplay(1)
}
5 changes: 3 additions & 2 deletions client/src/rpc.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ exports.parseRes = ({ HTTP, SSE }) => {
, invoice$: reply('invoice').map(r => ({ ...r.body, ...r.request.ctx }))
, outgoing$: reply('pay').map(r => ({ ...r.body, ...r.request.ctx }))
, newaddr$: reply('newaddr').map(r => ({ address: r.body.address, type: r.request.send.params[0] }))
, withdrawn$: reply('withdraw').map(r => ({tx: r.body.tx, txid: r.body.txid}))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have use for the full tx, we can keep just the txid.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

, funded$: reply('connectfund').map(r => r.body)
, closed$: reply('closeget').map(r => r.body)
, execRes$: reply('console').map(r => ({ ...r.request.send, res: r.body }))
Expand All @@ -43,7 +44,7 @@ exports.parseRes = ({ HTTP, SSE }) => {

// RPC commands to send
// NOTE: "connectfund" and "closeget" are custom rpc commands provided by the Spark server.
exports.makeReq = ({ viewPay$, confPay$, newInv$, goLogs$, goChan$, goNewChan$, goDeposit$, updChan$, openChan$, closeChan$, execRpc$ }) => O.merge(
exports.makeReq = ({ viewPay$, confPay$, newInv$, goLogs$, goChan$, goNewChan$, execWithdraw$, goDeposit$, updChan$, openChan$, closeChan$, execRpc$ }) => O.merge(
viewPay$.map(bolt11 => [ 'decodepay', [ bolt11 ], { bolt11 } ])
, confPay$.map(pay => [ 'pay', [ pay.bolt11, ...(pay.custom_msat ? [ pay.custom_msat ] : []) ], pay ])
, newInv$.map(inv => [ 'invoice', [ inv.msatoshi, inv.label, inv.description, INVOICE_TTL ], inv ])
Expand All @@ -52,7 +53,7 @@ exports.makeReq = ({ viewPay$, confPay$, newInv$, goLogs$, goChan$, goNewChan$,
, updChan$.mapTo( [ 'listpeers' ] )
, openChan$.map(d => [ 'connectfund', [ d.nodeuri, d.channel_capacity_sat, d.feerate ] ])
, closeChan$.map(d => [ 'closeget', [ d.peerid, d.chanid ] ])

, execWithdraw$.map(d => [ 'withdraw', [ d.address, d.amount_sat, d.feerate ] ])
, goDeposit$.map(type => [ 'newaddr', [ type ] ])

, timer(60000).mapTo( [ 'listinvoices', [], { bg: true } ])
Expand Down
3 changes: 2 additions & 1 deletion client/src/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import themeColors from '../theme-colors.json'
const isFunc = x => typeof x == 'function'

// DOM view
exports.vdom = ({ state$, goHome$, goScan$, goSend$, goRecv$, goChan$, goNewChan$, goNode$, goRpc$, payreq$, invoice$, newaddr$, logs$ }) => {
exports.vdom = ({ state$, goHome$, goScan$, goSend$, goRecv$, goChan$, goNewChan$, goWithdraw$, goNode$, goRpc$, payreq$, invoice$, newaddr$, logs$ }) => {
const body$ = O.merge(
// user actions
goHome$.startWith(1).mapTo(views.home)
Expand All @@ -15,6 +15,7 @@ exports.vdom = ({ state$, goHome$, goScan$, goSend$, goRecv$, goChan$, goNewChan
, goRecv$.mapTo(views.recv)
, goChan$.mapTo(views.channels)
, goNewChan$.mapTo(views.newChannel)
, goWithdraw$.mapTo(views.withdraw)
, goNode$.mapTo(views.nodeInfo)
, goRpc$.mapTo(views.rpc)

Expand Down
File renamed without changes.
3 changes: 2 additions & 1 deletion client/src/views/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module.exports = {
, ...require('./recv')
, ...require('./node')
, ...require('./channels')
, ...require('./onchain')
, ...require('./deposit')
, ...require('./withdraw')
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe have both views under onchain.js?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

, ...require('./expert')
}
5 changes: 4 additions & 1 deletion client/src/views/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ exports.nodeInfo = async ({ info, peers, conf: { expert } }) => {
process.env.BUILD_TARGET != 'web' ? a('.btn.btn-secondary.btn-sm', { attrs: { href: 'settings.html', rel: 'external' }}, 'Server settings') : ''
, ' '
, a('.btn.btn-secondary.btn-sm', { attrs: { href: '#/channels' }}, 'Channels')
, ' '
])
, p('.text-center.mt-4', [
, a('.btn.btn-secondary.btn-sm', { attrs: { href: '#/deposit' }}, 'Deposit')
, ' '
, a('.btn.btn-secondary.btn-sm', { attrs: { href: '#/withdraw' }}, 'Withdraw')
])
, expert ? yaml(info) : ''
])
Expand Down
32 changes: 32 additions & 0 deletions client/src/views/withdraw.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { div, h2, span, button, form, input } from '@cycle/dom'
import { formGroup, amountField, fancyCheckbox } from './util'

export const withdraw = ({ amtData, withdrawAll, obalance, unitf, conf: { unit, expert } }) => {
const availText = obalance != null ? `Available: ${unitf(obalance)}` : ''

return form({ attrs: { do: 'exec-withdraw' } }, [
h2('On-chain withdraw')

, formGroup('Address', input('.form-control.form-control-lg' , { attrs: {
name: 'address', required: true } }))

, formGroup('Withdraw Amount', div([
!withdrawAll
? amountField(amtData, 'amount_sat', true, availText)
: div('.input-group', [
input({ attrs: { type: 'hidden', name: 'amount_sat', value: 'all' } })
, input('.form-control.form-control-lg.disabled', { attrs: { disabled: true, placeholder: availText } })
, div('.input-group-append.toggle-unit', span('.input-group-text', unit))
])
, fancyCheckbox('withdraw-all', 'Withdraw All', withdrawAll, '.btn-sm')
]))

, expert ? formGroup('Fee rate', input('.form-control.form-control-lg'
, { attrs: { type: 'text', name: 'feerate', placeholder: '(optional)'
, pattern: '[0-9]+(perk[bw])?|slow|normal|urgent' } })) : ''

, div('.form-buttons', [
button('.btn.btn-lg.btn-primary', { attrs: { type: 'submit' } }, 'Withdraw')
])
])
}
2 changes: 1 addition & 1 deletion client/styl/style.styl
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ html
.alert
font-size 0.9rem
font-weight 400
word-break break-word
word-wrap break-word

.btn-link
cursor pointer
Expand Down