Builder's Guide
  • Welcome to the Builder's Guide to the LND Galaxy!
  • The Lightning Network
    • Overview
    • Payment Channels
      • Lifecycle of a Payment Channel
      • Watchtowers
      • Understanding Sweeping
      • Etymology
    • The Gossip Network
      • Identifying Good Peers on the Lightning Network
    • Pathfinding
      • Finding routes in the Lightning Network
      • Channel Fees
      • Multipath Payments (MPP)
    • Lightning Network Invoices
      • Understanding Lightning Invoices
    • Making Payments
      • The Payment Cycle
      • Timelocks
      • ⭐Hashed Timelock Contract (HTLC)
      • Payment Etymology
      • ⭐What Makes a Good Routing Node
      • Understanding Submarine Swaps
      • Instant Submarine Swaps
    • Liquidity
      • ⭐Understanding Liquidity
      • Managing Liquidity on the Lightning Network
      • Liquidity Management for Lightning Merchants
      • How to Get Inbound Capacity on the Lightning Network
      • Lightning Service Provider
    • L402: Lightning HTTP 402 Protocol
      • Macaroons
      • L402
      • 📋Protocol Specification
      • Implementations and Links
    • Taproot Assets
      • Taproot Assets Protocol
      • Taproot Assets on Lightning
      • Edge Nodes
      • Taproot Assets Trustless Swap
      • FAQ
      • Glossary
  • Lightning Network Tools
    • LND
      • 🛠️Get Started
      • lnd.conf
      • First Steps With LND
      • Wallet Management
      • Sending Payments
      • Atomic Multi-path Payments (AMP)
      • Receiving Payments
      • Unconfirmed Bitcoin Transactions
      • Channel Fees
      • Inbound Channel Fees
      • Macaroons
      • Configuring Watchtowers
      • Pathfinding
      • Blinded Paths
      • Key Import
      • Secure Your Lightning Network Node
      • Configuration of a Routing Node
      • Quick Tor Setup
      • Configuring Tor
      • Enable ‘Neutrino mode’ in Bitcoin Core
      • Send Messages With Keysend
      • Partially Signed Bitcoin Transactions
      • Bulk onchain actions with PSBTs
      • Sweeper
      • Debugging LND
      • Fuzzing LND
      • LND API documentation
      • Channel Acceptor
      • RPC Middleware Interceptor
      • HTLC Interceptor
      • NAT Traversal
      • Recovery: Planning for Failure
      • Migrating LND
      • Disaster recovery
      • Contribute to LND
    • Lightning Terminal
      • What is Lightning Terminal?
      • 🛠️Get litd
      • Run litd
      • Integrating litd
      • Demo: Litd Speed Run
      • Connect to Terminal
      • Recommended Channels
      • Rankings
      • Health Checks
      • Liquidity Report
      • Opening Lightning Network Channels
      • Managing Channel Liquidity
      • Autofees
      • AutoOpen
      • LND Accounts
      • Loop and Lightning Terminal
      • Loop Fees
      • Pool and Lightning Terminal
      • Command Line Interface
      • Troubleshooting
      • Lightning Node Connect: Under the hood
      • LNC Node Package
      • LITD API Documentation
      • Privacy and Security
      • Privacy Policy
      • Terms of Use
    • Loop
      • 🛠️Get Started
      • The Loop CLI
      • Autoloop
      • Static Loop In Addresses
      • Instant Loop Outs
      • Peer with Loop
      • Loop API Documentation
    • Pool
      • Overview
      • Quickstart
      • 🛠️Installation
      • First Steps
      • Accounts
      • Orders and Asks
      • Sidecar Channels
      • Zero-confirmation Channels
      • Channel Leases
      • Batch Execution
      • Account Recovery
      • Pool API Documentation
      • FAQs
    • Taproot Assets
      • Get Started
      • First Steps
      • Taproot Assets Channels
      • Asset Decimal Display
      • Become an Edge Node
      • RFQ
      • Collectibles
      • Universes
      • Asset Loop
      • Debugging Tapd
      • Multisignature
      • Minting Assets With an External Signer
      • Lightning Polar
      • Operational Safety Guidelines
      • Taproot Assets API Documentation
    • Aperture
      • ⚒️Get Aperture
      • LNC Backend
      • LNC Mailbox
      • Pricing
    • Faraday
      • 🛠️Get Started
      • The Faraday CLI
      • Faraday API Documentation
  • LAPPs
    • Guides
      • Use Polar to Build Your First LAPP
        • Setup: Local Cluster with Polar
        • Setup: Run the Completed App
        • Setup: Run the App Without LND
      • Add Features
        • Feature 1: Connect to LND
        • Feature 2: Display Node Alias and Balance
        • Feature 3: Sign and Verify Posts
        • Feature 4: Modify Upvote Action
      • Make Your own LNC-powered Application
    • Next Steps
  • Community Resources
    • Resource List
    • Lightning Bulb 💡
    • Glossary
    • FAQ
