mirror of
https://github.com/nostrlabs-io/zap-stream-flutter.git
synced 2025-06-14 11:27:43 +00:00
204 lines
5.2 KiB
Dart
204 lines
5.2 KiB
Dart
import 'dart:convert';
|
|
import 'dart:developer' as developer;
|
|
|
|
import 'package:collection/collection.dart';
|
|
import 'package:flutter/widgets.dart';
|
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
|
import 'package:ndk/ndk.dart';
|
|
import 'package:ndk/shared/nips/nip01/bip340.dart';
|
|
import 'package:ndk/shared/nips/nip19/nip19.dart';
|
|
import 'package:zap_stream_flutter/const.dart';
|
|
import 'package:zap_stream_flutter/utils.dart';
|
|
|
|
enum WalletType { nwc }
|
|
|
|
class WalletConfig {
|
|
final WalletType type;
|
|
final String data;
|
|
final String? privateKey;
|
|
|
|
WalletConfig({required this.type, required this.data, this.privateKey});
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return {"type": type.name, "data": data, "privateKey": privateKey};
|
|
}
|
|
|
|
static WalletConfig fromJson(Map<String, dynamic> json) {
|
|
final type = WalletType.values.firstWhereOrNull(
|
|
(v) => v.name == json["type"],
|
|
);
|
|
if (type == null) {
|
|
throw "Invalid wallet type: ${json["type"]}";
|
|
}
|
|
return WalletConfig(
|
|
type: type,
|
|
data: json["data"],
|
|
privateKey: json["privateKey"],
|
|
);
|
|
}
|
|
}
|
|
|
|
abstract class SimpleWallet {
|
|
Future<String> payInvoice(String pr);
|
|
}
|
|
|
|
class NWCWrapper extends SimpleWallet {
|
|
final NwcConnection _conn;
|
|
|
|
NWCWrapper({required NwcConnection conn}) : _conn = conn;
|
|
|
|
@override
|
|
Future<String> payInvoice(String pr) async {
|
|
final rsp = await ndk.nwc.payInvoice(
|
|
_conn,
|
|
invoice: pr,
|
|
timeout: Duration(seconds: 60),
|
|
);
|
|
if (rsp.preimage == null) {
|
|
throw "Payment failed, preimage missing";
|
|
} else {
|
|
return rsp.preimage!;
|
|
}
|
|
}
|
|
}
|
|
|
|
class LoginAccount {
|
|
final AccountType type;
|
|
final String pubkey;
|
|
final String? privateKey;
|
|
final List<String>? signerRelays;
|
|
final WalletConfig? wallet;
|
|
|
|
SimpleWallet? _cachedWallet;
|
|
|
|
LoginAccount({
|
|
required this.type,
|
|
required this.pubkey,
|
|
this.privateKey,
|
|
this.signerRelays,
|
|
this.wallet,
|
|
});
|
|
|
|
static LoginAccount nip19(String key) {
|
|
final keyData = bech32ToHex(key);
|
|
final pubkey =
|
|
Nip19.isKey("nsec", key) ? Bip340.getPublicKey(keyData) : keyData;
|
|
final privateKey = Nip19.isKey("npub", key) ? null : keyData;
|
|
return LoginAccount(
|
|
type:
|
|
Nip19.isKey("npub", key)
|
|
? AccountType.publicKey
|
|
: AccountType.privateKey,
|
|
pubkey: pubkey,
|
|
privateKey: privateKey,
|
|
);
|
|
}
|
|
|
|
static LoginAccount privateKeyHex(String key) {
|
|
return LoginAccount(
|
|
type: AccountType.privateKey,
|
|
privateKey: key,
|
|
pubkey: Bip340.getPublicKey(key),
|
|
);
|
|
}
|
|
|
|
static LoginAccount externalPublicKeyHex(String key) {
|
|
return LoginAccount(type: AccountType.externalSigner, pubkey: key);
|
|
}
|
|
|
|
static LoginAccount bunker(
|
|
String privateKey,
|
|
String pubkey,
|
|
List<String> relays,
|
|
) {
|
|
return LoginAccount(
|
|
type: AccountType.externalSigner,
|
|
pubkey: pubkey,
|
|
privateKey: privateKey,
|
|
signerRelays: relays,
|
|
);
|
|
}
|
|
|
|
static Map<String, dynamic> toJson(LoginAccount? acc) => {
|
|
"type": acc?.type.name,
|
|
"pubKey": acc?.pubkey,
|
|
"privateKey": acc?.privateKey,
|
|
"wallet": acc?.wallet?.toJson(),
|
|
};
|
|
|
|
static LoginAccount? fromJson(Map<String, dynamic> json) {
|
|
if (json.length > 2 && json.containsKey("pubKey")) {
|
|
if ((json["pubKey"] as String).length != 64) {
|
|
throw "Invalid pubkey, length != 64";
|
|
}
|
|
if (json.containsKey("privateKey")) {
|
|
final privKey = json["privateKey"] as String?;
|
|
if (privKey != null && privKey.length != 64) {
|
|
throw "Invalid privateKey, length != 64";
|
|
}
|
|
}
|
|
return LoginAccount(
|
|
type: AccountType.values.firstWhere(
|
|
(v) => v.toString().endsWith(json["type"] as String),
|
|
),
|
|
pubkey: json["pubKey"],
|
|
privateKey: json["privateKey"],
|
|
wallet:
|
|
json.containsKey("wallet")
|
|
? WalletConfig.fromJson(json["wallet"])
|
|
: null,
|
|
);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
Future<SimpleWallet?> getWallet() async {
|
|
if (_cachedWallet == null && wallet != null) {
|
|
switch (wallet!.type) {
|
|
case WalletType.nwc:
|
|
{
|
|
try {
|
|
final conn = await ndk.nwc.connect(wallet!.data);
|
|
_cachedWallet = NWCWrapper(conn: conn);
|
|
} catch (e) {
|
|
developer.log("Failed to setup wallet: $e");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return _cachedWallet;
|
|
}
|
|
}
|
|
|
|
class LoginData extends ValueNotifier<LoginAccount?> {
|
|
final _storage = FlutterSecureStorage();
|
|
static const String _storageKey = "accounts";
|
|
|
|
LoginData() : super(null) {
|
|
super.addListener(() async {
|
|
if (value != null) {
|
|
final data = json.encode(LoginAccount.toJson(value));
|
|
await _storage.write(key: _storageKey, value: data);
|
|
} else {
|
|
await _storage.delete(key: _storageKey);
|
|
}
|
|
});
|
|
}
|
|
|
|
void logout() {
|
|
super.value = null;
|
|
}
|
|
|
|
Future<void> load() async {
|
|
final acc = await _storage.read(key: _storageKey);
|
|
if (acc?.isNotEmpty ?? false) {
|
|
try {
|
|
super.value = LoginAccount.fromJson(json.decode(acc!));
|
|
} catch (e) {
|
|
developer.log(e.toString());
|
|
}
|
|
}
|
|
}
|
|
}
|