lnurl-pay
Version:
Client library for lnurl-pay and lightning address
241 lines (240 loc) • 7.69 kB
JavaScript
import { __awaiter, __generator } from "tslib";
import isURL from 'is-url';
import { bech32 } from 'bech32';
import axios from 'axios';
import aesjs from 'aes-js';
import Base64 from 'base64-js';
import * as bolt11 from 'bolt11';
import { Sha256 } from '@aws-crypto/sha256-js';
var LNURL_REGEX = /^(?:http.*[&?]lightning=|lightning:)?(lnurl[0-9]{1,}[02-9ac-hj-np-z]+)/;
var LN_ADDRESS_REGEX = /^((?:[^<>()\[\]\\.,;:\s@"]+(?:\.[^<>()\[\]\\.,;:\s@"]+)*)|(?:".+"))@((?:\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(?:(?:[a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
var ONION_REGEX = /^(http:\/\/[^/:@]+\.onion(?::\d{1,5})?)(\/.*)?$/;
var LNURLP_REGEX = /^lnurlp:\/\/([\w-]+\.)+[\w-]+(:\d{1,5})?(\/[\w-.\/?%&=]*)?$/;
/**
* Decode a bech32 encoded url (lnurl), lightning address or lnurlp url and return a url
* @method decodeUrlOrAddress
* @param lnUrlOrAddress string to decode
* @return plain url or null if is an invalid url, lightning address or lnurlp
*/
export var decodeUrlOrAddress = function (lnUrlOrAddress) {
var bech32Url = parseLnUrl(lnUrlOrAddress);
if (bech32Url) {
var decoded = bech32.decode(bech32Url, 20000);
return Buffer.from(bech32.fromWords(decoded.words)).toString();
}
var address = parseLightningAddress(lnUrlOrAddress);
if (address) {
var username = address.username, domain = address.domain;
var protocol = domain.match(/\.onion$/) ? 'http' : 'https';
return "".concat(protocol, "://").concat(domain, "/.well-known/lnurlp/").concat(username);
}
return parseLnurlp(lnUrlOrAddress);
};
/**
* Parse an url and return a bech32 encoded url (lnurl)
* @method parseLnUrl
* @param url string to parse
* @return bech32 encoded url (lnurl) or null if is an invalid url
*/
export var parseLnUrl = function (url) {
if (!url)
return null;
var result = LNURL_REGEX.exec(url.toLowerCase());
return result ? result[1] : null;
};
/**
* Verify if a string is a valid lnurl value
* @method isLnurl
* @param url string to validate
* @return true if is a valid lnurl value
*/
export var isLnurl = function (url) {
if (!url)
return false;
return LNURL_REGEX.test(url.toLowerCase());
};
/**
* Verify if a string is a lightning adress
* @method isLightningAddress
* @param address string to validate
* @return true if is a lightning address
*/
export var isLightningAddress = function (address) {
if (!address)
return false;
return LN_ADDRESS_REGEX.test(address);
};
/**
* Parse an address and return username and domain
* @method parseLightningAddress
* @param address string to parse
* @return LightningAddress { username, domain }
*/
export var parseLightningAddress = function (address) {
if (!address)
return null;
var result = LN_ADDRESS_REGEX.exec(address);
return result ? { username: result[1], domain: result[2] } : null;
};
/**
* Verify if a string is a lnurlp url
* @method isLnurlp
* @param url string to validate
* @return true if is a lnurlp url
*/
export var isLnurlp = function (url) {
if (!url)
return false;
return LNURLP_REGEX.test(url);
};
/**
* Parse a lnurlp url and return an url with the proper protocol
* @method parseLnurlp
* @param url string to parse
* @return url (http or https) or null if is an invalid lnurlp
*/
export var parseLnurlp = function (url) {
if (!url)
return null;
var parsedUrl = url.toLowerCase();
if (!LNURLP_REGEX.test(parsedUrl))
return null;
var protocol = parsedUrl.includes('.onion') ? 'http://' : 'https://';
return parsedUrl.replace('lnurlp://', protocol);
};
/**
* Verify if a string is an url
* @method isUrl
* @param url string to validate
* @return true if is an url
*/
export var isUrl = function (url) {
if (!url)
return false;
try {
return isURL(url);
}
catch (_a) {
return false;
}
};
/**
* Verify if a string is an onion url
* @method isOnionUrl
* @param url string to validate
* @return true if is an onion url
*/
export var isOnionUrl = function (url) {
return isUrl(url) && ONION_REGEX.test(url.toLowerCase());
};
/**
* Parse a number to Satoshis
* @method checkedToSats
* @param value number to parse
* @return Satoshis or null
*/
export var checkedToSats = function (value) {
if (value && value >= 0)
return toSats(value);
return null;
};
/**
* Cast a number to Satoshis type
* @method toSats
* @param value number to cast
* @return Satoshis
*/
export var toSats = function (value) {
return value;
};
export var isValidAmount = function (_a) {
var amount = _a.amount, min = _a.min, max = _a.max;
var isValid = amount > 0 && amount >= min && amount <= max;
var isFixed = min === max;
return isValid && isFixed ? amount === min : isValid;
};
export var getJson = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
var url = _b.url, params = _b.params;
return __generator(this, function (_c) {
return [2 /*return*/, axios.get(url, { params: params }).then(function (response) {
if (response.data.status === 'ERROR')
throw new Error(response.data.reason + '');
return response.data;
})];
});
}); };
export var sha256 = function (data, encoding) {
if (encoding === void 0) { encoding = 'hex'; }
var sha256 = new Sha256();
sha256.update(Buffer.from(data, encoding));
return Buffer.from(sha256.digestSync()).toString('hex');
};
export var decodeInvoice = function (invoice) {
if (!invoice)
return null;
try {
var network = undefined;
// hack to support signet invoices, remove when it is supported in bolt11
if (invoice.startsWith('lntbs')) {
network = {
bech32: 'tbs',
pubKeyHash: 0x6f,
scriptHash: 0xc4,
validWitnessVersions: [0, 1],
};
}
return bolt11.decode(invoice, network);
}
catch (_a) {
return null;
}
};
export var getHashFromInvoice = function (invoice) {
if (!invoice)
return null;
try {
var decoded = decodeInvoice(invoice);
if (!decoded || !decoded.tags)
return null;
var hashTag = decoded.tags.find(function (value) { return value.tagName === 'payment_hash'; });
if (!hashTag || !hashTag.data)
return null;
return hashTag.data.toString();
}
catch (_a) {
return null;
}
};
export var isValidPreimage = function (_a) {
var invoice = _a.invoice, preimage = _a.preimage;
if (!invoice || !preimage)
return false;
var invoiceHash = getHashFromInvoice(invoice);
if (!invoiceHash)
return false;
try {
var preimageHash = sha256(preimage);
return invoiceHash === preimageHash;
}
catch (_b) {
return false;
}
};
export var decipherAES = function (_a) {
var successAction = _a.successAction, preimage = _a.preimage;
if (successAction.tag !== 'aes' ||
!successAction.iv ||
!successAction.ciphertext ||
!preimage)
return null;
var key = aesjs.utils.hex.toBytes(preimage);
var iv = Base64.toByteArray(successAction.iv);
var ciphertext = Base64.toByteArray(successAction.ciphertext);
var cbc = new aesjs.ModeOfOperation.cbc(key, iv);
var plaintext = cbc.decrypt(ciphertext);
// remove padding
var size = plaintext.length;
var pad = plaintext[size - 1];
plaintext = plaintext.slice(0, size - pad);
return aesjs.utils.utf8.fromBytes(plaintext);
};