Powered by GitBook
On this page
  • Intro
  • Installation
  • Upgrade with LNC
  • useLNC.ts
  • Login.tsx
  • Connect.tsx
  • App.tsx
  • Page.tsx
  • Home.tsx
  • Connect your node to the upgraded app

Was this helpful?

  1. LAPPs
  2. Guides

Make Your own LNC-powered Application

Build the LNC demo app and and learn how to use it to build your own.

PreviousFeature 4: Modify Upvote ActionNextNext Steps

Last updated 1 year ago

Was this helpful?

is a standard to create an end-to-end encrypted connection between your node and a web or mobile app. It is used by , Alby and Zeus to give convenient access to your node across a firewall or NAT.

LNC is bundled in litd and is available as a standalone npm package for and .

In this demonstration we are going to use the to build our own application that connects to our Lightning node.

Intro

In the initial state, this demo app allows a user to enter their node alias into a form. This alias is then used to generate a Lightning meme. This guide will walk you through the process of adding LNC to the web app to allow users to connect their node and automatically pull in their node alias to be used to generate custom images.

You will need to have an LND Lightning node with litd available.

Installation

NodeJS is required to install the demo application.

  1. Clone the repo and checkout the pre-lnc branch git clone https://github.com/HannahMR/lnc-web/

    cd lnc-web/meme-demo

    git checkout pre-lnc

  2. Install the dependencies

  3. Start the web app

First we are going to a look at the structure of this simple web app. Notable are components and pages. By default only one page is available: Home.tsx. There is one component for our page, and one that the node alias into the form to generate our meme.

Upgrade with LNC

We’re going to make the changes that automatically pull the node alias.

First we’ll add lnc-web as a dependency by adding the below line the the list of dependencies in the package.json file.

@lightninglabs/lnc-web": "0.2.1-alpha",

npm install

And for this project we’ve created a useLNC hook. We’ll need to create a new directory inside src called hooks, and add the useLNC.ts file.

useLNC.ts

import { useCallback } from 'react';
import LNC from '@lightninglabs/lnc-web';

// create a singleton instance of LNC that will live for the lifetime of the app
const lnc = new LNC({});

/**
 * A hook that exposes a single LNC instance of LNC to all components that need it.
 * It also returns a couple helper functions to simplify the usage of LNC
 */
const useLNC = () => {
  /** Connects to LNC using the provided pairing phrase and password */
  const connect = useCallback(async (pairingPhrase: string, password: string) => {
	lnc.credentials.pairingPhrase = pairingPhrase;
	await lnc.connect();
	// verify we can fetch data
	await lnc.lnd.lightning.listChannels();
	// set the password after confirming the connection works
	lnc.credentials.password = password;
  }, []);

  /** Connects to LNC using the password to decrypt the stored keys */
  const login = useCallback(async (password: string) => {
	lnc.credentials.password = password;
	await lnc.connect();
  }, []);

  return { lnc, connect, login };
};

export default useLNC;

We’ll add two new pages, Login and Connect, which give users the forms they need to enter their connection info and then pass that info along to the LNC module.

Login.tsx

import React, { useCallback, useEffect, useState } from 'react';
import { Alert, Button, Col, Form, Row } from 'react-bootstrap';
import { useNavigate } from 'react-router-dom';
import Page from '../components/Page';
import useLNC from '../hooks/useLNC';

