Merge pull request #3717 from BlueWallet/add-lightning-address-send

ADD: lightning address send support (closes #3690)
This commit is contained in:
GLaDOS 2021-08-30 22:51:34 +01:00 committed by GitHub
commit 82d387278b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 111 additions and 7 deletions

View File

@ -153,6 +153,18 @@ class DeeplinkSchemaMatch {
},
},
]);
} else if (Lnurl.isLightningAddress(event.url)) {
// this might be not just an email but a lightning addres
// @see https://lightningaddress.com
completionHandler([
'ScanLndInvoiceRoot',
{
screen: 'ScanLndInvoice',
params: {
uri: event.url,
},
},
]);
} else if (DeeplinkSchemaMatch.isSafelloRedirect(event)) {
const urlObject = url.parse(event.url, true); // eslint-disable-line node/no-deprecated-api

View File

@ -28,7 +28,15 @@ export default class Lnurl {
static getUrlFromLnurl(lnurlExample) {
const found = Lnurl.findlnurl(lnurlExample);
if (!found) return false;
if (!found) {
if (Lnurl.isLightningAddress(lnurlExample)) {
const username = lnurlExample.split('@')[0].trim();
const host = lnurlExample.split('@')[1].trim();
return `https://${host}/.well-known/lnurlp/${username}`;
} else {
return false;
}
}
const decoded = bech32.decode(found, 10000);
return Buffer.from(bech32.fromWords(decoded.words)).toString();
@ -256,4 +264,11 @@ export default class Lnurl {
getCommentAllowed() {
return this?._lnurlPayServicePayload?.commentAllowed ? parseInt(this._lnurlPayServicePayload.commentAllowed) : false;
}
static isLightningAddress(address) {
// ensure only 1 `@` present:
if (address.split('@').length !== 2) return false;
const splitted = address.split('@');
return !!splitted[0].trim() && !!splitted[1].trim();
}
}

View File

@ -56,11 +56,16 @@ const LnurlPay = () => {
useEffect(() => {
if (lnurl) {
const ln = new Lnurl(lnurl, AsyncStorage);
ln.callLnurlPayService().then(setPayload);
ln.callLnurlPayService()
.then(setPayload)
.catch(error => {
alert(error.message);
pop();
});
setLN(ln);
setIsLoading(false);
}
}, [lnurl]);
}, [lnurl, pop]);
useEffect(() => {
setPayButtonDisabled(isLoading);

View File

@ -92,6 +92,9 @@ const ScanLndInvoice = () => {
useEffect(() => {
if (wallet && uri) {
if (Lnurl.isLnurl(uri)) return processLnurlPay(uri);
if (Lnurl.isLightningAddress(uri)) return processLnurlPay(uri);
let data = uri;
// handling BIP21 w/BOLT11 support
const ind = data.indexOf('lightning=');
@ -102,9 +105,6 @@ const ScanLndInvoice = () => {
data = data.replace('LIGHTNING:', '').replace('lightning:', '');
console.log(data);
/**
* @type {LightningCustodianWallet}
*/
let decoded;
try {
decoded = wallet.decodeInvoice(data);
@ -144,6 +144,7 @@ const ScanLndInvoice = () => {
const processInvoice = data => {
if (Lnurl.isLnurl(data)) return processLnurlPay(data);
if (Lnurl.isLightningAddress(data)) return processLnurlPay(data);
setParams({ uri: data });
};
@ -216,7 +217,12 @@ const ScanLndInvoice = () => {
};
const processTextForInvoice = text => {
if (text.toLowerCase().startsWith('lnb') || text.toLowerCase().startsWith('lightning:lnb') || Lnurl.isLnurl(text)) {
if (
text.toLowerCase().startsWith('lnb') ||
text.toLowerCase().startsWith('lightning:lnb') ||
Lnurl.isLnurl(text) ||
Lnurl.isLightningAddress(text)
) {
processInvoice(text);
} else {
setDecoded(undefined);

View File

@ -202,6 +202,20 @@ describe('unit - DeepLinkSchemaMatch', function () {
},
],
},
{
argument: {
url: 'lnaddress@zbd.gg',
},
expected: [
'ScanLndInvoiceRoot',
{
screen: 'ScanLndInvoice',
params: {
uri: 'lnaddress@zbd.gg',
},
},
],
},
{
argument: {
url: require('fs').readFileSync('./tests/unit/fixtures/skeleton-cobo.txt', 'ascii'),

View File

@ -164,3 +164,55 @@ describe('LNURL', function () {
assert.strictEqual(Lnurl.decipherAES(ciphertext, preimage, iv), '1234');
});
});
describe('lightning address', function () {
it('can getUrlFromLnurl()', () => {
assert.strictEqual(Lnurl.getUrlFromLnurl('lnaddress@zbd.gg'), 'https://zbd.gg/.well-known/lnurlp/lnaddress');
});
it('can detect', async () => {
assert.ok(Lnurl.isLightningAddress('lnaddress@zbd.gg'));
assert.ok(Lnurl.isLightningAddress(' lnaddress@zbd.gg '));
assert.ok(Lnurl.isLightningAddress(' lnaddress@zbd.gg '));
assert.ok(Lnurl.isLightningAddress(' lnaddress@8.8.8.8 '));
assert.ok(Lnurl.isLightningAddress(' lnaddress@hidden.onion '));
assert.ok(!Lnurl.isLightningAddress(' bla bla '));
assert.ok(!Lnurl.isLightningAddress(''));
assert.ok(!Lnurl.isLightningAddress('@'));
assert.ok(!Lnurl.isLightningAddress('@a'));
assert.ok(!Lnurl.isLightningAddress('a@'));
const LN = new Lnurl('lnaddress@zbd.gg');
// poor-man's mock:
LN._fetchGet = LN.fetchGet;
let requestedUri = -1;
LN.fetchGet = actuallyRequestedUri => {
requestedUri = actuallyRequestedUri;
return {
minSendable: 1000,
maxSendable: 45000000,
commentAllowed: 150,
tag: 'payRequest',
metadata: '[["text/plain","lnaddress - lightningaddress.com"],["text/identifier","lnaddress@zbd.gg"],["image/png;base64","img"]]',
callback: 'https://api.zebedee.io/v0/process-static-charges/9a44621d-0665-44eb-96af-e06534311be5',
};
};
const lnurlpayPayload = await LN.callLnurlPayService();
assert.deepStrictEqual(lnurlpayPayload, {
amount: 1,
callback: 'https://api.zebedee.io/v0/process-static-charges/9a44621d-0665-44eb-96af-e06534311be5',
commentAllowed: 150,
description: 'lnaddress - lightningaddress.com',
domain: 'api.zebedee.io',
fixed: false,
image: 'data:image/png;base64,img',
max: 45000,
metadata: '[["text/plain","lnaddress - lightningaddress.com"],["text/identifier","lnaddress@zbd.gg"],["image/png;base64","img"]]',
min: 1,
});
assert.strictEqual(requestedUri, 'https://zbd.gg/.well-known/lnurlp/lnaddress');
});
});