Skip to main content

⚡LSPS1: Get Inbound Liquidity For Mobile & Browser Wallets

Are you building an app that uses Bitcoin?

These days, your users are going to want to send and receive Lightning payments.

This means you need to help them open channels on the Lightning Network.

Outbound liquidity is easy

To SEND payments, your users should simply open a channel to a well-connected routing node.

Opening a channel is the fastest and easiest way to get OUTBOUND liquidity.

Your users can open channels to any big "routing node", but obviously, we suggest The Megalith Node. Megalith supports channels that are at least 1,000,000 (one million) sats in size.

If your users want to open smaller channels (under a million sats), Megalith has another node which caters to "the little guys". Grab the node URI from this page: The Megalith Node (for small channels).

Inbound liquidity is more challenging

Inbound liquidity -- the ability to RECEIVE payments, is one of the central design challenges of the Lightning Network.

Newbies, when they set up nodes, are almost always bewildered by the fact that they can't immediately receive funds.

To get inbound liquidity immediately, you'll need to find a routing node to open a channel to your user's node.

Luckily, a group of people considerably smarter than myself have recently finalized a specification for this kind of service, so all the service providers (Lighting Service Providers -- LSPs) can offer the same API.

You can read the full specification here.

Megalith is offering this LSPS1 service, and we're going to walk you through how to use it in your app or wallet.

The beauty of supporting the LSP spec in your app is that you could switch providers at any time, since all LSPS1 providers support the same API calls.

⚠️ Our current implementation uses HTTP communication, instead of BOLT8

According to the specification, the client and Megalith should communicate via BOLT8 messages. In theory this is a great idea, but in the short-run, it might add extra complexity to your implementation.

We do want to switch this API to using BOLT8 in the near future, as that is what is called for in the specification. If you're a wallet or app developer who is ready to integrate LSPS1 with BOLT8, please contact us.

But: for now, although the final "channel opening" will be between Lightning nodes, all the "negotiation steps", before that, will use plain-old HTTP REST requests and responses. Let's party like it's 1999!

Some Definitions

In this guide, Megalith is the LSP (Lighting Service Provider).

The CLIENT is your app or wallet, which could be operating in the browser, in a mobile app, or server-side in your backend (Ruby On Rails, Django, Next.js, Fancy serverless cloud functions, etc...).

An ON-CHAIN PAYMENT is a Bitcoin transaction that is broadcast to the Bitcoin network.

A LIGHTNING PAYMENT is a payment that is routed through the Lightning Network.

A CHANNEL is a connection between two Lightning nodes, which allows them to send payments to each other.

A CHANNEL OPENING is the process of creating a channel.

And, remember, the opener of a channel always pays the opening costs!

Step #1: Client requests info about the LSP service

Documentation: lsps1.get_info

To start the workflow, the client issues a GET request to grab a JSON object from one of these URLs.

MAINNET: https://megalithic.me/api/lsps1/v1/get_info
MUTINYNET: https://lsp1.mutiny.megalith-node.com/api/lsps1/v1/get_info

These are the exact URLs your client will be using. Visit one of these URLs in your browser now to preview it.

You can see that for this /get_info endpoint, and for the rest of the endpoints in this documentation, we've provided both MAINNET and MUTINYNET URLs.

MutinyNet is a "signet", meaning, it is basically a fake copy of the entire Bitcoin network, controlled by one party, who can create blocks and coins "at will."

In MutinyNet, blocks are created every 30 seconds. That makes it faster (and less confusing) to test things that require "waiting for blocks."

MutinyNet works particularly well for testing Lightning applications because there are already nodes on MutinyNet who can do convenient things like provide free coins, inbound liquidity, and other stuff you'll need for testing.

And crucially, MutinyNet allows you to test integrations with services (like Megalith LSP), who have already set up testing infrastructure on this network.

Also: Trying to build and test an LSP integration on mainnet could get potentially expensive, as you'll have to pay real sats for your test channel openings.

So, we recommend you build and test your client's workflow using MutinyNet.