const Login: React.FC = () => {
  const { lnc, login } = useLNC();
  const navigate = useNavigate();
  const [password, setPassword] = useState('');
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');

  useEffect(() => {
	// preload the WASM file when this component is mounted
	lnc.preload();
  }, [lnc]);

  const handleSubmit = useCallback(
	(e: React.FormEvent<HTMLFormElement>) => {
  	// wrap LNC calls into an async function
  	const connect = async () => {
    	e.preventDefault();
    	try {
      	setLoading(true);
      	setError('');
      	if (!password) throw new Error('Enter a password');

      	// connect to the litd node via LNC
      	await login(password);

      	navigate('/');
    	} catch (err) {
      	setError((err as Error).message);
      	// tslint:disable-next-line: no-console
      	console.error(err);
    	} finally {
      	setLoading(false);
    	}
  	};
  	connect();
	},
	[password, navigate, login],
  );

  const handleClear = useCallback(() => {
	lnc.credentials.clear();
	navigate('/connect');
  }, [navigate, lnc]);

  return (
	<Page>
  	<h2>Connect to Lightning Terminal</h2>

  	{error && <Alert variant="danger">{error}</Alert>}

  	<Form onSubmit={handleSubmit}>
    	<Form.Group className="mb-3" controlId="formBasicPassword">
      	<Form.Label>Password</Form.Label>
      	<Form.Control
        	type="password"
        	autoComplete="new-password"
        	value={password}
        	onChange={e => setPassword(e.target.value)}
        	disabled={loading}
      	/>
      	<Form.Text className="text-muted">
        	Enter the password that was used when previously connecting with the pairing
        	phrase
      	</Form.Text>
    	</Form.Group>
    	<Row>
      	<Col>
        	<Button variant="primary" type="submit" disabled={loading}>
          	Submit
        	</Button>
      	</Col>
      	<Col className="text-right">
        	<Button variant="link" type="button" disabled={loading} onClick={handleClear}>
          	Connect using a different pairing phrase
        	</Button>
      	</Col>
    	</Row>
  	</Form>
	</Page>
  );
};

export default Login;

Connect.tsx

import React, { useCallback, useEffect, useState } from 'react';
import { Alert, Button, Form } from 'react-bootstrap';
import { useNavigate } from 'react-router-dom';
import Page from '../components/Page';
import useLNC from '../hooks/useLNC';

const Connect: React.FC = () => {
  const { lnc, connect } = useLNC();
  const navigate = useNavigate();
  const [phrase, setPhrase] = useState('');
  const [password, setPassword] = useState('');
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');

  useEffect(() => {
	// preload the WASM file when this component is mounted
	lnc.preload();
  }, [lnc]);

  const handleSubmit = useCallback(
	(e: React.FormEvent<HTMLFormElement>) => {
  	// wrap LNC calls into an async function
  	const connectAsync = async () => {
    	e.preventDefault();
    	try {
      	setLoading(true);
      	setError('');
      	if (!phrase || !password) throw new Error('Enter a phrase and password');

      	// connect to the litd node via LNC
      	await connect(phrase, password);

      	navigate('/');
    	} catch (err) {
      	setError((err as Error).message);
      	// tslint:disable-next-line: no-console
      	console.error(err);
    	} finally {
      	setLoading(false);
    	}
  	};
  	connectAsync();
	},
	[phrase, password, navigate, connect],
  );

  return (
	<Page>
  	<h2>Connect to Lightning Terminal</h2>

  	{error && <Alert variant="danger">{error}</Alert>}

  	<Form onSubmit={handleSubmit}>
    	<Form.Group className="mb-3" controlId="formBasicEmail">
      	<Form.Label>Pairing Phrase</Form.Label>
      	<Form.Control
        	autoComplete="off"
        	value={phrase}
        	onChange={e => setPhrase(e.target.value)}
        	disabled={loading}
      	/>
      	<Form.Text className="text-muted">
        	Obtain a new pairing phrase from <code>litd</code> and enter it here
      	</Form.Text>
    	</Form.Group>
    	<Form.Group className="mb-3" controlId="formBasicPassword">
      	<Form.Label>Create Password</Form.Label>
      	<Form.Control
        	type="password"
        	autoComplete="new-password"
        	value={password}
        	onChange={e => setPassword(e.target.value)}
        	disabled={loading}
      	/>
      	<Form.Text className="text-muted">
        	lnc-web stores connection data in localStorage. This password will be used to
        	encrypt the data at rest.
      	</Form.Text>
    	</Form.Group>
    	<Button variant="primary" type="submit" disabled={loading}>
      	Submit
    	</Button>
  	</Form>
	</Page>
  );
};

