Skip to content

Latest commit

 

History

History
62 lines (38 loc) · 2.76 KB

01.md

File metadata and controls

62 lines (38 loc) · 2.76 KB

LUD-01: Base LNURL encoding and decoding

author: rompert


LNURL is a bech32-encoded HTTPS/Onion URL that can be interacted with automatically by a WALLET in a standard way such that a SERVICE can provide extra services or a better experience for the user.

Bech32-encoded LNURLs can both be uppercase or lowercase, but not mixed case. When used in QR-Codes they SHOULD be uppercase.

An example LNURL:

https://service.com/api?q=3fc3645b439ce8e7f2553a69e5267081d96dcd340693afabe04be7b0ccd178df

would be bech32-encoded as:

LNURL1DP68GURN8GHJ7UM9WFMXJCM99E3K7MF0V9CXJ0M385EKVCENXC6R2C35XVUKXEFCV5MKVV34X5EKZD3EV56NYD3HXQURZEPEXEJXXEPNXSCRVWFNV9NXZCN9XQ6XYEFHVGCXXCMYXYMNSERXFQ5FNS

and presented as the following QR:

A QR encoded LNURL example

Once LNURL is decoded:

  • If tag query parameter is present, then this LNURL has a special meaning. Further actions will be based on tag parameter value.
  • Otherwise, a GET request should be executed, which must return a JSON object containing a tag field. Further actions will be based on tag field value.

HTTPS or Onion

LNURL is acceptable in two forms: either an https:// clearnet link (no self-signed certificates allowed) or an http:// v2/v3 onion link.

Fallback scheme

LNURL can be used as a fallback inside of other URI schemes, with the key 'lightning' and the value equal to the bech32-encoding, an example: https://service.com/giftcard/redeem?id=123&lightning=LNURL1...

HTTP Status Codes and Content-Type

Neither status codes or any HTTP Header has any meaning. Servers may use whatever they want. Clients should ignore them (and be careful when using libraries that treat responses differently based on headers and status codes) and just parse the response body as JSON, then interpret it accordingly.

Decoding examples

In Scala:

import fr.acinq.bitcoin.Bech32

val bech32lnurl: String = "LNURL1DP68GURN8GHJ7UM9WFMXJCM99E3K7MF0V9CXJ0M385EKVCENXC6R2C35XVUKXEFCV5MKVV34X5EKZD3EV56NYD3HXQURZEPEXEJXXEPNXSCRVWFNV9NXZCN9XQ6XYEFHVGCXXCMYXYMNSERXFQ5FNS"

val (hrp, dataPart) = Bech32.decode(bech32lnurl)
val requestByteArray = Bech32.five2eight(dataPart)

new String(requestByteArray, "UTF-8") // https://service.com/api?q=3fc3645b439ce8e7f2553a69e5267081d96dcd340693afabe04be7b0ccd178df

In JavaScript:

var { bech32 } = require("bech32")

let bech32lnurl = "LNURL1DP68GURN8GHJ7UM9WFMXJCM99E3K7MF0V9CXJ0M385EKVCENXC6R2C35XVUKXEFCV5MKVV34X5EKZD3EV56NYD3HXQURZEPEXEJXXEPNXSCRVWFNV9NXZCN9XQ6XYEFHVGCXXCMYXYMNSERXFQ5FNS"

let { prefix: hrp, words: dataPart } = bech32.decode(bech32lnurl, 2000)
let requestByteArray = bech32.fromWords(dataPart)

Buffer.from(requestByteArray).toString()