Anyway: Let's look at the response from this /get_info call, and understand what each field means:

# HTTP GET

MAINNET: https://megalithic.me/api/lsps1/v1/get_info
MUTINYNET: https://lsp1.mutiny.megalith-node.com/api/lsps1/v1/get_info

# HTTP RESPONSE
{
"min_required_channel_confirmations": 0, 1️⃣
"min_funding_confirms_within_blocks": 6, 2️⃣
"supports_zero_channel_reserve": true,
"max_channel_expiry_blocks": 13140, 3️⃣
"min_initial_client_balance_sat": "0",4️⃣
"max_initial_client_balance_sat": "0",
"min_initial_lsp_balance_sat": "20000", 5️⃣
"max_initial_lsp_balance_sat": "3000000",
"min_channel_balance_sat": "20000",
"max_channel_balance_sat": "3000000",
"uris": [ 6️⃣
"03e30fda71887a916ef5548a4d02b06fe04aaa1a8de9e24134ce7f139cf79d7579@64.23.162.51:9736"
]
}

1️⃣ min_required_channel_confirmations Opening a channel in Lightning requires an on-chain transaction, and here Megalith is saying: "After we open the channel, we can get it working for payments within min_required_channel_confirmations".

This number is really important if you have fast-moving and impatient users. (All users, basically.) Your users don't want to wait around for ages to start receiving Lightning payments. They want their channel to be active immediately.

That's why Megalith sets this value to 0 -- another term for this is Zero-Conf Channels. Your users are going to want zero-conf channels.

Be aware: Your client's node configuration will almost certainly need to be tweaked to permit zero-conf channels. LND's docs are here, and LDK's docs are here.

2️⃣ min_funding_confirms_within_blocks This one is confusing. It's related to LND's conf target concept. In this example, Megalith has set this value to 6, meaning that, Megalith allows the client to demand that Megalith pay enough fees for the opening transaction such that the channel will be confirmed in as few as 6 blocks. Confusing, right? And also, it might not matter much, assuming we're using zero-conf channels.

3️⃣ max_channel_expiry_blocks The maximum number of blocks that Megalith will agree to keep the channel open. Blocks show up about once every ten minutes, so 13,140 blocks is about 3 months. But you should also know: Typically routing nodes (like Megalith), will continue to keep a channel open if the channel is consistently being used for payments. So although you might only request a 3-month channel, it could stay open for much longer!

4️⃣min_initial_client_balance_sat and max_initial_client_balance_sat LSPS1 allows for an LSP like Megalith to "push" an initial balance to the client. This is a way to help the client get started with outbound liquidity. There is a problem however: Supporting BOTH 0-conf channels AND initial_client_balance is dangerous for an LSP that accepts on-chain payments. This is because the client could potentially steal the funds. So, Megalith sets these values to 0. For outbound liquidity, see the outbound liquidity is easy section above.

5️⃣ min_initial_lsp_balance_sat, max_initial_lsp_balance_sat, min_channel_balance_sat, max_channel_balance_sat Since Megalith doesn't support client balances, these are the bounds of the channel size that your client can request. Note that 20,000 sats is a very, very small channel. We recommend that your clients open channels no smaller than 100,000 sats.

6️⃣ uris By using any URI in this array, your client can connect to the Megalith LSP node. Your client must be already connected to the Megalith LSP before they go to the next step in the workflow!

Step #2: Client creates an order

Documentation: lsps1.create_order.

In this step, the client POSTs to Megalith, requesting a channel to be opened.

# HTTP POST

# MAINNET: https://megalithic.me/api/lsps1/v1/create_order
# MUTINYNET: https://lsp1.mutiny.megalith-node.com/api/lsps1/v1/create_order

Step #2 Checklist

Be sure all of these pieces are in place before you go forward...

☑️ Your client has ALREADY connected to the Megalith node using the URI provided in the /get_info response. With LND, the client would use connect peer. With LDK, here is an example.

☑️ Your client knows what size channel it wants Megalith to open, and is sure that this size is within the bounds specified in /get_info.

