-
Notifications
You must be signed in to change notification settings - Fork 133
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add WebLN support #215
Add WebLN support #215
Conversation
cde3e08
to
959380d
Compare
frontend/src/components/OrderPage.js
Outdated
} else { | ||
this.setState({ waitingWebNL: false }); | ||
} | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, confirm these are the right combinations of user role + status + invoice
a9be7b8
to
452b255
Compare
frontend/src/components/OrderPage.js
Outdated
} | ||
|
||
orderDetailsReceived = (data) =>{ | ||
if (data.status !== this.state.status) { this.handleWebLN(data) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the only way I found to manage status
changes without using Hooks, happy to hear any other suggestions I might have missed out.
This looks absolutely fantastic!! Congrats! 🚀 I assume there is about 5-10 second when Alby shows the spinner and RoboSats still shows "please check your WebLN wallet"? Does WebLN send any signal the moment the payment is initiated? If so, it could be used to trigger a text change in the dialog "Waiting for your bond to be accepted".
I also was not able to get the extension to work. I cannot get pass the password creation step, the "confirm password" button simply does not work. Have you tried testing on Brave with Tor or on a torified Mozilla session? We should report about the TOR Browser bug anyway. Can WebLN generate and autofill the Bolt11 invoice requested to buyers in |
On average for me took 1-2 seconds, never 5.
No, there are 0 signals from the provider until the payment is completed, so never. WebLN is really restrictive.
I tried Brave but looks like it has a strong CORS block, even for localhost, Tor Browser has the same limitation and I manage to make it work, but I had no luck with brave. I never tried the torified Mozilla session.
I think I can create tomorrow an issue for them.
I think what you want is to use this function right? https://webln.dev/#/api/make-invoice |
Ah, I see. Well, then this is the best approach possible at the moment :)
Very appreciated!
It looks exactly like what is needed, with an explicit amount. Once the user confirms creating the invoice on the extension RoboSats could directly proceed with the API request. |
c9e9810
to
1160fb1
Compare
Signed-off-by: KoalaSat <111684255+KoalaSat@users.noreply.github.com>
2dd915f
to
e3ef5b0
Compare
Been testing it for a little while, it's amazing! I need more time to test it but I am super tight these days. I left a few comments. This feature will be the best companion with the release of the Umbrel / Citadel apps as TOR browser won't be a limiting factor for those that self host the RoboSats client. |
@@ -90,6 +93,18 @@ const ProfileDialog = ({ | |||
copyToClipboard(`http://${host}/ref/${referralCode}`); | |||
}; | |||
|
|||
const handleWeblnInvoiceClicked = (e: any) =>{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const handleWeblnInvoiceClicked = (e: any) =>{ | |
const handleWeblnInvoiceClicked = async (e: any) =>{ |
webln.makeInvoice(earnedRewards).then((response) => { | ||
handleSubmitInvoiceClicked(e, response.paymentRequest); | ||
}) | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about the following suggestion to avoid the nested callbacks?
const webln = await getWebln();
const response = await webln.makeInvoice(earnedRewards);
handleSubmitInvoiceClicked(e, response.paymentRequest)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If that's the patterns you apply it's fine, but I'm curious now, what's the difference?
frontend/src/components/OrderPage.js
Outdated
@@ -113,6 +122,44 @@ class OrderPage extends Component { | |||
this.getOrderDetails(this.state.orderId); | |||
} | |||
|
|||
handleWebln = (data) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same concept could be applied here, so we can await the getWebln
function call.
frontend/src/components/OrderPage.js
Outdated
} else { | ||
this.setState({ waitingWebNL: false }); | ||
} | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These conditions exactly match the rol+status that is expected to lock an invoice.
https://github.com/Reckless-Satoshi/robosats/blob/67a542cf31d36933a0bbb5bb1832110ba0070e12/api/models.py#L272-L279
The use of data.bond_invoice
and escrow_invoice
is also correct.
frontend/static/locales/en.json
Outdated
@@ -300,6 +300,9 @@ | |||
"This order has been cancelled collaborativelly":"This order has been cancelled collaboratively", | |||
"This order is not available":"This order is not available", | |||
"The Robotic Satoshis working in the warehouse did not understand you. Please, fill a Bug Issue in Github https://github.com/reckless-satoshi/robosats/issues":"The Robotic Satoshis working in the warehouse did not understand you. Please, fill a Bug Issue in Github https://github.com/reckless-satoshi/robosats/issues", | |||
"WebNL Payment": "WebNL Payment", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just small typo WebNL -> WebLN (happens in every translation file and javascript source)
frontend/src/components/OrderPage.js
Outdated
this.setState({openWebNLDialog: false}); | ||
} | ||
|
||
webNLDialog =() =>{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I noticed yesterday and I tried to change it everywhere, on the last version of this PR all NL
typos should be fixed
frontend/src/components/OrderPage.js
Outdated
{this.state.waitingWebln ? | ||
<> | ||
<CircularProgress size={16} thickness={5} style={{ marginRight: 10 }}/> | ||
{data.is_buyer ? t("Invoice not received, please check your WebLN wallet.") : t("Payment not received, please check your WebLN wallet.")} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
data.is_buyer
does not exist. Therefore the app crashes when creating a maker / buy order. It should be replaced with this.state.is_buyer
frontend/src/components/HomePage.js
Outdated
getWebln() | ||
.then((webln) => { | ||
this.setState({ weblnEnabled: webln !== undefined }) | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is probably not the best location to introduce the webln enabling routine. In case webln is not enabled, it will keep checking everytime setAppState is called (that's very often!).
I tested to directly define the state variable as
weblnEnabled: getWebln().then((webln) => {webln !== undefined })
However webln enabling tends to fail the first few times. On componentDidMount
does also not work for me. Ideas?
componentDidMount() {
getWebln()
.then((webln) => {
this.setState({ weblnEnabled: webln !== undefined })
});
};
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok on that case let's keep it on the component using it (just the rewards), I move it there it was more convenient for future uses, anyways with the implementation of typescripts eventually it might be created a Context for this one-shot global data.
frontend/src/components/OrderPage.js
Outdated
</DialogContentText> | ||
</DialogContent> | ||
<DialogActions> | ||
<Button onClick={this.handleCloseWeblnDialog} autoFocus>{t("Go back")}</Button> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Done" might be more appropriate than "Go back" when the text is "You can close now your WebLN wallet popup."
frontend/src/components/OrderPage.js
Outdated
this.setState({ waitingWebln: true, openWeblnDialog: true}); | ||
} else if (data.is_buyer & (data.status == 6 || data.status == 8 )) { | ||
webln.makeInvoice(data.amount).then((invoice) => { | ||
this.sendWeblnInvoice(invoice) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Submitting a payout invoice as a buyer. I get this error immediately after clicking the
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'id')
at OrderPage.eval [as sendWeblnInvoice] (OrderPage.js:216:61)
In case the user rejects the creation of the invoice ("Cancel" button in Aldy) the "User Rejected" response could be used to close the WebLN Dialog.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pop up with the spinner also stays open when the invoice is successfully submitted and the order moves to the next state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a catch
will do the trick, but I can't confirm
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume you cannot test it since this feature can only be tested on mainnet. While it is a bit of a hassle, it possible to test the submission of the invoice payout on the mainnet platform by taking your own order with another Robot. After the escrow is locked (20K Sats), you can collaboratively cancel to unlock every hodl invoice.
frontend/src/components/OrderPage.js
Outdated
'invoice': invoice, | ||
}), | ||
}; | ||
fetch('/api/order/' + '?order_id=' + this.props.data.id, requestOptions) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this.props.data.id
-> this.state.orderId
invoice': invoice,
-> invoice': invoice.paymentRequest,
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Invoice submission as buyer works with these fixes.
frontend/src/components/OrderPage.js
Outdated
webln.sendPayment(data.escrow_invoice); | ||
this.setState({ waitingWebln: true, openWeblnDialog: true}); | ||
} else if (data.is_buyer & (data.status == 6 || data.status == 8 )) { | ||
webln.makeInvoice(data.amount).then((invoice) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
data.amount
is the amount of fiat. I believe we want to use data.trade_satoshis
.Otherwise the invoice is rejected by the backend for having the wrong amount of Sats.
frontend/src/components/OrderPage.js
Outdated
this.setState({openWebNLDialog: false}); | ||
} | ||
|
||
webNLDialog =() =>{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I noticed yesterday and I tried to change it everywhere, on the last version of this PR all NL
typos should be fixed
4e7ea5f
to
c71cead
Compare
56d9504
to
4af8ae2
Compare
Everything ready! thanks for the feedback @Reckless-Satoshi @fernandoporazzi |
{this.state.is_buyer ? t("Invoice not received, please check your WebLN wallet.") : t("Payment not received, please check your WebLN wallet.")} | ||
</> | ||
: <> | ||
{t("You can close now your WebLN wallet popup.")} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To highlight the barely noticeable change in the dialog when the bond is locked, after CircularProgress spinner is gone we could place a green checkmark
import CheckIcon from '@mui/icons-material/Check';
...
<CheckIcon color="success"/>
{t("You can close now your WebLN wallet popup.")}
...
webln.sendPayment(data.escrow_invoice); | ||
this.setState({ waitingWebln: true, openWeblnDialog: true}); | ||
} else if (data.is_buyer & (data.status == 6 || data.status == 8 )) { | ||
webln.makeInvoice(data.trade_satoshis) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this.setState({ waitingWebln: true, openWeblnDialog: true});
here
webln.makeInvoice(data.trade_satoshis) | ||
.then((invoice) => { | ||
if (invoice) { | ||
this.sendWeblnInvoice(invoice.paymentRequest); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this.setState({ waitingWebln: true, openWeblnDialog: true});
from here. The Dialog must be opened earlier.
this.setState({ waitingWebln: true, openWeblnDialog: true}); | ||
} | ||
}).catch(() => { | ||
this.setState({ waitingWebln: false, openWeblnDialog: false }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The catch does not close the dialog if the invoice is successfully submitted and the order advances status. The dialog only closes with this catch when the Alby invoice generation is Canceled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ON that case we need to set some special conditions here, so the dialog is closed automatically with invoices but keeps open with payment, makes sense.
It's looking ready to be merged! There is only a couple of things to polish with the invoice submission (comments above). Please @KoalaSat , send a LN invoice for 200K Sats with a long expiration time from a proxy wallet (not your node pubkey) Thanks a lot @fernandoporazzi for reviewing |
Last changes applied, thanks for the extra support!
|
3662393239386161343530323765363265366431643766303565653164383237 |
* Add WebLN support * Fix Variable Typo * Invoice Generation Signed-off-by: KoalaSat <111684255+KoalaSat@users.noreply.github.com> * Code Review * Second CR * Catch cancelations * Final Review Signed-off-by: KoalaSat <111684255+KoalaSat@users.noreply.github.com>
* Add WebLN support * Fix Variable Typo * Invoice Generation Signed-off-by: KoalaSat <111684255+KoalaSat@users.noreply.github.com> * Code Review * Second CR * Catch cancelations * Final Review Signed-off-by: KoalaSat <111684255+KoalaSat@users.noreply.github.com>
* Add WebLN support * Fix Variable Typo * Invoice Generation Signed-off-by: KoalaSat <111684255+KoalaSat@users.noreply.github.com> * Code Review * Second CR * Catch cancelations * Final Review Signed-off-by: KoalaSat <111684255+KoalaSat@users.noreply.github.com>
Fixes #176
This PR implements the automatic detection of WebLN providers on user's browser.
It also includes a workaround to deal with locked payments, since WebLN does not support yet this feature, we just wait for the back-end to communicate a new status.
The main workflow will behave as follows:
The user with a WebLN app takes/creates an order.
Robosats detects WebLN and automatically tries to contact with the provider, if this is the first time, the provider ask for permissions to the user:
Robosats creates a payment with the BOLT-11 invoice received from back-end, a notification is shown to help the user understand the next step:
The user accepts the payment on the provider popup, at this point WebLN does not contemplate locking payments so for this case (Alby) it'll keep loading forever, until the user cancels or the order expires, Robosats updates the message to help the user understand the next step:
The user closes the provider popup and continue with the standard order process.
I tried to create several payment locks with the same wallet and despite the loading button problem, all payments are successfully locked in the network.
@Reckless-Satoshi - I need some help testing this on Tor Browser, looks like my Alby extension does not work.