I did some thinking about Lightning invoice expirations times, or expiries.
Funny sounding, right? It turns out that “expiry” is a real word in English. I always just thought it was some abbreviation people use when they’re too lazy to type out “expiration date”. However, sources like Merriam Webster make claim to the word’s legitimacy. They’ve been at it since 1828, so I’ll accept this.
How expiration works
BOLT 11 invoices are not permanent; they expire over time. If the creator of the invoice does not explicitly define the expiration time, then the invoice is considered to expire by default after 3600 seconds, or 1 hour.
Why do Lightning invoices expire?
I’ll just refer to Rene Pickhardt’s answer from this StackOverflow question.
The bolt 11 which specifies invoices does not give a rational for this design choice therefor – unless one of the people who build it come along – I can only give an educated guess:
So first of all if invoices were valid for an arbitrary time the recipient would have to keep an arbitrary amount of preimages running eventually into memory / Harddisk issues. I guess this is the single most important reason.Rene Pickhardt
It’s important to remember that a Lightning payment is not as simple as signing a Bitcoin transaction and sending it to the miners. Lightning payments are a collaborative process that happens among 2 or more participating nodes.
For any invoice that a node wishes to receive payment for, the node must also retain the corresponding preimage. Retaining preimages for invoices that go unpaid indefinitely would be a total waste of memory and storage; therefore it makes sense that invoices should expire so that the preimages of unpaid invoices can be safely discarded if needed.
What is the best time for invoices to expire?
Like with many things in Bitcoin, best practices are debated.
In my view, it depends on the situation we’re optimizing for. I would like to run through some sample scenarios and see what the results are. First, let’s establish some axioms.
- Invoices need an expiration, otherwise nodes might need to retain very large amounts of preimages.
- From the time an invoice is created until the time it expires, bitcoin’s local currency valuation will fluctuate. The greater the invoice expiry, the greater the chance of significant fluctuation.
- Displaying a timer portraying invoice expiration can make some users feel stressed out, particularly for smaller expiries like 1 minute.
- Once a recipient has shared an invoice with a sender, there is no way to retract the invoice in a technical sense. The recipient can say “please don’t use this invoice”, but there is nothing within the Lightning protocol which prevents the sender from attempting to use the invoice.
Ok, let’s look at some scenarios.
Recipient creates an invoice for $5 with an expiry of 60 seconds.
Both sender and recipient may be stressed out by the time limit.
Recipient creates an invoice for $5 with an expiry of 1 hour. Sender and receiver feel more comfortable with the time limit.
The bitcoin price fluctuates in the time it takes for the sender to pay the invoice, resulting in the recipient getting less than $5.
Recipient wallet creates an invoice for $5 with an expiry of 60 seconds. This wallet has a special UX consideration: instead of displaying a timer, the wallet simply refreshes the QR code every 60 seconds with a newly generated invoice in which the amount in msats is more currently calculated.
The recipient is always displaying an invoice with a more current BTC/USD valuation, and neither the recipient nor sender even notices the expiration time.
This does not work if recipient has copy-and-pasted the invoice string and sent to the recipient via text, email, etc. This solution works best for in-person payment interactions facilitated by QR code scanning (and could also feasibly be adapted for NFC).
Recipient wallet creates an invoice with an expiry of 1 hour (or longer). The invoice does not have an amount defined; it’s up to the sender to define the amount. The recipient communicates this verbally for in-person payments. For online sales, the eCommerce platform must validate that the order total has been paid in full.
For eComm, there is no objective way for the platform to determine if the order has been paid in full. What if the BTC/USD price fluctuated during the small window of time it took the sender’s wallet to pathfind? What if the recipient and sender get their BTC/USD rate from different exchanges?
This method works best for in-person sales where a human can easily determine “Yes, that’s $5” or “Yes, that’s close enough to $5”.
Recipient publishes a BOLT 12 offer for $5. Sender scans the offer at their own leisure; then, their wallet requests an invoice for $5 from the recipient. Recipient node sends an invoice for $5 worth of bitcoin to the sender’s wallet, and the sender pays immediately.
BOLT 12 isn’t widely implemented yet.
Price volatility is a UX problem
The volatility of Bitcoin’s price is a huge issue for daily usage. Even if you accept the idea that Bitcoin’s price will continue to increase significantly over time, that sort of thinking only works from an investment mindset. For a user who is lower-income, using Bitcoin as a primary currency exposes them to significant reductions in purchasing power in the short term.
Now I’m going to state an opinion that seems counter-intuitive.
Bitcoin price volatility in Lightning invoices is only an issue for custodial wallets
In a custodial wallet, such as Strike or Chivo, the user actually owns dollars. These wallets use Bitcoin and Lightning as payment rails, but hold dollars on behalf of the user. The user has access to the global monetary networks that are Bitcoin and Lightning, but is guaranteed a stable amount of dollars.
When transacting with these types of wallets, getting the invoice amount precise is more beneficial. When a user creates a $5 invoice with Strike, the user is literally requesting 5 dollars, not $5 worth of bitcoin. If the BTC/USD price goes down while a Strike user is invoicing somebody for $5, then they may only end up with $4.17.
Bitcoin price volatility in Lightning invoices is not a significant problem in non-custodial wallets
Now consider the same situation with a non-custodial wallet, in which the user maintains strictly bitcoin.
I create an invoice for $5 worth of sats. You pay the invoice. Then:
- The price of bitcoin goes up; I profit and you lose.
- OR the price of bitcoin goes down; you profit and I lose.
- OR the price of bitcoin goes up; I feel like I profit, then it goes down tomorrow morning and I feel like I lose.
- And so on…
Transacting everyday from a Lightning wallet that is non-custodial means the user is constantly exposed to the volatility. There’s no way to make accurate predictions about the price from one moment to the next, so the user is basically gambling with their value anytime they send or receive bitcoin.
Therefore, a price fluctuation in an invoice with a 1 hour expiry sounds bad, but it is just one among countless price fluctuations that the user must endure (or benefit from) on a minutely basis.
Here’s a flowchart I made depicting how I think about appropriate expiries, and described in more detail below.
BOLT 12 Offers – The ultimate solution
BOLT 12 offers resolve any concern about price volatility during the timespan before the invoice expires. Publish a BOLT 12 offer for $5 and watch the sender request a fresh invoice for $5 worth of bitcoin. The expiration time doesn’t really matter, because the sender can easily request a new invoice without needing to nag the sender.
No more “sorry, the invoice expired, can u please send again” messages on Telegram. 💯
I think that the best expiration time for a Lightning invoice depends on your use case.
As it stands, BOLT 12 has not been merged into the the main branch of the lightning/bolts repository and is not widely implemented. In the meantime, here are some design considerations.
You are denominating your prices in bitcoin or sats
Price volatility is not an issue since you are denominating the price in bitcoin terms. Use a comfortable expiration time. 24 hours is great for situations where you might text somebody an invoice and wait for them to pay it.
You sell things in person denominated in fiat
You may want to receive an amount in bitcoin that closely matches your fiat price. (Like I pointed out above, I view this is a futile exercise if you are maintaining these payments as dollars on the balance sheet). In this situation, you will want either
- a small expiry (say 1 minute) and a defined amount, or
- a larger expiry (say 10 minutes) and no defined amount.
Why? The small expiry with the defined amount shields the merchant from price fluctuation. If the customer fumbles and can’t pay the LN invoice within a minute, the merchant’s wallet can generate a new invoice for the customer. No big deal.
The alternative approach is for the merchant to use a larger expiry, but allow the customer to manually define the amount when they send. The customer types in $5 and their wallet handles the math of converting dollars to sats.
Not all businesses will like this latter approach. A larger business, like an established coffee chain, may prefer the objective approach of the former in which “I tell the customer how much they owe me, and customer pays precisely that amount”. However, in El Salvador, I witnessed small business owners who seemed perfectly comfortable allowing the customer to manually punch in their payment amount.
This is no different than a cash transaction in which a merchant looks at what the customer has handed them and determines if they are satisfied with what they have been given.
You sell things online denominated in fiat via an eCommerce store
Use a small expiry with a defined amount. Your eCommerce platform needs to be able to make an objective determination if the order has been paid for, so having the customer punch in the fiat amount manually is just not going to work.
Other design considerations
- Ultimately, if an invoice expires, it’s pretty easy to generate a new one. Make sure the user is aware, but don’t stress them out too much.
- Set a sensible default expiry for the types of situations you see the wallet being used in. (A personal wallet vs. a retail POS may have different needs).
- Allow the user to override the default and change the expiry (perhaps tuck this away in a submenu).
1 BTC to a FOSS non-custodial wallet that enables their users to “peg” their Lightning balance to the US dollar. We have no requirement for exactly how to do this, but suspect it will have something to do with contracts for difference. We realize this is an ambitious goal, and that submissions may be prototypes. We will leave it up to the board of judges to determine what will qualify as success. Bonus points if the mechanism that the user interacts with to “peg” their Bitcoin to USD is a slick slider.Human Rights Foundation
I think that will be a huge innovation; such a feature, if widely used, would nullify price fluctuation as a rationale for short invoice teams.
I think stabilized lightning and BOLT 12 offers will eventually reduce invoice expiries to being an under-the-hood developer feature that most users don’t think about. 🤞