☑️ Your client has enough funds to pay for the channel opening. Note that the payment does NOT need to originate from any particular node, it's fine for the client to pay for the channel with any wallet! You could even pay for your user's channels for them, if you're feeling flush!

☑️ If your client wants to pay for the channel with on-chain funds, your client will need to provide a "refund" address: Any kind of BTC address, like P2TH, P2PKH, or P2SH, will work, as long as it's valid. If your client wants to pay with Lightning, this field can be omitted.

Let's look at a sample POST that your client would make to Megalith.


# HTTP POST

# MAINNET: https://megalithic.me/api/lsps1/v1/create_order
# MUTINYNET: https://lsp1.mutiny.megalith-node.com/api/lsps1/v1/create_order

# POST Payload:
{
"lsp_balance_sat": "100000", 1️⃣
"client_balance_sat": "0", 2️⃣
"required_channel_confirmations": 0, 3️⃣
"funding_confirms_within_blocks": 6,
"channel_expiry_blocks": 13140, 4️⃣
"token": "my-unique-token..", 5️⃣
"refund_on_chain_address": "bc1p58fpvz...", 6️⃣
"announce_channel": false, 7️⃣
"public_key": "02a98c86ef366ce226a..." 8️⃣
}

1️⃣ lsp_balance_sat This is the size of the channel. We recommend channels of at least 100,000 sats. The amount must be equal to or more than the min_initial_lsp_balance_sat value from the /get_info response, and less than or equal to the max_initial_lsp_balance_sat value.

2️⃣ client_balance_sat As discussed above, currently Megalith does not support pushing client balance, so this must be set to 0. If your user wants immediate outbound liquidity, they should just open a channel to Megalith.

3️⃣ required_channel_confirmations If your client is set up for zero-conf channels, this value should be 0. If your client is not set up for zero-conf channels, this value should be 1.

4️⃣ channel_expiry_blocks The duration of the channel lease, described above. This value must be less than or equal to the max_channel_expiry_blocks value from the /get_info response.

5️⃣ token You can include any data you want in this field. Megalith will echo this token back to your client in the response.

6️⃣ refund_on_chain_address If your client wants to pay for the channel using an on-chain payment, this is the address that Megalith will use to refund the client's payment if the channel opening fails. If your client is paying with a Lightning payment, this field can be omitted.

7️⃣ announce_channel If your client wants the channel to be announced to the network, set this to true. If your client wants the channel to be private, set this to false.

8️⃣ public_key This is the public key of the client's node. This is how Megalith will know where to open the channel. Remember! Your client must actually control the node corresponding to this public_key and must ALREADY be connected to our node BEFORE making the /create_order request.

Step #3: Megalith responds to the client with an invoice

Megalith will now immediately respond with JSON that looks like this:

# HTTP RESPONSE
{
order_id: "103b7b67-661e-4402-9ec1-575584568a9d",1️⃣
lsp_balance_sat: "100000",
client_balance_sat: "0",
required_channel_confirmations: 6,
funding_confirms_within_blocks: 6,
channel_expiry_blocks: 13140,
token: "my-unique-token..",
created_at: "2024-06-22T17:09:56Z",
announce_channel: false,
order_state: "CREATED",
payment: {
bolt11: {
state: "EXPECT_PAYMENT",
expires_at: "2024-06-22T18:39:56Z", 2️⃣
fee_total_sat: "9126",
order_total_sat: "9126",
invoice: "lntbs91260n1pn8wpt9pp560..." 3️⃣
},
onchain: {
state: "EXPECT_PAYMENT",
expires_at: "2024-06-22T18:39:56Z",
fee_total_sat: "20000",
order_total_sat: "20000",
address: "tb1qa6wrqek4z4y7jhef7dn55thnxdcp9raqva4ga6", 4️⃣
min_onchain_payment_confirmations: 0,
min_fee_for_0conf: 1650 5️⃣
}
}
channel: null
}


Here are the important parts of this response:

1️⃣ order_id This is a unique identifier for the order. Your client should store this identifier, as they will need it if they want to check the status of their order. It's guaranteed to be unique, so you can use it as a key in your database, if you want.

