diff --git a/App.tsx b/App.tsx index 3b8815acc..1a33d4e0e 100644 --- a/App.tsx +++ b/App.tsx @@ -138,6 +138,12 @@ import LspExplanationWrappedInvoices from './views/Explanations/LspExplanationWr import LspExplanationOverview from './views/Explanations/LspExplanationOverview'; import RestoreChannelBackups from './views/Settings/EmbeddedNode/RestoreChannelBackups'; +// LSPS1 +import LSPS1 from './views/Settings/LSPS1/index'; +import LSPS1Settings from './views/Settings/LSPS1/Settings'; +import OrdersPane from './views/Settings/LSPS1/OrdersPane'; +import Orders from './views/Settings/LSPS1/Order'; + import RawTxHex from './views/RawTxHex'; import CustodialWalletWarning from './views/Settings/CustodialWalletWarning'; @@ -785,6 +791,24 @@ export default class App extends React.PureComponent { name="TxHex" component={TxHex} /> + + + + diff --git a/assets/images/SVG/OlympusAnimated.svg b/assets/images/SVG/OlympusAnimated.svg new file mode 100644 index 000000000..b9d675f1c --- /dev/null +++ b/assets/images/SVG/OlympusAnimated.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/SVG/order-list.svg b/assets/images/SVG/order-list.svg new file mode 100644 index 000000000..f33f118ab --- /dev/null +++ b/assets/images/SVG/order-list.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/backends/CLightningREST.ts b/backends/CLightningREST.ts index c0591031a..13cef2a81 100644 --- a/backends/CLightningREST.ts +++ b/backends/CLightningREST.ts @@ -275,4 +275,6 @@ export default class CLightningREST extends LND { supportsOnchainBatching = () => false; supportsChannelBatching = () => false; isLNDBased = () => false; + supportsLSPS1customMessage = () => false; + supportsLSPS1rest = () => true; } diff --git a/backends/Eclair.ts b/backends/Eclair.ts index 313fb39b2..01f26ae46 100644 --- a/backends/Eclair.ts +++ b/backends/Eclair.ts @@ -507,6 +507,8 @@ export default class Eclair { supportsOnchainBatching = () => false; supportsChannelBatching = () => false; isLNDBased = () => false; + supportsLSPS1customMessage = () => false; + supportsLSPS1rest = () => true; } const mapInvoice = diff --git a/backends/EmbeddedLND.ts b/backends/EmbeddedLND.ts index a01702ebc..9ee0e7f55 100644 --- a/backends/EmbeddedLND.ts +++ b/backends/EmbeddedLND.ts @@ -23,7 +23,9 @@ const { getNetworkInfo, queryRoutes, lookupInvoice, - fundingStateStep + fundingStateStep, + sendCustomMessage, + subscribeCustomMessages } = lndMobile.index; const { channelBalance, @@ -68,6 +70,9 @@ export default class EmbeddedLND extends LND { data.spend_unconfirmed, data.send_all ); + sendCustomMessage = async (data: any) => + await sendCustomMessage(data.peer, data.type, data.data); + subscribeCustomMessages = async () => await subscribeCustomMessages(); getMyNodeInfo = async () => await getInfo(); getNetworkInfo = async () => await getNetworkInfo(); getInvoices = async () => await listInvoices(); @@ -290,4 +295,6 @@ export default class EmbeddedLND extends LND { supportsOnchainBatching = () => true; supportsChannelBatching = () => true; isLNDBased = () => true; + supportsLSPS1customMessage = () => true; + supportsLSPS1rest = () => false; } diff --git a/backends/LND.ts b/backends/LND.ts index 63d84fa0e..326f49fca 100644 --- a/backends/LND.ts +++ b/backends/LND.ts @@ -251,6 +251,57 @@ export default class LND { spend_unconfirmed: data.spend_unconfirmed, send_all: data.send_all }); + sendCustomMessage = (data: any) => + this.postRequest('/v1/custommessage', { + peer: Base64Utils.hexToBase64(data.peer), + type: data.type, + data: Base64Utils.hexToBase64(data.data) + }); + subscribeCustomMessages = (onResponse: any, onError: any) => { + const route = '/v1/custommessage/subscribe'; + const method = 'GET'; + + const { host, lndhubUrl, port, macaroonHex, accessToken } = + stores.settingsStore; + + const auth = macaroonHex || accessToken; + const headers: any = this.getHeaders(auth, true); + const methodRoute = `${route}?method=${method}`; + const url = this.getURL(host || lndhubUrl, port, methodRoute, true); + + const ws: any = new WebSocket(url, null, { + headers + }); + + ws.addEventListener('open', () => { + // connection opened + console.log('subscribeCustomMessages ws open'); + ws.send(JSON.stringify({})); + }); + + ws.addEventListener('message', (e: any) => { + // a message was received + const data = JSON.parse(e.data); + console.log('subscribeCustomMessagews message', data); + if (data.error) { + onError(data.error); + } else { + onResponse(data); + } + }); + + ws.addEventListener('error', (e: any) => { + // an error occurred + console.log('subscribeCustomMessages ws err', e); + const certWarning = localeString('backends.LND.wsReq.warning'); + onError(e.message ? `${certWarning} (${e.message})` : certWarning); + }); + + ws.addEventListener('close', () => { + // ws closed + console.log('subscribeCustomMessages ws close'); + }); + }; getMyNodeInfo = () => this.getRequest('/v1/getinfo'); getInvoices = (data: any) => this.getRequest( @@ -617,4 +668,6 @@ export default class LND { supportsOnchainBatching = () => true; supportsChannelBatching = () => true; isLNDBased = () => true; + supportsLSPS1customMessage = () => true; + supportsLSPS1rest = () => false; } diff --git a/backends/LightningNodeConnect.ts b/backends/LightningNodeConnect.ts index e2d16ba14..a39724571 100644 --- a/backends/LightningNodeConnect.ts +++ b/backends/LightningNodeConnect.ts @@ -123,6 +123,16 @@ export default class LightningNodeConnect { send_all: data.send_all }) .then((data: lnrpc.SendCoinsResponse) => snakeize(data)); + sendCustomMessage = async (data: any) => + await this.lnc.lnd.lightning + .sendCustomMessage({ + peer: Base64Utils.hexToBase64(data.peer), + type: data.type, + data: Base64Utils.hexToBase64(data.data) + }) + .then((data: lnrpc.SendCustomMessageResponse) => snakeize(data)); + subscribeCustomMessages = () => + this.lnc.lnd.lightning.subscribeCustomMessages({}); getMyNodeInfo = async () => await this.lnc.lnd.lightning .getInfo({}) @@ -477,4 +487,6 @@ export default class LightningNodeConnect { supportsOnchainBatching = () => true; supportsChannelBatching = () => true; isLNDBased = () => true; + supportsLSPS1customMessage = () => true; + supportsLSPS1rest = () => false; } diff --git a/backends/LndHub.ts b/backends/LndHub.ts index 7f4212818..c5fccd704 100644 --- a/backends/LndHub.ts +++ b/backends/LndHub.ts @@ -155,4 +155,6 @@ export default class LndHub extends LND { supportsOnchainBatching = () => false; supportsChannelBatching = () => true; isLNDBased = () => false; + supportsLSPS1customMessage = () => false; + supportsLSPS1rest = () => false; } diff --git a/backends/Spark.ts b/backends/Spark.ts index 226034e78..b9b02189c 100644 --- a/backends/Spark.ts +++ b/backends/Spark.ts @@ -381,4 +381,6 @@ export default class Spark { supportsOnchainBatching = () => false; supportsChannelBatching = () => true; isLNDBased = () => false; + supportsLSPS1customMessage = () => false; + supportsLSPS1rest = () => true; } diff --git a/components/LSPS1OrderResponse.tsx b/components/LSPS1OrderResponse.tsx new file mode 100644 index 000000000..96f92fa1e --- /dev/null +++ b/components/LSPS1OrderResponse.tsx @@ -0,0 +1,327 @@ +import * as React from 'react'; +import { inject, observer } from 'mobx-react'; +import { ScrollView, View } from 'react-native'; +import moment from 'moment'; + +import Screen from './Screen'; +import KeyValue from './KeyValue'; +import Amount from './Amount'; +import Button from './Button'; + +import { localeString } from '../utils/LocaleUtils'; +import { themeColor } from '../utils/ThemeUtils'; +import UrlUtils from '../utils/UrlUtils'; + +import InvoicesStore from '../stores/InvoicesStore'; +import NodeInfoStore from '../stores/NodeInfoStore'; +import FiatStore from '../stores/FiatStore'; + +interface LSPS1OrderResponseProps { + navigation: any; + orderResponse: any; + InvoicesStore: InvoicesStore; + NodeInfoStore: NodeInfoStore; + FiatStore: FiatStore; + orderView: boolean; +} + +@inject('InvoicesStore', 'NodeInfoStore', 'FiatStore') +@observer +export default class LSPS1OrderResponse extends React.Component< + LSPS1OrderResponseProps, + null +> { + render() { + const { + orderResponse, + InvoicesStore, + NodeInfoStore, + FiatStore, + orderView, + navigation + } = this.props; + const { testnet } = NodeInfoStore; + const payment = orderResponse?.payment; + const channel = orderResponse?.channel; + return ( + + + + {orderResponse?.lsp_balance_sat && ( + + } + /> + )} + {orderResponse?.client_balance_sat && ( + + } + /> + )} + {orderResponse?.lsp_balance_sat && + orderResponse?.client_balance_sat && ( + + } + /> + )} + {orderResponse?.announce_channel && ( + + )} + {orderResponse?.channel_expiry_blocks && ( + + )} + + {orderResponse?.funding_confirms_within_blocks && ( + + )} + {orderResponse?.created_at && ( + + )} + {orderResponse?.expires_at && ( + + )} + + {orderResponse?.order_id && ( + + )} + {orderResponse?.order_state && ( + + )} + + {payment?.fee_total_sat && ( + + } + /> + )} + {(payment?.lightning_invoice || + payment?.bolt11_invoice) && ( + + )} + {payment?.state && ( + + )} + {payment?.min_fee_for_0conf && ( + + )} + {payment?.min_onchain_payment_confirmations && ( + + )} + {payment?.onchain_address && ( + + )} + {payment?.onchain_payment && ( + + )} + {payment?.order_total_sat && ( + + } + /> + )} + {channel && ( + <> + + + + + UrlUtils.goToBlockExplorerTXID( + channel?.funding_outpoint, + testnet + ) + } + /> + + )} + {orderResponse?.order_state === 'CREATED' && orderView && ( +