Browse Source

ADD: pay zero-amount (tip) invoices

zigzag
Overtorment 6 years ago
parent
commit
4c1affead1
  1. 65
      LightningCustodianWallet.test.js
  2. 4
      class/lightning-custodian-wallet.js
  3. 4
      prompt.js
  4. 31
      screen/lnd/scanLndInvoice.js

65
LightningCustodianWallet.test.js

@ -247,4 +247,69 @@ describe('LightningCustodianWallet', () => {
assert.equal(lOld.balance - oldBalance, 1); assert.equal(lOld.balance - oldBalance, 1);
assert.equal(lNew.balance, 0); assert.equal(lNew.balance, 0);
}); });
it('can pay free amount (tip) invoice', async function() {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100 * 1000;
if (!process.env.BLITZHUB) {
console.error('process.env.BLITZHUB not set, skipped');
return;
}
// fetchig invoice from tippin.me :
const api = new Frisbee({
baseURI: 'https://tippin.me',
});
const res = await api.post('/lndreq/newinvoice.php', {
headers: {
Origin: 'https://tippin.me',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'en-GB,en-US;q=0.9,en;q=0.8',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
Accept: 'application/json, text/javascript, */*; q=0.01',
},
body: 'userid=1188&username=overtorment&istaco=0&customAmnt=0&customMemo=',
});
let json;
let invoice;
if (res && res.body && (json = JSON.parse(res.body)) && json.message) {
invoice = json.message;
} else {
throw new Error('tippin.me problem: ' + JSON.stringify(res));
}
// --> use to pay specific invoice
// invoice =
// 'lnbc1pwrp35spp5z62nvj8yw6luq7ns4a8utpwn2qkkdwdt0ludwm54wjeazk2xv5wsdpu235hqurfdcsx7an9wf6x7undv4h8ggpgw35hqurfdchx6eff9p6nzvfc8q5scqzysxqyz5vqj8xq6wz6dezmunw6qxleuw67ensjnt3fldltrmmkvzurge0dczpn94fkwwh7hkh5wqrhsvfegtvhswn252hn6uw5kx99dyumz4v5n9sp337py2';
let l2 = new LightningCustodianWallet();
l2.setSecret(process.env.BLITZHUB);
await l2.authorize();
await l2.fetchTransactions();
await l2.fetchBalance();
let oldBalance = +l2.balance;
let txLen = l2.transactions_raw.length;
let decoded = await l2.decodeInvoice(invoice);
assert.ok(decoded.payment_hash);
assert.ok(decoded.description);
assert.equal(+decoded.num_satoshis, 0);
await l2.checkRouteInvoice(invoice);
let start = +new Date();
await l2.payInvoice(invoice, 3);
let end = +new Date();
if ((end - start) / 1000 > 9) {
console.warn('payInvoice took', (end - start) / 1000, 'sec');
}
await l2.fetchTransactions();
assert.equal(l2.transactions_raw.length, txLen + 1);
// transactions became more after paying an invoice
await l2.fetchBalance();
assert.equal(oldBalance - l2.balance, 3);
});
}); });

4
class/lightning-custodian-wallet.js

@ -102,9 +102,9 @@ export class LightningCustodianWallet extends LegacyWallet {
this.secret = 'lndhub://' + json.login + ':' + json.password; this.secret = 'lndhub://' + json.login + ':' + json.password;
} }
async payInvoice(invoice) { async payInvoice(invoice, freeAmount = 0) {
let response = await this._api.post('/payinvoice', { let response = await this._api.post('/payinvoice', {
body: { invoice: invoice }, body: { invoice: invoice, amount: freeAmount },
headers: { headers: {
'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json', 'Content-Type': 'application/json',

4
prompt.js

@ -1,6 +1,6 @@
import prompt from 'react-native-prompt-android'; import prompt from 'react-native-prompt-android';
module.exports = (title, text, isCancelable = true) => { module.exports = (title, text, isCancelable = true, type = 'secure-text') => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const buttons = isCancelable const buttons = isCancelable
? [ ? [
@ -30,7 +30,7 @@ module.exports = (title, text, isCancelable = true) => {
]; ];
prompt(title, text, buttons, { prompt(title, text, buttons, {
type: 'secure-text', type: type,
cancelable: isCancelable, cancelable: isCancelable,
}); });
}); });

31
screen/lnd/scanLndInvoice.js

@ -10,6 +10,7 @@ let BlueApp = require('../../BlueApp');
let currency = require('../../currency'); let currency = require('../../currency');
let EV = require('../../events'); let EV = require('../../events');
let loc = require('../../loc'); let loc = require('../../loc');
let prompt = require('../../prompt');
const { width } = Dimensions.get('window'); const { width } = Dimensions.get('window');
export default class ScanLndInvoice extends React.Component { export default class ScanLndInvoice extends React.Component {
@ -92,6 +93,14 @@ export default class ScanLndInvoice extends React.Component {
let decoded; let decoded;
try { try {
decoded = await w.decodeInvoice(data); decoded = await w.decodeInvoice(data);
let freeAmount = 0;
while (+decoded.num_satoshis === 0) {
freeAmount = await prompt('This is free amount invoice', 'How many satoshis do you want to tip?', false, 'numeric');
freeAmount = parseInt(freeAmount);
if (!isNaN(freeAmount) && freeAmount > 0) {
decoded.num_satoshis = freeAmount;
}
}
let expiresIn = (decoded.timestamp * 1 + decoded.expiry * 1) * 1000; // ms let expiresIn = (decoded.timestamp * 1 + decoded.expiry * 1) * 1000; // ms
if (+new Date() > expiresIn) { if (+new Date() > expiresIn) {
@ -105,6 +114,7 @@ export default class ScanLndInvoice extends React.Component {
invoice: data, invoice: data,
decoded, decoded,
expiresIn, expiresIn,
freeAmount,
destination: data, destination: data,
}); });
} catch (Err) { } catch (Err) {
@ -143,7 +153,7 @@ export default class ScanLndInvoice extends React.Component {
let start = +new Date(); let start = +new Date();
let end; let end;
try { try {
await fromWallet.payInvoice(this.state.invoice); await fromWallet.payInvoice(this.state.invoice, this.state.freeAmount);
end = +new Date(); end = +new Date();
} catch (Err) { } catch (Err) {
console.log(Err.message); console.log(Err.message);
@ -170,7 +180,24 @@ export default class ScanLndInvoice extends React.Component {
render() { render() {
return ( return (
<TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}> <TouchableWithoutFeedback
onPress={async () => {
if (this.state.freeAmount) {
// must ask user again about the amount
let freeAmount = await prompt('This is free amount invoice', 'How many satoshis do you want to tip?', false, 'numeric');
freeAmount = parseInt(freeAmount);
if (!isNaN(freeAmount) && freeAmount > 0) {
let decoded = this.state.decoded;
decoded.num_satoshis = freeAmount;
this.setState({ decoded, freeAmount });
Keyboard.dismiss();
}
} else {
Keyboard.dismiss();
}
}}
accessible={false}
>
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={{ flex: 1 }}> <SafeBlueArea forceInset={{ horizontal: 'always' }} style={{ flex: 1 }}>
<Text style={{ textAlign: 'center', fontSize: 50, fontWeight: '700', color: '#2f5fb3' }}> <Text style={{ textAlign: 'center', fontSize: 50, fontWeight: '700', color: '#2f5fb3' }}>
{this.state.hasOwnProperty('decoded') && {this.state.hasOwnProperty('decoded') &&

Loading…
Cancel
Save