2️⃣ expires_at The client has until this date/time to pay for their order.

3️⃣ invoice This is the Lightning invoice that your client should pay. You'll want to display this to your user as QR code, probably.

4️⃣ address If your client is paying with an on-chain payment, this is the address that your client should send the payment to. You'll want to display this to your user as a QR code, probably.

5️⃣ min_fee_for_0conf This field unfortunately has a confusing name. It's NOT directly related to Zero-Conf Channels.

min_fee_for_0conf means this: If your user is planning on making an on-chain payment, then, when they send the payment, they must include a fee of AT LEAST min_fee_for_0conf sats.

And: This might be difficult for some users -- lots of "normies" don't understand Bitcoin fees and have probably never manually set a fee for payment.

Your users might be paying this fee from some random wallet, like their Coinbase account, which might not ever allow the user to set a fee.

But that's OK.

If your user sends the on-chain payment, and includes fees BELOW min_fee_for_0conf, Megalith will simply wait for more than zero (typically 1 or 2) confirmations of the payment, before Megalith opens the channel.

Step #4: Your client pays

Finally... let's do this!

Your client should now pay the invoice or the address. Don't allow them to pay both! Remember, they can pay these amounts from ANY wallet, it doesn't have to be the wallet of the node that is connected to Megalith.

Step #5: Your client checks the status of the order

Documentation: lsps1.get_order.

If your client is paying with Lightning, and everything goes well, they might never need to do this. Our systems are fast, and typically we can recognize a Lightning payment and open the channel within about two seconds!

If your client is paying with an on-chain payment, especially if they've failed to include at least the fees specified in min_fee_for_0conf, they might need to check the status of their order.

How do we do that?

Through some fancy event-driven architecture? Websockets? Cloud-native enterprise functions? Nope!

Your client should pretend that it is 2005 and just POLL this endpoint every few seconds.....

# HTTP GET

MAINNET: https://megalithic.me/api/lsps1/v1/get_order?order_id=[your_order_id_here]
MUTINYNET: https://lsp1.mutiny.megalith-node.com/api/lsps1/v1/get_order?order_id=[your_order_id_here]

Ah! Nothing like the simple things in life: Polling a REST endpoint.

You can see that your user should poll the /get_order endpoint with the order_id that they received in the /create_order response.

Megalith will swiftly reply each time, with data that is VERY similar to the response to the /create_order endpoint:

# HTTP RESPONSE
{
"order_id": "103b7b67-661e-4402-9ec1-575584568a9d",
"lsp_balance_sat": "100000",
"client_balance_sat": "0",
"required_channel_confirmations": 6,
"funding_confirms_within_blocks": 6,
"channel_expiry_blocks": 13140,
"token": "",
"created_at": "2024-06-22T17:09:56Z",
"announce_channel": false,
"order_state": "COMPLETED",
"payment": {
"bolt11": {
"state": "PAID",
"expires_at": "2024-06-22T18:39:56Z",
"fee_total_sat": "9126",
"order_total_sat": "9126",
"invoice": "lntbs91260n1pn8wpt9pp560tf....."
},
"onchain": {
"state": "PAID",
"expires_at": "2024-06-22T18:39:56Z",
"fee_total_sat": "20000",
"order_total_sat": "20000",
"address": "tb1qa6wrqek4z4y7jhef7dn55thnxdcp9raqva4ga6",
"min_onchain_payment_confirmations": 0,
"min_fee_for_0conf": 1650
}
},
"channel": { 1️⃣
"funded_at": "2024-06-22T17:29:24Z",
"funding_outpoint": "f9317d2ec1faecc9ea0ec53f1713050c95c6739f54dc7cb0d47c90fb15745b45:0",
"expires_at": "2024-09-21T23:29:24.338Z"
}
}

1️⃣ channel If the channel has been opened, this field will be populated with the details of the channel. The funded_at field is the time that the channel was funded, and the expires_at field is the time that the channel will expire.

We're done!

Any questions? Feel free to contact us!