diff --git a/app/animated_checkmark.scss b/app/animated_checkmark.scss new file mode 100644 index 00000000..0eb592f2 --- /dev/null +++ b/app/animated_checkmark.scss @@ -0,0 +1,50 @@ +$curve: cubic-bezier(0.650, 0.000, 0.450, 1.000); + +.checkmark__circle { + stroke-dasharray: 166; + stroke-dashoffset: 166; + stroke-width: 2; + stroke-miterlimit: 10; + stroke: $main; + fill: none; + animation: stroke .6s $curve forwards; +} + +.checkmark { + width: 56px; + height: 56px; + border-radius: 50%; + stroke-width: 2; + stroke: #fff; + stroke-miterlimit: 10; + box-shadow: inset 0px 0px 0px $main; + animation: fill .4s ease-in-out .4s forwards, scale .3s ease-in-out .9s both; +} + +.checkmark__check { + transform-origin: 50% 50%; + stroke-dasharray: 48; + stroke-dashoffset: 48; + animation: stroke .3s $curve .8s forwards; +} + +@keyframes stroke { + 100% { + stroke-dashoffset: 0; + } +} + +@keyframes scale { + 0%, 100% { + transform: none; + } + 50% { + transform: scale3d(1.1, 1.1, 1); + } +} + +@keyframes fill { + 100% { + box-shadow: inset 0px 0px 0px 30px $main; + } +} \ No newline at end of file diff --git a/app/app.global.scss b/app/app.global.scss index aebe8940..d55b7f23 100644 --- a/app/app.global.scss +++ b/app/app.global.scss @@ -2,6 +2,7 @@ @import 'reset.scss'; @import 'variables.scss'; @import 'tooltip.scss'; +@import 'animated_checkmark.scss'; @font-face { font-family: 'Roboto'; @@ -34,4 +35,35 @@ body { &--before-close { opacity: 0; } +} + +.load-draw-svg { + stroke-dasharray: 211; + stroke-dashoffset: 2110; + stroke-width: 2; + stroke-linecap: round; + animation: dash 15s linear infinite; + fill-opacity: 0; + stroke: $main; +} + +@keyframes dash { + 0% { + stroke-dashoffset: 2110; + opacity: 1; + stroke: $main; + } + 15%{ + opacity: 1; + stroke: $main; + } + 70%{ + opacity: 1; + stroke: $main; + } + 100%{ + stroke-dashoffset: 0; + opacity: 1; + stroke: darken($main, 10%); + } } \ No newline at end of file diff --git a/app/components/AnimatedCheckmark/AnimatedCheckmark.js b/app/components/AnimatedCheckmark/AnimatedCheckmark.js new file mode 100644 index 00000000..9e9240a3 --- /dev/null +++ b/app/components/AnimatedCheckmark/AnimatedCheckmark.js @@ -0,0 +1,6 @@ +import React from 'react' +import Isvg from 'react-inlinesvg' + +const AnimatedCheckmark = () => + +export default AnimatedCheckmark diff --git a/app/components/AnimatedCheckmark/checkmark.svg b/app/components/AnimatedCheckmark/checkmark.svg new file mode 100644 index 00000000..4433cf25 --- /dev/null +++ b/app/components/AnimatedCheckmark/checkmark.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/components/AnimatedCheckmark/index.js b/app/components/AnimatedCheckmark/index.js new file mode 100644 index 00000000..984b70ff --- /dev/null +++ b/app/components/AnimatedCheckmark/index.js @@ -0,0 +1,3 @@ +import AnimatedCheckmark from './AnimatedCheckmark' + +export default AnimatedCheckmark diff --git a/app/components/LoadingBolt/LoadingBolt.js b/app/components/LoadingBolt/LoadingBolt.js new file mode 100644 index 00000000..13351372 --- /dev/null +++ b/app/components/LoadingBolt/LoadingBolt.js @@ -0,0 +1,15 @@ +import React from 'react' +import path from 'path' +import Isvg from 'react-inlinesvg' +import styles from './LoadingBolt.scss' + +const LoadingBolt = () => ( +
+
+ +

loading

+
+
+) + +export default LoadingBolt diff --git a/app/components/LoadingBolt/LoadingBolt.scss b/app/components/LoadingBolt/LoadingBolt.scss new file mode 100644 index 00000000..11f2ceb6 --- /dev/null +++ b/app/components/LoadingBolt/LoadingBolt.scss @@ -0,0 +1,29 @@ +@import '../../variables.scss'; + +.container { + z-index: 1000; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: $white; + text-align: center; +} + +.content { + position: relative; + top: calc(50% - 250px); + min-height: 250px; + + .bolt svg { + height: 150px; + width: 150px; + } + + h1 { + color: $main; + margin-top: 50px; + font-size: 25px; + } +} diff --git a/app/components/LoadingBolt/index.js b/app/components/LoadingBolt/index.js new file mode 100644 index 00000000..04af1d9a --- /dev/null +++ b/app/components/LoadingBolt/index.js @@ -0,0 +1,3 @@ +import LoadingBolt from './LoadingBolt' + +export default LoadingBolt diff --git a/app/lnd/config/index.js b/app/lnd/config/index.js index ddf42d90..0b79a5e6 100644 --- a/app/lnd/config/index.js +++ b/app/lnd/config/index.js @@ -7,5 +7,5 @@ import { userInfo } from 'os' export default { lightningRpc: `${__dirname}/rpc.proto`, lightningHost: 'localhost:10009', - cert: `/Users/${userInfo().username}/Library/Application\ Support/Lnd/tls.cert` + cert: `/Users/${userInfo().username}/Library/Application Support/Lnd/tls.cert` } diff --git a/app/lnd/methods/index.js b/app/lnd/methods/index.js index ab3129a8..a891a453 100644 --- a/app/lnd/methods/index.js +++ b/app/lnd/methods/index.js @@ -7,16 +7,15 @@ import * as peersController from './peersController' import * as paymentsController from './paymentsController' import * as networkController from './networkController' -//TODO - GetChanInfo -//TODO - GetTransactions -//TODO - GetNodeInfo -//TODO - DescribeGraph -//TODO - GetNetworkInfo -//TODO - QueryRoutes -//TODO - DecodePayReq -//TODO - SendPayment -//TODO - DeleteAllPayments - +// TODO - GetChanInfo +// TODO - GetTransactions +// TODO - GetNodeInfo +// TODO - DescribeGraph +// TODO - GetNetworkInfo +// TODO - QueryRoutes +// TODO - DecodePayReq +// TODO - SendPayment +// TODO - DeleteAllPayments export default function (lnd, event, msg, data) { @@ -95,13 +94,10 @@ export default function (lnd, event, msg, data) { break case 'sendCoins': // Transaction looks like { txid: String } - // { addr, amount } = data + // { amount, addr } = data walletController.sendCoins(lnd, data) - .then((transaction) => { - console.log('transaction: ', transaction) - event.sender.send('sendSuccessful', { transaction }) - }) - .catch(error => console.log('sendcoins error: ', error)) + .then(({ txid }) => event.sender.send('sendSuccessful', { amount: data.amount, addr: data.addr, txid })) + .catch(error => event.sender.send('sendCoinsError', { error })) break case 'openChannel': // Response is empty. Streaming updates on channel status and updates diff --git a/app/reducers/form.js b/app/reducers/form.js index b1e5033d..e5b0cfeb 100644 --- a/app/reducers/form.js +++ b/app/reducers/form.js @@ -1,8 +1,12 @@ +import { createSelector } from 'reselect' +import bitcoin from 'bitcoinjs-lib' + // Initial State const initialState = { modalOpen: false, formType: 'pay', amount: '0', + onchainAmount: '0', message: '', pubkey: '', payment_request: '' @@ -12,6 +16,7 @@ const initialState = { // ------------------------------------ export const SET_FORM = 'SET_FORM' export const SET_AMOUNT = 'SET_AMOUNT' +export const SET_ONCHAIN_AMOUNT = 'SET_ONCHAIN_AMOUNT' export const SET_MESSAGE = 'SET_MESSAGE' export const SET_PUBKEY = 'SET_PUBKEY' export const SET_PAYMENT_REQUEST = 'SET_PAYMENT_REQUEST' @@ -35,6 +40,13 @@ export function setAmount(amount) { } } +export function setOnchainAmount(onchainAmount) { + return { + type: SET_ONCHAIN_AMOUNT, + onchainAmount + } +} + export function setMessage(message) { return { type: SET_MESSAGE, @@ -68,12 +80,40 @@ export function resetForm() { const ACTION_HANDLERS = { [SET_FORM]: (state, { modalOpen, formType }) => ({ ...state, modalOpen, formType }), [SET_AMOUNT]: (state, { amount }) => ({ ...state, amount }), + [SET_ONCHAIN_AMOUNT]: (state, { onchainAmount }) => ({ ...state, onchainAmount }), [SET_MESSAGE]: (state, { message }) => ({ ...state, message }), [SET_PUBKEY]: (state, { pubkey }) => ({ ...state, pubkey }), [SET_PAYMENT_REQUEST]: (state, { payment_request }) => ({ ...state, payment_request }), [RESET_FORM]: () => (initialState) } +// ------------------------------------ +// Selector +// ------------------------------------ +const formSelectors = {} +const paymentRequestSelector = state => state.form.payment_request + +formSelectors.isOnchain = createSelector( + paymentRequestSelector, + (paymentRequest) => { + // TODO: work with bitcoin-js to fix p2wkh error and make testnet/mainnet dynamic + try { + bitcoin.address.toOutputScript(paymentRequest, bitcoin.networks.testnet) + return true + } catch (e) { + return false + } + } +) + +// TODO: Add more robust logic to detect a LN payment request +formSelectors.isLn = createSelector( + paymentRequestSelector, + paymentRequest => paymentRequest.length === 124 +) + +export { formSelectors } + // ------------------------------------ // Reducer // ------------------------------------ diff --git a/app/reducers/index.js b/app/reducers/index.js index 9126ea8b..c736b6e3 100644 --- a/app/reducers/index.js +++ b/app/reducers/index.js @@ -9,6 +9,7 @@ import peers from './peers' import channels from './channels' import form from './form' import invoice from './invoice' +import modal from './modal' import address from './address' const rootReducer = combineReducers({ @@ -21,6 +22,7 @@ const rootReducer = combineReducers({ channels, form, invoice, + modal, address }) diff --git a/app/reducers/ipc.js b/app/reducers/ipc.js index 15a92e75..78988e07 100644 --- a/app/reducers/ipc.js +++ b/app/reducers/ipc.js @@ -19,7 +19,7 @@ import { pushclosechannelstatus } from './channels' -import { receivePayments, paymentSuccessful } from './payment' +import { receivePayments, paymentSuccessful, sendSuccessful, sendCoinsError } from './payment' import { receiveInvoices, createdInvoice, receiveFormInvoice } from './invoice' import { receiveBalance } from './balance' @@ -40,6 +40,8 @@ const ipc = createIpc({ receiveBalance, paymentSuccessful, + sendSuccessful, + sendCoinsError, channelSuccessful, pushchannelupdated, diff --git a/app/reducers/modal.js b/app/reducers/modal.js new file mode 100644 index 00000000..dd1a4591 --- /dev/null +++ b/app/reducers/modal.js @@ -0,0 +1,47 @@ +// ------------------------------------ +// Initial State +// ------------------------------------ +const initialState = { + modalType: null, + modalProps: {} +} + +// ------------------------------------ +// Constants +// ------------------------------------ +export const SHOW_MODAL = 'SHOW_MODAL' +export const HIDE_MODAL = 'HIDE_MODAL' + +// ------------------------------------ +// Actions +// ------------------------------------ +export function showModal(modalType, modalProps) { + return { + type: SHOW_MODAL, + modalType, + modalProps + } +} + +export function hideModal() { + return { + type: HIDE_MODAL + } +} + +// ------------------------------------ +// Action Handlers +// ------------------------------------ +const ACTION_HANDLERS = { + [SHOW_MODAL]: (state, { modalType, modalProps }) => ({ ...state, modalType, modalProps }), + [HIDE_MODAL]: () => initialState +} + +// ------------------------------------ +// Reducer +// ------------------------------------ +export default function modalReducer(state = initialState, action) { + const handler = ACTION_HANDLERS[action.type] + + return handler ? handler(state, action) : state +} diff --git a/app/reducers/payment.js b/app/reducers/payment.js index 448214f4..4bfdf453 100644 --- a/app/reducers/payment.js +++ b/app/reducers/payment.js @@ -1,5 +1,8 @@ import { createSelector } from 'reselect' import { ipcRenderer } from 'electron' +import { btc, usd } from '../utils' +import { setForm, resetForm } from './form' +import { showModal } from './modal' // ------------------------------------ // Constants @@ -10,6 +13,7 @@ export const GET_PAYMENTS = 'GET_PAYMENTS' export const RECEIVE_PAYMENTS = 'RECEIVE_PAYMENTS' export const SEND_PAYMENT = 'SEND_PAYMENT' + export const PAYMENT_SUCCESSFULL = 'PAYMENT_SUCCESSFULL' export const PAYMENT_FAILED = 'PAYMENT_FAILED' @@ -62,10 +66,32 @@ export const payInvoice = paymentRequest => (dispatch) => { ipcRenderer.send('lnd', { msg: 'sendPayment', data: { paymentRequest } }) } +export const sendCoins = ({ value, addr, currency, rate }) => (dispatch) => { + const amount = currency === 'usd' ? btc.btcToSatoshis(usd.usdToBtc(value, rate)) : btc.btcToSatoshis(value) + dispatch(sendPayment()) + ipcRenderer.send('lnd', { msg: 'sendCoins', data: { amount, addr } }) +} + // Receive IPC event for successful payment // TODO: Add payment to state, not a total re-fetch export const paymentSuccessful = () => fetchPayments() +export const sendSuccessful = (event, { amount, addr, txid }) => (dispatch) => { + // Close the form modal once the payment was succesful + dispatch(setForm({ modalOpen: false })) + // Show successful payment state + dispatch(showModal('SUCCESSFUL_SEND_COINS', { txid, amount, addr })) + // TODO: Add successful on-chain payment to payments list once payments list supports on-chain and LN + // dispatch({ type: PAYMENT_SUCCESSFULL, payment: { amount, addr, txid, pending: true } }) + dispatch({ type: PAYMENT_SUCCESSFULL }) + // Reset the payment form + dispatch(resetForm()) +} + +export const sendCoinsError = () => (dispatch) => { + dispatch({ type: PAYMENT_FAILED }) +} + // ------------------------------------ // Action Handlers @@ -73,10 +99,10 @@ export const paymentSuccessful = () => fetchPayments() const ACTION_HANDLERS = { [SET_PAYMENT]: (state, { payment }) => ({ ...state, payment }), [GET_PAYMENTS]: state => ({ ...state, paymentLoading: true }), + [SEND_PAYMENT]: state => ({ ...state, sendingPayment: true }), [RECEIVE_PAYMENTS]: (state, { payments }) => ({ ...state, paymentLoading: false, payments }), - [PAYMENT_SUCCESSFULL]: (state, { payment }) => ( - { ...state, paymentLoading: false, payments: [payment, ...state.payments] } - ) + [PAYMENT_SUCCESSFULL]: state => ({ ...state, sendingPayment: false }), + [PAYMENT_FAILED]: state => ({ ...state, sendingPayment: false }) } const paymentSelectors = {} @@ -93,6 +119,7 @@ export { paymentSelectors } // Reducer // ------------------------------------ const initialState = { + sendingPayment: false, paymentLoading: false, payments: [], payment: null diff --git a/app/routes/activity/components/components/Payments.js b/app/routes/activity/components/components/Payments.js index 4321a185..b65875e4 100644 --- a/app/routes/activity/components/components/Payments.js +++ b/app/routes/activity/components/components/Payments.js @@ -62,36 +62,38 @@ const Payments = ({ { payments.map((paymentItem, index) => - (
  • setPayment(paymentItem)}> -
    -
    {paymentItem.path[0]}
    -
    -
    -
    - {paymentItem.creation_date * 1000} + ( +
  • setPayment(paymentItem)}> +
    +
    {paymentItem.path[0]}
    - -
    - - { - ticker.currency === 'usd' ? - btc.satoshisToUsd(paymentItem.fee, currentTicker.price_usd) - : - btc.satoshisToBtc(paymentItem.fee) - } - -
    -
    - - { - ticker.currency === 'usd' ? - btc.satoshisToUsd(paymentItem.value, currentTicker.price_usd) - : - btc.satoshisToBtc(paymentItem.value) - } - -
    -
  • ) +
    +
    + {paymentItem.creation_date * 1000} +
    +
    +
    + + { + ticker.currency === 'usd' ? + btc.satoshisToUsd(paymentItem.fee, currentTicker.price_usd) + : + btc.satoshisToBtc(paymentItem.fee) + } + +
    +
    + + { + ticker.currency === 'usd' ? + btc.satoshisToUsd(paymentItem.value, currentTicker.price_usd) + : + btc.satoshisToBtc(paymentItem.value) + } + +
    + + ) ) } diff --git a/app/routes/app/components/App.js b/app/routes/app/components/App.js index 3a51c121..421be3fc 100644 --- a/app/routes/app/components/App.js +++ b/app/routes/app/components/App.js @@ -1,5 +1,6 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' +import ModalRoot from './components/ModalRoot' import Form from './components/Form' import Nav from './components/Nav' import styles from './App.scss' @@ -15,11 +16,14 @@ class App extends Component { render() { const { + modal: { modalType, modalProps }, + hideModal, ticker, balance, invoice: { formInvoice }, form, setAmount, + setOnchainAmount, setMessage, setPubkey, setPaymentRequest, @@ -29,8 +33,11 @@ class App extends Component { setForm, createInvoice, payInvoice, + sendCoins, fetchInvoice, currentTicker, + isOnchain, + isLn, children } = this.props @@ -38,10 +45,18 @@ class App extends Component { return (
    +
    setForm({ modalOpen: false })} setAmount={setAmount} + setOnchainAmount={setOnchainAmount} setMessage={setMessage} setPubkey={setPubkey} setPaymentRequest={setPaymentRequest} @@ -51,9 +66,12 @@ class App extends Component { form={form} createInvoice={createInvoice} payInvoice={payInvoice} + sendCoins={sendCoins} fetchInvoice={fetchInvoice} formInvoice={formInvoice} currentTicker={currentTicker} + isOnchain={isOnchain} + isLn={isLn} />
    +) Form.propTypes = { + payment: PropTypes.object.isRequired, form: PropTypes.object.isRequired, ticker: PropTypes.object.isRequired, setAmount: PropTypes.func.isRequired, + setOnchainAmount: PropTypes.func.isRequired, setMessage: PropTypes.func.isRequired, setPaymentRequest: PropTypes.func.isRequired, isOpen: PropTypes.bool.isRequired, close: PropTypes.func.isRequired, createInvoice: PropTypes.func.isRequired, payInvoice: PropTypes.func.isRequired, + sendCoins: PropTypes.func.isRequired, fetchInvoice: PropTypes.func.isRequired, formInvoice: PropTypes.object.isRequired, - currentTicker: PropTypes.object.isRequired + currentTicker: PropTypes.object.isRequired, + isOnchain: PropTypes.bool.isRequired, + isLn: PropTypes.bool.isRequired } export default Form diff --git a/app/routes/app/components/components/Form/Form.scss b/app/routes/app/components/components/Form/Form.scss index 282d0686..191f89a8 100644 --- a/app/routes/app/components/components/Form/Form.scss +++ b/app/routes/app/components/components/Form/Form.scss @@ -128,67 +128,6 @@ } } - .peersContainer { - width: 100%; - margin: 10px 0 50px 0; - - h4 { - text-transform: uppercase; - color: $black; - letter-spacing: 2; - font-weight: bold; - margin-bottom: 20px; - padding: 10px 0; - } - - .peers { - overflow-y: scroll; - width: 100%; - } - - .peer { - position: relative; - width: 100%; - border-top: 1px solid $traditionalgrey; - border-right: 1px solid transparent; - border-bottom: 1px solid transparent; - border-left: 1px solid transparent; - padding: 5px; - cursor: pointer; - transition: all 0.25s; - - &:hover { - background: lighten($main, 20%); - border-radius: 6px; - border: 1px solid $main; - - svg { - visibility: visible; - } - } - - p { - margin: 3px 0; - } - - .address { - font-size: 18px; - } - - .pubkey { - font-size: 12px; - } - - svg { - visibility: hidden; - position: absolute; - top: calc(50% - 8px); - right: 5%; - color: $black; - } - } - } - .buttonGroup { width: 100%; display: flex; diff --git a/app/routes/app/components/components/Form/components/Pay/Pay.js b/app/routes/app/components/components/Form/components/Pay/Pay.js new file mode 100644 index 00000000..a94fae17 --- /dev/null +++ b/app/routes/app/components/components/Form/components/Pay/Pay.js @@ -0,0 +1,146 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { FaBolt, FaChain } from 'react-icons/lib/fa' +import CurrencyIcon from '../../../../../../../components/CurrencyIcon' +import LoadingBolt from '../../../../../../../components/LoadingBolt' +import { btc } from '../../../../../../../utils' +import styles from './Pay.scss' + +class Pay extends Component { + componentDidUpdate(prevProps) { + const { isOnchain, isLn, fetchInvoice, payment_request } = this.props + + if (isOnchain) { this.amountInput.focus() } + if ((prevProps.payment_request !== payment_request) && isLn) { fetchInvoice(payment_request) } + } + + render() { + const { + sendingPayment, + invoiceAmount, + onchainAmount, + setOnchainAmount, + payment_request, + setPaymentRequest, + payInvoice, + sendCoins, + currentTicker, + currency, + crypto, + isOnchain, + isLn + } = this.props + + const payClicked = () => { + if (!isOnchain && !isLn) { return } + + if (isOnchain) { sendCoins({ value: onchainAmount, addr: payment_request, currency, rate: currentTicker.price_usd }) } + if (isLn) { payInvoice(payment_request) } + } + + const calculateAmount = value => (currency === 'usd' ? btc.satoshisToUsd(value, currentTicker.price_usd) : btc.satoshisToBtc(value)) + + return ( +
    + { + sendingPayment ? + + : + null + + } +
    +
    + + this.amountInput = input} // eslint-disable-line + size='' + style={ + isLn ? + { width: '75%', fontSize: '85px' } + : + { width: `${onchainAmount.length > 1 ? (onchainAmount.length * 15) - 5 : 25}%`, fontSize: `${190 - (onchainAmount.length ** 2)}px` } + } + value={isLn ? calculateAmount(invoiceAmount) : onchainAmount} + onChange={event => setOnchainAmount(event.target.value)} + id='amount' + readOnly={isLn} + /> +
    +
    +
    + {(() => { + if (isOnchain) { + return ( + {`You're about to send ${onchainAmount} ${currency.toUpperCase()} on-chain which should take around 10 minutes`} + ) + } else if (isLn) { + return ( + {`You're about to send ${calculateAmount(invoiceAmount)} ${currency.toUpperCase()} over the Lightning Network which will be instant`} // eslint-disable-line + ) + } + return null + })()} +
    + +
    + setPaymentRequest(event.target.value)} + id='paymentRequest' + /> +
    +
    +
    +
    + Pay +
    +
    +
    +
    + ) + } +} + +Pay.propTypes = { + sendingPayment: PropTypes.bool.isRequired, + invoiceAmount: PropTypes.string.isRequired, + onchainAmount: PropTypes.string.isRequired, + setOnchainAmount: PropTypes.func.isRequired, + payment_request: PropTypes.string.isRequired, + setPaymentRequest: PropTypes.func.isRequired, + fetchInvoice: PropTypes.func.isRequired, + payInvoice: PropTypes.func.isRequired, + sendCoins: PropTypes.func.isRequired, + currentTicker: PropTypes.object.isRequired, + currency: PropTypes.string.isRequired, + crypto: PropTypes.string.isRequired, + isOnchain: PropTypes.bool.isRequired, + isLn: PropTypes.bool.isRequired +} + +export default Pay diff --git a/app/routes/app/components/components/Form/components/Pay/Pay.scss b/app/routes/app/components/components/Form/components/Pay/Pay.scss new file mode 100644 index 00000000..90bff877 --- /dev/null +++ b/app/routes/app/components/components/Form/components/Pay/Pay.scss @@ -0,0 +1,142 @@ +@import '../../../../../../../variables.scss'; + +.container { + margin: 0 auto; + display: flex; + flex-direction: column; + height: 75vh; + justify-content: center; + align-items: center; +} + +.amountContainer { + color: $main; + display: flex; + justify-content: center; + min-height: 120px; + margin-bottom: 20px; + min-height: 175px; + + &.ln { + opacity: 0.75; + } + + label, input[type=text] { + color: inherit; + display: inline-block; + vertical-align: top; + padding: 0; + } + + label { + svg { + width: 85px; + height: 85px; + } + + svg[data-icon='ltc'] { + margin-right: 10px; + + g { + transform: scale(1.75) translate(-5px, -5px); + } + } + } + + input[type=text] { + width: 100px; + font-size: 180px; + border: none; + outline: 0; + -webkit-appearance: none; + } +} + +.inputContainer { + position: relative; + width: 100%; + padding: 40px 0; + cursor: pointer; + + .info { + margin-bottom: 10px; + min-height: 19px; + } + + .paymentIcon { + position: absolute; + width: 20%; + left: calc(-12.5% - 75px); + top: 42px; + color: $main; + font-size: 50px; + text-align: center; + + span { + text-transform: uppercase; + display: block; + font-size: 12px; + font-weight: 200; + } + } +} + +.input { + display: flex; + justify-content: center; + font-size: 18px; + height: auto; + min-height: 55px; + border: 1px solid $traditionalgrey; + border-radius: 6px; + position: relative; + padding: 0 20px; + + label, input[type=text] { + font-size: inherit; + } + + label { + padding-top: 19px; + padding-bottom: 12px; + color: $traditionalgrey; + } + + input[type=text] { + width: 100%; + border: none; + outline: 0; + -webkit-appearance: none; + height: 55px; + padding: 0 10px; + } +} + +.buttonGroup { + width: 100%; + display: flex; + flex-direction: row; + border-radius: 6px; + overflow: hidden; + + .button { + cursor: pointer; + height: 55px; + min-height: 55px; + text-transform: none; + font-size: 18px; + transition: opacity .2s ease-out; + background: $main; + color: $white; + border: none; + font-weight: 500; + padding: 0; + width: 100%; + text-align: center; + line-height: 55px; + + &:first-child { + border-right: 1px solid lighten($main, 20%); + } + } +} \ No newline at end of file diff --git a/app/routes/app/components/components/Form/components/Pay/index.js b/app/routes/app/components/components/Form/components/Pay/index.js new file mode 100644 index 00000000..89680350 --- /dev/null +++ b/app/routes/app/components/components/Form/components/Pay/index.js @@ -0,0 +1,3 @@ +import Pay from './Pay' + +export default Pay diff --git a/app/routes/app/components/components/Form/components/Request/Request.js b/app/routes/app/components/components/Form/components/Request/Request.js new file mode 100644 index 00000000..0b64ab7f --- /dev/null +++ b/app/routes/app/components/components/Form/components/Request/Request.js @@ -0,0 +1,68 @@ +import React from 'react' +import PropTypes from 'prop-types' +import CurrencyIcon from '../../../../../../../components/CurrencyIcon' +import styles from './Request.scss' + +const Request = ({ + amount, + setAmount, + setMessage, + createInvoice, + message, + currentTicker, + currency, + crypto, + close +}) => { + const requestClicked = () => { + createInvoice(amount, message, currency, currentTicker.price_usd) + close() + } + + return ( +
    +
    + + 1 ? (amount.length * 15) - 5 : 25}%`, fontSize: `${190 - (amount.length ** 2)}px` }} + value={amount} + onChange={event => setAmount(event.target.value)} + id='amount' + /> +
    +
    + + setMessage(event.target.value)} + id='paymentRequest' + /> +
    +
    +
    + Request +
    +
    +
    + ) +} + +Request.propTypes = { + amount: PropTypes.string.isRequired, + setAmount: PropTypes.func.isRequired, + setMessage: PropTypes.func.isRequired, + createInvoice: PropTypes.func.isRequired, + message: PropTypes.string.isRequired, + currentTicker: PropTypes.object.isRequired, + currency: PropTypes.string.isRequired, + crypto: PropTypes.string.isRequired, + close: PropTypes.func.isRequired +} + +export default Request diff --git a/app/routes/app/components/components/Form/components/Request/Request.scss b/app/routes/app/components/components/Form/components/Request/Request.scss new file mode 100644 index 00000000..7a18ac05 --- /dev/null +++ b/app/routes/app/components/components/Form/components/Request/Request.scss @@ -0,0 +1,110 @@ +@import '../../../../../../../variables.scss'; + +.container { + margin: 0 auto; + display: flex; + flex-direction: column; + height: 75vh; + justify-content: center; + align-items: center; +} + +.amountContainer { + color: $main; + display: flex; + justify-content: center; + min-height: 120px; + margin-bottom: 20px; + + label, input[type=text] { + color: inherit; + display: inline-block; + vertical-align: top; + padding: 0; + } + + label { + svg { + width: 85px; + height: 85px; + } + + svg[data-icon='ltc'] { + margin-right: 10px; + + g { + transform: scale(1.75) translate(-5px, -5px); + } + } + } + + input[type=text] { + width: 100px; + font-size: 180px; + border: none; + outline: 0; + -webkit-appearance: none; + } +} + +.inputContainer { + width: 100%; + display: flex; + justify-content: center; + font-size: 18px; + height: auto; + min-height: 55px; + margin-bottom: 20px; + border: 1px solid $traditionalgrey; + border-radius: 6px; + position: relative; + padding: 0 20px; + + label, input[type=text] { + font-size: inherit; + } + + label { + padding-top: 19px; + padding-bottom: 12px; + color: $traditionalgrey; + } + + input[type=text] { + width: 100%; + border: none; + outline: 0; + -webkit-appearance: none; + height: 55px; + padding: 0 10px; + } +} + +.buttonGroup { + width: 100%; + display: flex; + flex-direction: row; + border-radius: 6px; + overflow: hidden; + + .button { + cursor: pointer; + height: 55px; + min-height: 55px; + text-transform: none; + font-size: 18px; + transition: opacity .2s ease-out; + background: $main; + color: $white; + border: none; + font-weight: 500; + padding: 0; + width: 100%; + text-align: center; + line-height: 55px; + + &:first-child { + border-right: 1px solid lighten($main, 20%); + } + } +} \ No newline at end of file diff --git a/app/routes/app/components/components/Form/components/Request/index.js b/app/routes/app/components/components/Form/components/Request/index.js new file mode 100644 index 00000000..29b01605 --- /dev/null +++ b/app/routes/app/components/components/Form/components/Request/index.js @@ -0,0 +1,3 @@ +import Request from './Request' + +export default Request diff --git a/app/routes/app/components/components/ModalRoot/ModalRoot.js b/app/routes/app/components/components/ModalRoot/ModalRoot.js new file mode 100644 index 00000000..1146c57e --- /dev/null +++ b/app/routes/app/components/components/ModalRoot/ModalRoot.js @@ -0,0 +1,42 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { MdClose } from 'react-icons/lib/md' +import SuccessfulSendCoins from './SuccessfulSendCoins' +import styles from './ModalRoot.scss' + +const MODAL_COMPONENTS = { + SUCCESSFUL_SEND_COINS: SuccessfulSendCoins + /* other modals */ +} + +const ModalRoot = ({ modalType, modalProps, hideModal, currentTicker, currency }) => { + if (!modalType) { return null } + + const SpecificModal = MODAL_COMPONENTS[modalType] + return ( +
    +
    +
    + +
    + + +
    +
    + ) +} + +ModalRoot.propTypes = { + modalType: PropTypes.string, + modalProps: PropTypes.object.isRequired, + hideModal: PropTypes.func.isRequired, + currentTicker: PropTypes.object.isRequired, + currency: PropTypes.string.isRequired +} + +export default ModalRoot diff --git a/app/routes/app/components/components/ModalRoot/ModalRoot.scss b/app/routes/app/components/components/ModalRoot/ModalRoot.scss new file mode 100644 index 00000000..b20daf3a --- /dev/null +++ b/app/routes/app/components/components/ModalRoot/ModalRoot.scss @@ -0,0 +1,42 @@ +@import '../../../../../variables.scss'; + +.container { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 10; + background: #fff; +} + +.content { + position: relative; + height: 100vh; + margin: 5%; +} + +.esc { + position: absolute; + top: 0; + right: 0; + color: $darkestgrey; + cursor: pointer; + padding: 20px; + border-radius: 50%; + + &:hover { + color: $bluegrey; + background: $darkgrey; + } + + &:active { + color: $white; + background: $main; + } + + svg { + width: 32px; + height: 32px; + } +} \ No newline at end of file diff --git a/app/routes/app/components/components/ModalRoot/SuccessfulSendCoins/SuccessfulSendCoins.js b/app/routes/app/components/components/ModalRoot/SuccessfulSendCoins/SuccessfulSendCoins.js new file mode 100644 index 00000000..bdccac84 --- /dev/null +++ b/app/routes/app/components/components/ModalRoot/SuccessfulSendCoins/SuccessfulSendCoins.js @@ -0,0 +1,40 @@ +import { shell } from 'electron' +import React from 'react' +import PropTypes from 'prop-types' +import AnimatedCheckmark from '../../../../../../components/AnimatedCheckmark' +import { btc } from '../../../../../../utils' +import styles from './SuccessfulSendCoins.scss' + +const SuccessfulSendCoins = ({ amount, addr, txid, hideModal, currentTicker, currency }) => { + const calculatedAmount = currency === 'usd' ? btc.satoshisToUsd(amount, currentTicker.price_usd) : btc.satoshisToBtc(amount) + + return ( +
    + +

    + You  + shell.openExternal(`https://testnet.smartbit.com.au/tx/${txid}`)}>sent  + {calculatedAmount} {currency.toUpperCase()}  + to  + {addr} +

    +
    + Done +
    +
    + ) +} + +SuccessfulSendCoins.propTypes = { + amount: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired, + addr: PropTypes.string.isRequired, + txid: PropTypes.string.isRequired, + hideModal: PropTypes.func.isRequired, + currentTicker: PropTypes.object.isRequired, + currency: PropTypes.string.isRequired +} + +export default SuccessfulSendCoins diff --git a/app/routes/app/components/components/ModalRoot/SuccessfulSendCoins/SuccessfulSendCoins.scss b/app/routes/app/components/components/ModalRoot/SuccessfulSendCoins/SuccessfulSendCoins.scss new file mode 100644 index 00000000..b562cf30 --- /dev/null +++ b/app/routes/app/components/components/ModalRoot/SuccessfulSendCoins/SuccessfulSendCoins.scss @@ -0,0 +1,37 @@ +@import '../../../../../../variables.scss'; + +.container { + position: relative; + min-height: 250px; + top: calc(50% - 250px); + text-align: center; + + h1 { + font-size: 20px; + margin: 50px 0; + + .link { + cursor: pointer; + color: $main; + text-decoration: underline; + } + + .amount, .addr { + font-weight: bold; + } + } + + .button { + text-align: center; + border-radius: 8px; + background: $main; + padding: 20px 10px; + font-weight: bold; + cursor: pointer; + text-transform: uppercase; + letter-spacing: .2px; + color: $white; + width: 15%; + margin: 0 auto; + } +} \ No newline at end of file diff --git a/app/routes/app/components/components/ModalRoot/SuccessfulSendCoins/index.js b/app/routes/app/components/components/ModalRoot/SuccessfulSendCoins/index.js new file mode 100644 index 00000000..b6c90a73 --- /dev/null +++ b/app/routes/app/components/components/ModalRoot/SuccessfulSendCoins/index.js @@ -0,0 +1,3 @@ +import SuccessfulSendCoins from './SuccessfulSendCoins' + +export default SuccessfulSendCoins diff --git a/app/routes/app/components/components/ModalRoot/index.js b/app/routes/app/components/components/ModalRoot/index.js new file mode 100644 index 00000000..676d19a1 --- /dev/null +++ b/app/routes/app/components/components/ModalRoot/index.js @@ -0,0 +1,3 @@ +import ModalRoot from './ModalRoot' + +export default ModalRoot diff --git a/app/routes/app/containers/AppContainer.js b/app/routes/app/containers/AppContainer.js index cf6de0ef..3f2e8153 100644 --- a/app/routes/app/containers/AppContainer.js +++ b/app/routes/app/containers/AppContainer.js @@ -4,14 +4,18 @@ import { fetchTicker, setCurrency, tickerSelectors } from '../../../reducers/tic import { fetchBalance } from '../../../reducers/balance' import { fetchInfo } from '../../../reducers/info' import { createInvoice, fetchInvoice } from '../../../reducers/invoice' -import { payInvoice } from '../../../reducers/payment' +import { hideModal } from '../../../reducers/modal' +import { payInvoice, sendCoins } from '../../../reducers/payment' import { fetchChannels } from '../../../reducers/channels' import { setForm, + setPaymentType, setAmount, + setOnchainAmount, setMessage, setPubkey, - setPaymentRequest + setPaymentRequest, + formSelectors } from '../../../reducers/form' const mapDispatchToProps = { @@ -20,12 +24,16 @@ const mapDispatchToProps = { fetchBalance, fetchInfo, setAmount, + setOnchainAmount, setMessage, setPubkey, setPaymentRequest, setForm, + setPaymentType, createInvoice, + hideModal, payInvoice, + sendCoins, fetchChannels, fetchInvoice } @@ -36,8 +44,11 @@ const mapStateToProps = state => ({ payment: state.payment, form: state.form, invoice: state.invoice, + modal: state.modal, - currentTicker: tickerSelectors.currentTicker(state) + currentTicker: tickerSelectors.currentTicker(state), + isOnchain: formSelectors.isOnchain(state), + isLn: formSelectors.isLn(state) }) export default connect(mapStateToProps, mapDispatchToProps)(App) diff --git a/app/variables.scss b/app/variables.scss index a0ae306c..760ee4f8 100644 --- a/app/variables.scss +++ b/app/variables.scss @@ -11,4 +11,5 @@ $darkestgrey: #999999; $bluegrey: #555459; $green: #0bb634; -$red: #ff0b00; \ No newline at end of file +$red: #ff0b00; +$curve: cubic-bezier(0.650, 0.000, 0.450, 1.000); \ No newline at end of file diff --git a/package.json b/package.json index 35846349..88ee631d 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "build-dll": "cross-env NODE_ENV=development node --trace-warnings -r babel-register ./node_modules/webpack/bin/webpack --config webpack.config.renderer.dev.dll.js --colors", "build-main": "cross-env NODE_ENV=production node --trace-warnings -r babel-register ./node_modules/webpack/bin/webpack --config webpack.config.main.prod.js --colors", "build-renderer": "cross-env NODE_ENV=production node --trace-warnings -r babel-register ./node_modules/webpack/bin/webpack --config webpack.config.renderer.prod.js --colors", - "dev": "cross-env START_HOT=1 npm run start-renderer-dev", + "dev": "cross-env START_HOT=1 concurrently \"npm run start-renderer-dev\"", "flow": "flow", "flow-typed": "rimraf flow-typed/npm && flow-typed install --overwrite || true", "lint": "eslint --cache --format=node_modules/eslint-formatter-pretty .", @@ -27,7 +27,8 @@ "test": "cross-env NODE_ENV=test BABEL_DISABLE_CACHE=1 node --trace-warnings ./test/runTests.js", "test-all": "npm run lint && npm run flow && npm run build && npm run test && npm run test-e2e", "test-e2e": "cross-env NODE_ENV=test BABEL_DISABLE_CACHE=1 node --trace-warnings ./test/runTests.js e2e", - "test-watch": "npm test -- --watch" + "test-watch": "npm test -- --watch", + "lnd": "lnd --bitcoin.active --bitcoin.testnet --debuglevel=debug --neutrino.active --neutrino.connect=faucet.lightning.community:18333 --no-macaroons" }, "browserslist": "electron 1.6", "build": { @@ -184,6 +185,7 @@ }, "dependencies": { "axios": "^0.16.2", + "bitcoinjs-lib": "^3.1.1", "bitcore-lib": "^0.14.0", "devtron": "^1.4.0", "electron-debug": "^1.2.0", diff --git a/resources/cloudbolt.svg b/resources/cloudbolt.svg new file mode 100644 index 00000000..c35acce3 --- /dev/null +++ b/resources/cloudbolt.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/thunderstorm.svg b/resources/thunderstorm.svg new file mode 100644 index 00000000..30c98b71 --- /dev/null +++ b/resources/thunderstorm.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/test/reducers/__snapshots__/form.spec.js.snap b/test/reducers/__snapshots__/form.spec.js.snap index 8e69e1db..c4351236 100644 --- a/test/reducers/__snapshots__/form.spec.js.snap +++ b/test/reducers/__snapshots__/form.spec.js.snap @@ -6,6 +6,7 @@ Object { "formType": "pay", "message": "", "modalOpen": false, + "onchainAmount": "0", "payment_request": "", "pubkey": "", } @@ -17,6 +18,7 @@ Object { "formType": "pay", "message": "", "modalOpen": false, + "onchainAmount": "0", "payment_request": "", "pubkey": "", } @@ -28,6 +30,7 @@ Object { "formType": "foo", "message": "", "modalOpen": true, + "onchainAmount": "0", "payment_request": "", "pubkey": "", } @@ -39,6 +42,7 @@ Object { "formType": "pay", "message": "foo", "modalOpen": false, + "onchainAmount": "0", "payment_request": "", "pubkey": "", } @@ -50,6 +54,7 @@ Object { "formType": "pay", "message": "", "modalOpen": false, + "onchainAmount": "0", "payment_request": "foo", "pubkey": "", } @@ -61,6 +66,7 @@ Object { "formType": "pay", "message": "", "modalOpen": false, + "onchainAmount": "0", "payment_request": "", "pubkey": "foo", } @@ -72,6 +78,7 @@ Object { "formType": "pay", "message": "", "modalOpen": false, + "onchainAmount": "0", "payment_request": "", "pubkey": "", } diff --git a/test/reducers/__snapshots__/payment.spec.js.snap b/test/reducers/__snapshots__/payment.spec.js.snap index 135c163d..fe7667c3 100644 --- a/test/reducers/__snapshots__/payment.spec.js.snap +++ b/test/reducers/__snapshots__/payment.spec.js.snap @@ -5,6 +5,7 @@ Object { "payment": null, "paymentLoading": true, "payments": Array [], + "sendingPayment": false, } `; @@ -12,9 +13,8 @@ exports[`reducers paymentReducer should correctly paymentSuccessful 1`] = ` Object { "payment": null, "paymentLoading": false, - "payments": Array [ - "foo", - ], + "payments": Array [], + "sendingPayment": false, } `; @@ -26,6 +26,7 @@ Object { 1, 2, ], + "sendingPayment": false, } `; @@ -34,6 +35,7 @@ Object { "payment": "foo", "paymentLoading": false, "payments": Array [], + "sendingPayment": false, } `; @@ -42,5 +44,6 @@ Object { "payment": null, "paymentLoading": false, "payments": Array [], + "sendingPayment": false, } `; diff --git a/yarn.lock b/yarn.lock index 733d7f3f..da74513f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1452,6 +1452,12 @@ balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" +base-x@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.2.tgz#bf873861b7514279b7969f340929eab87c11d130" + dependencies: + safe-buffer "^5.0.1" + base64-js@1.2.0, base64-js@^1.0.2: version "1.2.0" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1" @@ -1474,6 +1480,10 @@ big.js@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.1.3.tgz#4cada2193652eb3ca9ec8e55c9015669c9806978" +bigi@^1.1.0, bigi@^1.4.0: + version "1.4.2" + resolved "https://registry.yarnpkg.com/bigi/-/bigi-1.4.2.tgz#9c665a95f88b8b08fc05cfd731f561859d725825" + binary-extensions@^1.0.0: version "1.8.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.8.0.tgz#48ec8d16df4377eae5fa5884682480af4d95c774" @@ -1485,6 +1495,35 @@ binary-extensions@^1.0.0: buffers "~0.1.1" chainsaw "~0.1.0" +bip66@^1.1.0: + version "1.1.5" + resolved "https://registry.yarnpkg.com/bip66/-/bip66-1.1.5.tgz#01fa8748785ca70955d5011217d1b3139969ca22" + dependencies: + safe-buffer "^5.0.1" + +bitcoin-ops@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/bitcoin-ops/-/bitcoin-ops-1.3.0.tgz#6b126b585537bc679b02ed499f14450cffc37e13" + +bitcoinjs-lib@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/bitcoinjs-lib/-/bitcoinjs-lib-3.1.1.tgz#c2b1efe455992be88f6be70b5f6fe1a93b9abd90" + dependencies: + bigi "^1.4.0" + bip66 "^1.1.0" + bitcoin-ops "^1.3.0" + bs58check "^2.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.3" + ecurve "^1.0.0" + merkle-lib "^2.0.10" + pushdata-bitcoin "^1.0.1" + randombytes "^2.0.1" + safe-buffer "^5.0.1" + typeforce "^1.8.7" + varuint-bitcoin "^1.0.4" + wif "^2.0.1" + bitcore-lib@^0.14.0: version "0.14.0" resolved "https://registry.yarnpkg.com/bitcore-lib/-/bitcore-lib-0.14.0.tgz#21cb2359fe7b997a3b7b773eb7d7275ae37d644e" @@ -1657,6 +1696,19 @@ bs58@=2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/bs58/-/bs58-2.0.0.tgz#72b713bed223a0ac518bbda0e3ce3f4817f39eb5" +bs58@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + dependencies: + base-x "^3.0.2" + +bs58check@<3.0.0, bs58check@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.0.2.tgz#06f63b01c2fa6173033c90eb87f1fe3d2e13d89a" + dependencies: + bs58 "^4.0.0" + create-hash "^1.1.0" + bser@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/bser/-/bser-1.0.2.tgz#381116970b2a6deea5646dd15dd7278444b56169" @@ -2231,7 +2283,7 @@ create-hash@^1.1.0, create-hash@^1.1.1, create-hash@^1.1.2: ripemd160 "^2.0.0" sha.js "^2.4.0" -create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: +create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.3, create-hmac@^1.1.4: version "1.1.6" resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.6.tgz#acb9e221a4e17bdb076e90657c42b93e3726cf06" dependencies: @@ -2791,6 +2843,12 @@ ecc-jsbn@~0.1.1: dependencies: jsbn "~0.1.0" +ecurve@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/ecurve/-/ecurve-1.0.5.tgz#d148e8fe50a674f983bb5bae09da0ea23e10535e" + dependencies: + bigi "^1.1.0" + editorconfig@^0.13.2: version "0.13.2" resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.13.2.tgz#8e57926d9ee69ab6cb999f027c2171467acceb35" @@ -5744,6 +5802,10 @@ merge@^1.1.3: version "1.2.0" resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da" +merkle-lib@^2.0.10: + version "2.0.10" + resolved "https://registry.yarnpkg.com/merkle-lib/-/merkle-lib-2.0.10.tgz#82b8dbae75e27a7785388b73f9d7725d0f6f3326" + methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" @@ -6997,6 +7059,12 @@ punycode@^1.2.4, punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" +pushdata-bitcoin@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/pushdata-bitcoin/-/pushdata-bitcoin-1.0.1.tgz#15931d3cd967ade52206f523aa7331aef7d43af7" + dependencies: + bitcoin-ops "^1.3.0" + q@^1.1.2, q@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/q/-/q-1.5.0.tgz#dd01bac9d06d30e6f219aecb8253ee9ebdc308f1" @@ -8637,6 +8705,12 @@ typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" +typeforce@^1.8.7: + version "1.11.4" + resolved "https://registry.yarnpkg.com/typeforce/-/typeforce-1.11.4.tgz#07ce3abcce836761243ae483dce5bc609786205c" + dependencies: + inherits "^2.0.1" + ua-parser-js@^0.7.9: version "0.7.12" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.12.tgz#04c81a99bdd5dc52263ea29d24c6bf8d4818a4bb" @@ -8849,6 +8923,10 @@ value-equal@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-0.2.1.tgz#c220a304361fce6994dbbedaa3c7e1a1b895871d" +varuint-bitcoin@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/varuint-bitcoin/-/varuint-bitcoin-1.0.4.tgz#d812c5dae16e32f60544b6adee1d4be1307d0283" + vary@~1.1.0, vary@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.1.tgz#67535ebb694c1d52257457984665323f587e8d37" @@ -9102,6 +9180,12 @@ widest-line@^1.0.0: dependencies: string-width "^1.0.1" +wif@^2.0.1: + version "2.0.6" + resolved "https://registry.yarnpkg.com/wif/-/wif-2.0.6.tgz#08d3f52056c66679299726fade0d432ae74b4704" + dependencies: + bs58check "<3.0.0" + window-size@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"