export default Connect;

App.tsx

We’ll also need to make sure that our App file knows about these new pages. We’ll import the two new pages.

import Connect from './pages/Connect'; import Login from './pages/Login';

Next we’ll add these paths to our routes.

<Route path="connect" element={<Connect />} /> <Route path="login" element={<Login />} />

Now we will need to update our MakeMeme component, we’ll alter that to import the useLNC hook, check that we are connected to a node, and then remove the form we had been using to collect node alias data, and replace it with the alias of the node that we are connected to.

First, adding the useLNC hook.

import useLNC from '../hooks/useLNC';

Then we’ll add two constants to MakeMeme.

const {lnc} = useLNC(); const [info, setInfo] = useState<any>();

We’ll now also need to remove some obsolete code.

We no longer need to track memeText as we will be pulling that in from our connection to the node.

const [memeText, setMemeText] = useState(null);

We can remove most of our form as we no longer need to directly collect this information from our users.

<Form.Group className="mb-3" controlId="exampleForm.ControlInput1"> <Form.Label><strong>Write your LNC Node here</strong></Form.Label> <Form.Control as="textarea" rows={2} placeholder="Paste your node alias here, then click generate" onChange={(e) => setMemeText(e.target.value)} /> </Form.Group>

We’ll add a useEffect function adding the below code just after our constants at about line 15.

useEffect(() => { if (lnc.isConnected) { const sendRequest = async () => { const res = await lnc.lnd.lightning.getInfo(); setInfo(res); }; sendRequest(); } }, [lnc.isConnected, lnc.lnd.lightning]); if (!lnc.isConnected || !info) return null;

We’ll need to update the variable we are using for the text that we will be adding to our meme in two spots. On what is likely now line 54 and line 89 we’ll change

{memeText}

to

{info.alias}

Page.tsx

We’ll need to update our Page component as well. First we are importing our useLNC hook.

import useLNC from '../hooks/useLNC';

Then we’ll add a constant for LNC to Page.

const { lnc } = useLNC();

We add the below inside Navbar.Collapse

<Nav className="ml-auto"> {lnc.isConnected ? ( <> <Navbar.Text>Connected</Navbar.Text> <a href="/"> <Button variant="link">Logout</Button> </a> </> ) : lnc.credentials.isPaired ? ( <Link to="/login"> <Button>Login</Button> </Link> ) : ( <Link to="/connect"> <Button>Connect</Button> </Link> )} </Nav>

Home.tsx

Of course our home page will need to know about our new connect option.

Again we’ll import the useLNC hook

import useLNC from '../hooks/useLNC';

Add we’ll add a constant inside Home.

const { lnc } = useLNC();

Inside the page div we’ll add a paragraph that displays instructions based upon if a user is connected to our web app.

<p className="text-center"> {lnc.isConnected ? 'You are now connected to your Lightning node.' : 'Connect or Login to view your Lightning node info.'} </p>

Connect your node to the upgraded app

After all these changes we should be able to refresh our app and receive a message asking us to connect our node.

This is where we need to head over to our node and use litd to generate a connection string.

litcli --lndtlscertpath ~/.lit/tls.cert sessions add --label=”LNC Connect!”

Note that you may need to adjust this connection to suit your node, for example by adding the --testnet flag if you are operating there. Be sure to omit the --type admin flag so that a read only connection string is generated. We should only require the permissions that we absolutely need.

After entering your litd UI password you can find your connection string in the output labeled as “pairing_secret_mnemonic.”

We can then head back to our web app, click the connect button, enter our connection phrase, set a password and connect!

Once we are connected we’ll see the option to generate a meme:

Push the button and see what meme you get!

Congratulations!

Your browser should open to where you will see the application’s landing page.

http://localhost:3000
Lightning Node Connect (LNC)
Lightning Terminal
React Native
web
LNC demo web app
Building Lightning Apps with Lightning Node Connect
Your app's welcome page.
Once connected to your app over LNC, your users will be able to generate their meme.