Skip to content
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

code refactoring, optimalisations, tests, isBech32 func, rename decodeUri to parseUri #8

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# dart_lnurl
[![pub package](https://img.shields.io/badge/pub-0.0.1-blueviolet.svg)](https://pub.dev/packages/dart_lnurl)

A Dart implementation of lnurl to decode bech32 lnurl strings. Currently supports the following tags:
A Dart implementation of lnurl to decode bech32 and parse non-bech32 lnurl strings. Currently supports the following tags:
* withdrawRequest
* payRequest
* channelRequest
Expand All @@ -10,16 +10,16 @@ A Dart implementation of lnurl to decode bech32 lnurl strings. Currently support
## Features
* ✅ Decode a bech32-encoded lnurl string.
* ✅ Handles LUD-17 non-bech32 lnurl string (lnurlw, lnurlp, lnurlc, keyauth).
* ✅ Make GET request to the decoded ln service and return the response.
* ✅ Make GET request to the ln service and return the response.



Learn more about the lnurl spec here: https://github.com/btcontract/lnurl-rfc

# API Reference

`Future<LNURLParseResult> getParams(String encodedUrl)`
Use this to parse an encoded bech32 lnurl string, call the decoded URI, and return the parsed response from the lnurl service. The `encodedUrl` can either have `lightning:` in it or not.
`Future<LNURLParseResult> getParams(String url)`
Use this to parse a lnurl string, call the (decoded if needed) URI, and return the parsed response from the lnurl service. The bech32-encoded `url` can either have `lightning:` in it or not and non-bech32 `url` can either have `lnurlw:`,`lnurlp:`,`lnurlc:` or `keyauth:`.

`String decryptSuccessActionAesPayload({LNURLPaySuccessAction successAction, String preimage})`

Expand Down
57 changes: 23 additions & 34 deletions lib/dart_lnurl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,24 @@ export 'src/types.dart';
export 'src/success_action.dart';
export 'src/bech32.dart';

Uri decodeUri(String encodedUrl) {
Uri decodedUri;

/// The URL doesn't have to be encoded at all as per LUD-17: Protocol schemes and raw (non bech32-encoded) URLs.
/// https://github.com/lnurl/luds/blob/luds/17.md
/// Handle non bech32-encoded LNURL
final lud17prefixes = ['lnurlw', 'lnurlc', 'lnurlp', 'keyauth'];
decodedUri = Uri.parse(encodedUrl);
for (final prefix in lud17prefixes) {
if (decodedUri.scheme.contains(prefix)) {
decodedUri = decodedUri.replace(scheme: prefix);
}
}
if (lud17prefixes.contains(decodedUri.scheme)) {
/// If the non-bech32 LNURL is a Tor address, the port has to be http instead of https for the clearnet LNURL so check if the host ends with '.onion' or '.onion.'
decodedUri = decodedUri.replace(
scheme: decodedUri.host.endsWith('onion') ||
decodedUri.host.endsWith('onion.')
? 'http'
: 'https');
} else {
Uri parseLnUri(String input) {
Uri parsedUri;
//Handle the cases when Uri doesn't have to be bech32 encoded, as per LUD-17

if (isbech32(input)) {
/// Bech32 encoded URL
/// Try to parse the input as a lnUrl. Will throw an error if it fails.
final lnUrl = findLnUrl(encodedUrl);
final lnUrl = findLnUrl(input);

/// Decode the lnurl using bech32
final bech32 = Bech32Codec().decode(lnUrl, lnUrl.length);
decodedUri = Uri.parse(utf8.decode(fromWords(bech32.data)));
parsedUri = Uri.parse(utf8.decode(fromWords(bech32.data)));
} else {
/// Non-Bech32 encoded URL
final String lnUrl = findLnUrlNonBech32(input);
parsedUri = Uri.parse(lnUrl);
}
return decodedUri;
return parsedUri;
}

/// Get params from a lnurl string. Possible types are:
Expand All @@ -51,11 +40,11 @@ Uri decodeUri(String encodedUrl) {
/// * `LNURLPayParams`
///
/// Throws [ArgumentError] if the provided input is not a valid lnurl.
Future<LNURLParseResult> getParams(String encodedUrl) async {
final decodedUri = decodeUri(encodedUrl);
Future<LNURLParseResult> getParams(String url) async {
final parsedUri = parseLnUri(url);
try {
/// Call the lnurl to get a response
final res = await http.get(decodedUri);
final res = await http.get(parsedUri);

/// If there's an error then throw it
if (res.statusCode >= 300) {
Expand All @@ -70,8 +59,8 @@ Future<LNURLParseResult> getParams(String encodedUrl) async {
error: LNURLErrorResponse.fromJson({
...parsedJson,
...{
'domain': decodedUri.host,
'url': decodedUri.toString(),
'domain': parsedUri.host,
'url': parsedUri.toString(),
}
}),
);
Expand Down Expand Up @@ -113,8 +102,8 @@ Future<LNURLParseResult> getParams(String encodedUrl) async {
error: LNURLErrorResponse.fromJson({
...parsedJson,
...{
'domain': decodedUri.host,
'url': decodedUri.toString(),
'domain': parsedUri.host,
'url': parsedUri.toString(),
}
}),
);
Expand All @@ -126,9 +115,9 @@ Future<LNURLParseResult> getParams(String encodedUrl) async {
return LNURLParseResult(
error: LNURLErrorResponse.fromJson({
'status': 'ERROR',
'reason': '${decodedUri.toString()} returned error: ${e.toString()}',
'url': decodedUri.toString(),
'domain': decodedUri.host,
'reason': '${parsedUri.toString()} returned error: ${e.toString()}',
'url': parsedUri.toString(),
'domain': parsedUri.host,
}),
);
}
Expand Down
31 changes: 31 additions & 0 deletions lib/src/lnurl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,34 @@ String findLnUrl(String input) {
throw ArgumentError('Not a valid lnurl string');
}
}

String findLnUrlNonBech32(String input) {
/// The URL doesn't have to be encoded at all as per LUD-17: Protocol schemes and raw (non bech32-encoded) URLs.
/// https://github.com/lnurl/luds/blob/luds/17.md
/// Handle non bech32-encoded LNURL

final lud17prefixes = ['lnurlw', 'lnurlp', 'keyauth', 'lnurlc'];
Uri parsedUri = Uri.parse(input);
for (final prefix in lud17prefixes) {
if (parsedUri.scheme.contains(prefix)) {
parsedUri = parsedUri.replace(scheme: prefix);
break;
}
}
if (lud17prefixes.contains(parsedUri.scheme)) {
/// If the non-bech32 LNURL is a Tor address, the port has to be http instead of https for the clearnet LNURL so check if the host ends with '.onion' or '.onion.'
parsedUri = parsedUri.replace(
scheme: parsedUri.host.endsWith('onion') ||
parsedUri.host.endsWith('onion.')
? 'http'
: 'https');
}
return parsedUri.toString();
}

bool isbech32(String input) {
final match = new RegExp(
r',*?((lnurl)([0-9]{1,}[a-z0-9]+){1})',
).allMatches(input.toLowerCase());
return match.length == 1 ? true : false;
}
Loading