Merge pull request #3640 from BlueWallet/add-support-lnrul-pay-comments

ADD: lnurl-pay comments (closes #3617)
This commit is contained in:
GLaDOS 2021-08-24 07:11:22 +01:00 committed by GitHub
commit 9cc8f7a8f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 77 additions and 3 deletions

View File

@ -92,9 +92,10 @@ export default class Lnurl {
return decoded;
}
async requestBolt11FromLnurlPayService(amountSat) {
async requestBolt11FromLnurlPayService(amountSat, comment = '') {
if (!this._lnurlPayServicePayload) throw new Error('this._lnurlPayServicePayload is not set');
if (!this._lnurlPayServicePayload.callback) throw new Error('this._lnurlPayServicePayload.callback is not set');
if (!comment && this.getCommentAllowed()) throw new Error('Comment not provided');
if (amountSat < this._lnurlPayServicePayload.min || amountSat > this._lnurlPayServicePayload.max)
throw new Error(
'amount is not right, ' +
@ -106,7 +107,12 @@ export default class Lnurl {
);
const nonce = Math.floor(Math.random() * 2e16).toString(16);
const separator = this._lnurlPayServicePayload.callback.indexOf('?') === -1 ? '?' : '&';
const urlToFetch = this._lnurlPayServicePayload.callback + separator + 'amount=' + Math.floor(amountSat * 1000) + '&nonce=' + nonce;
if (this.getCommentAllowed() && comment && comment.length > this.getCommentAllowed()) {
comment = comment.substr(0, this.getCommentAllowed());
}
if (comment) comment = `&comment=${encodeURIComponent(comment)}`;
const urlToFetch =
this._lnurlPayServicePayload.callback + separator + 'amount=' + Math.floor(amountSat * 1000) + '&nonce=' + nonce + comment;
this._lnurlPayServiceBolt11Payload = await this.fetchGet(urlToFetch);
if (this._lnurlPayServiceBolt11Payload.status === 'ERROR')
throw new Error(this._lnurlPayServiceBolt11Payload.reason || 'requestBolt11FromLnurlPayService() error');
@ -167,6 +173,7 @@ export default class Lnurl {
description,
image,
amount: min,
commentAllowed: data.commentAllowed,
// lnurl: uri,
};
return this._lnurlPayServicePayload;
@ -246,4 +253,8 @@ export default class Lnurl {
format: CryptoJS.format.Hex,
}).toString(CryptoJS.enc.Utf8);
}
getCommentAllowed() {
return this?._lnurlPayServicePayload?.commentAllowed ? parseInt(this._lnurlPayServicePayload.commentAllowed) : false;
}
}

View File

@ -23,6 +23,7 @@ import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
import loc, { formatBalanceWithoutSuffix } from '../../loc';
import Biometric from '../../class/biometrics';
import { BlueStorageContext } from '../../blue_modules/storage-context';
const prompt = require('../../blue_modules/prompt');
const currency = require('../../blue_modules/currency');
export default class LnurlPay extends Component {
@ -95,7 +96,12 @@ export default class LnurlPay extends Component {
let bolt11payload;
try {
bolt11payload = await LN.requestBolt11FromLnurlPayService(amountSats);
let comment;
if (LN.getCommentAllowed()) {
comment = await prompt('Comment', '', false, 'plain-text');
}
bolt11payload = await LN.requestBolt11FromLnurlPayService(amountSats, comment);
await fromWallet.payInvoice(bolt11payload.pr);
const decoded = fromWallet.decodeInvoice(bolt11payload.pr);
this.setState({ payButtonDisabled: false });

View File

@ -61,6 +61,7 @@ describe('LNURL', function () {
assert.deepStrictEqual(lnurlpayPayload, {
amount: 1,
callback: 'https://lntxbot.bigsun.xyz/lnurl/pay/callback?userid=7116',
commentAllowed: undefined,
description: 'Fund @overtorment account on t.me/lntxbot.',
domain: 'lntxbot.bigsun.xyz',
fixed: false,
@ -97,6 +98,62 @@ describe('LNURL', function () {
assert.strictEqual(LN.getImage(), undefined);
assert.strictEqual(LN.getLnurl(), 'LNURL1DP68GURN8GHJ7MRWW3UXYMM59E3XJEMNW4HZU7RE0GHKCMN4WFKZ7URP0YLH2UM9WF5KG0FHXYCNV9G9W58');
assert.strictEqual(LN.getDisposable(), false);
assert.strictEqual(LN.getCommentAllowed(), false);
});
it('can callLnurlPayService() and requestBolt11FromLnurlPayService() with comment', async () => {
const LN = new Lnurl('lnurl1dp68gurn8ghj7cmgv96zucnvd9u8gampd3kx2apwvdhk6tmpwp5j7um9dejz6ar90p6q3eqkzd');
// poor-man's mock:
LN._fetchGet = LN.fetchGet;
LN.fetchGet = () => {
return {
status: 'OK',
callback: 'https://lntxbot.bigsun.xyz/lnurl/pay/callback?userid=7116',
tag: 'payRequest',
maxSendable: 1000000000,
minSendable: 1000,
metadata: '[["text/plain","Comment on lnurl-pay chat 📝"]]',
commentAllowed: 144,
};
};
const lnurlpayPayload = await LN.callLnurlPayService();
assert.deepStrictEqual(lnurlpayPayload, {
amount: 1,
callback: 'https://lntxbot.bigsun.xyz/lnurl/pay/callback?userid=7116',
commentAllowed: 144,
description: 'Comment on lnurl-pay chat 📝',
domain: 'lntxbot.bigsun.xyz',
fixed: false,
image: undefined,
max: 1000000,
metadata: '[["text/plain","Comment on lnurl-pay chat 📝"]]',
min: 1,
});
assert.strictEqual(LN.getDomain(), 'lntxbot.bigsun.xyz');
assert.strictEqual(LN.getDescription(), 'Comment on lnurl-pay chat 📝');
assert.strictEqual(LN.getImage(), undefined);
assert.strictEqual(LN.getLnurl(), 'lnurl1dp68gurn8ghj7cmgv96zucnvd9u8gampd3kx2apwvdhk6tmpwp5j7um9dejz6ar90p6q3eqkzd');
assert.strictEqual(LN.getCommentAllowed(), 144);
// mock only to get fetched url:
let urlUsed = '';
LN.fetchGet = urlToFetch => {
urlUsed = urlToFetch;
return {
disposable: true,
pr:
'lnbc100n1psj8g53pp50t7xmnvnzsm6y78kcvqqudlnnushc04sevtneessp463ndpf83qshp5nh0t5w4w5zh8jdnn5a03hk4pk279l3eex4nzazgkwmqpn7wga6hqcqzpgxqr23ssp5ddpxstde98ekccnvzms67h9uflxmpj939aj4rwc5xwru0x6nfkus9qyyssq55n5hn9gwmrzx2ekajlqshvu53u8h3p0npu7ng4d0lnttgueprzr4mtpwa83jrpz4skhdx3p0xnh9jc92ysnu8umuwa70hkxhp44svsq9u5uqr',
successAction: null,
};
};
try {
await LN.requestBolt11FromLnurlPayService(10, 'hola pendejo!');
} finally {
assert.ok(urlUsed.includes('&comment=hola%20pendejo!'));
}
});
it('can decipher AES', () => {