feat: implement Phase 1 Rust API core (keys, token, mint_info, wallet)

This commit is contained in:
Forte11Cuba
2026-03-28 03:14:40 -06:00
parent e4f60010f7
commit fa5fd7d98e
20 changed files with 14223 additions and 17 deletions
+31
View File
@@ -0,0 +1,31 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import '../frb_generated.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
import 'package:freezed_annotation/freezed_annotation.dart' hide protected;
part 'error.freezed.dart';
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `fmt`, `fmt`, `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`
@freezed
sealed class Error with _$Error implements FrbException {
const Error._();
/// Error del protocolo Cashu (cdk)
const factory Error.cdk(String field0) = Error_Cdk;
/// Error de base de datos (SQLite)
const factory Error.database(String field0) = Error_Database;
/// Input inválido del usuario
const factory Error.invalidInput() = Error_InvalidInput;
/// Error de red (HTTP, conexión)
const factory Error.network(String field0) = Error_Network;
/// Error de UR (QR multiframe)
const factory Error.ur(String field0) = Error_Ur;
}
+863
View File
@@ -0,0 +1,863 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'error.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models',
);
/// @nodoc
mixin _$Error {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String field0) cdk,
required TResult Function(String field0) database,
required TResult Function() invalidInput,
required TResult Function(String field0) network,
required TResult Function(String field0) ur,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String field0)? cdk,
TResult? Function(String field0)? database,
TResult? Function()? invalidInput,
TResult? Function(String field0)? network,
TResult? Function(String field0)? ur,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String field0)? cdk,
TResult Function(String field0)? database,
TResult Function()? invalidInput,
TResult Function(String field0)? network,
TResult Function(String field0)? ur,
required TResult orElse(),
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(Error_Cdk value) cdk,
required TResult Function(Error_Database value) database,
required TResult Function(Error_InvalidInput value) invalidInput,
required TResult Function(Error_Network value) network,
required TResult Function(Error_Ur value) ur,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(Error_Cdk value)? cdk,
TResult? Function(Error_Database value)? database,
TResult? Function(Error_InvalidInput value)? invalidInput,
TResult? Function(Error_Network value)? network,
TResult? Function(Error_Ur value)? ur,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(Error_Cdk value)? cdk,
TResult Function(Error_Database value)? database,
TResult Function(Error_InvalidInput value)? invalidInput,
TResult Function(Error_Network value)? network,
TResult Function(Error_Ur value)? ur,
required TResult orElse(),
}) => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ErrorCopyWith<$Res> {
factory $ErrorCopyWith(Error value, $Res Function(Error) then) =
_$ErrorCopyWithImpl<$Res, Error>;
}
/// @nodoc
class _$ErrorCopyWithImpl<$Res, $Val extends Error>
implements $ErrorCopyWith<$Res> {
_$ErrorCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of Error
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
abstract class _$$Error_CdkImplCopyWith<$Res> {
factory _$$Error_CdkImplCopyWith(
_$Error_CdkImpl value,
$Res Function(_$Error_CdkImpl) then,
) = __$$Error_CdkImplCopyWithImpl<$Res>;
@useResult
$Res call({String field0});
}
/// @nodoc
class __$$Error_CdkImplCopyWithImpl<$Res>
extends _$ErrorCopyWithImpl<$Res, _$Error_CdkImpl>
implements _$$Error_CdkImplCopyWith<$Res> {
__$$Error_CdkImplCopyWithImpl(
_$Error_CdkImpl _value,
$Res Function(_$Error_CdkImpl) _then,
) : super(_value, _then);
/// Create a copy of Error
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({Object? field0 = null}) {
return _then(
_$Error_CdkImpl(
null == field0
? _value.field0
: field0 // ignore: cast_nullable_to_non_nullable
as String,
),
);
}
}
/// @nodoc
class _$Error_CdkImpl extends Error_Cdk {
const _$Error_CdkImpl(this.field0) : super._();
@override
final String field0;
@override
String toString() {
return 'Error.cdk(field0: $field0)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$Error_CdkImpl &&
(identical(other.field0, field0) || other.field0 == field0));
}
@override
int get hashCode => Object.hash(runtimeType, field0);
/// Create a copy of Error
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$Error_CdkImplCopyWith<_$Error_CdkImpl> get copyWith =>
__$$Error_CdkImplCopyWithImpl<_$Error_CdkImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String field0) cdk,
required TResult Function(String field0) database,
required TResult Function() invalidInput,
required TResult Function(String field0) network,
required TResult Function(String field0) ur,
}) {
return cdk(field0);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String field0)? cdk,
TResult? Function(String field0)? database,
TResult? Function()? invalidInput,
TResult? Function(String field0)? network,
TResult? Function(String field0)? ur,
}) {
return cdk?.call(field0);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String field0)? cdk,
TResult Function(String field0)? database,
TResult Function()? invalidInput,
TResult Function(String field0)? network,
TResult Function(String field0)? ur,
required TResult orElse(),
}) {
if (cdk != null) {
return cdk(field0);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(Error_Cdk value) cdk,
required TResult Function(Error_Database value) database,
required TResult Function(Error_InvalidInput value) invalidInput,
required TResult Function(Error_Network value) network,
required TResult Function(Error_Ur value) ur,
}) {
return cdk(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(Error_Cdk value)? cdk,
TResult? Function(Error_Database value)? database,
TResult? Function(Error_InvalidInput value)? invalidInput,
TResult? Function(Error_Network value)? network,
TResult? Function(Error_Ur value)? ur,
}) {
return cdk?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(Error_Cdk value)? cdk,
TResult Function(Error_Database value)? database,
TResult Function(Error_InvalidInput value)? invalidInput,
TResult Function(Error_Network value)? network,
TResult Function(Error_Ur value)? ur,
required TResult orElse(),
}) {
if (cdk != null) {
return cdk(this);
}
return orElse();
}
}
abstract class Error_Cdk extends Error {
const factory Error_Cdk(final String field0) = _$Error_CdkImpl;
const Error_Cdk._() : super._();
String get field0;
/// Create a copy of Error
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$Error_CdkImplCopyWith<_$Error_CdkImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$Error_DatabaseImplCopyWith<$Res> {
factory _$$Error_DatabaseImplCopyWith(
_$Error_DatabaseImpl value,
$Res Function(_$Error_DatabaseImpl) then,
) = __$$Error_DatabaseImplCopyWithImpl<$Res>;
@useResult
$Res call({String field0});
}
/// @nodoc
class __$$Error_DatabaseImplCopyWithImpl<$Res>
extends _$ErrorCopyWithImpl<$Res, _$Error_DatabaseImpl>
implements _$$Error_DatabaseImplCopyWith<$Res> {
__$$Error_DatabaseImplCopyWithImpl(
_$Error_DatabaseImpl _value,
$Res Function(_$Error_DatabaseImpl) _then,
) : super(_value, _then);
/// Create a copy of Error
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({Object? field0 = null}) {
return _then(
_$Error_DatabaseImpl(
null == field0
? _value.field0
: field0 // ignore: cast_nullable_to_non_nullable
as String,
),
);
}
}
/// @nodoc
class _$Error_DatabaseImpl extends Error_Database {
const _$Error_DatabaseImpl(this.field0) : super._();
@override
final String field0;
@override
String toString() {
return 'Error.database(field0: $field0)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$Error_DatabaseImpl &&
(identical(other.field0, field0) || other.field0 == field0));
}
@override
int get hashCode => Object.hash(runtimeType, field0);
/// Create a copy of Error
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$Error_DatabaseImplCopyWith<_$Error_DatabaseImpl> get copyWith =>
__$$Error_DatabaseImplCopyWithImpl<_$Error_DatabaseImpl>(
this,
_$identity,
);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String field0) cdk,
required TResult Function(String field0) database,
required TResult Function() invalidInput,
required TResult Function(String field0) network,
required TResult Function(String field0) ur,
}) {
return database(field0);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String field0)? cdk,
TResult? Function(String field0)? database,
TResult? Function()? invalidInput,
TResult? Function(String field0)? network,
TResult? Function(String field0)? ur,
}) {
return database?.call(field0);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String field0)? cdk,
TResult Function(String field0)? database,
TResult Function()? invalidInput,
TResult Function(String field0)? network,
TResult Function(String field0)? ur,
required TResult orElse(),
}) {
if (database != null) {
return database(field0);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(Error_Cdk value) cdk,
required TResult Function(Error_Database value) database,
required TResult Function(Error_InvalidInput value) invalidInput,
required TResult Function(Error_Network value) network,
required TResult Function(Error_Ur value) ur,
}) {
return database(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(Error_Cdk value)? cdk,
TResult? Function(Error_Database value)? database,
TResult? Function(Error_InvalidInput value)? invalidInput,
TResult? Function(Error_Network value)? network,
TResult? Function(Error_Ur value)? ur,
}) {
return database?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(Error_Cdk value)? cdk,
TResult Function(Error_Database value)? database,
TResult Function(Error_InvalidInput value)? invalidInput,
TResult Function(Error_Network value)? network,
TResult Function(Error_Ur value)? ur,
required TResult orElse(),
}) {
if (database != null) {
return database(this);
}
return orElse();
}
}
abstract class Error_Database extends Error {
const factory Error_Database(final String field0) = _$Error_DatabaseImpl;
const Error_Database._() : super._();
String get field0;
/// Create a copy of Error
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$Error_DatabaseImplCopyWith<_$Error_DatabaseImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$Error_InvalidInputImplCopyWith<$Res> {
factory _$$Error_InvalidInputImplCopyWith(
_$Error_InvalidInputImpl value,
$Res Function(_$Error_InvalidInputImpl) then,
) = __$$Error_InvalidInputImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$Error_InvalidInputImplCopyWithImpl<$Res>
extends _$ErrorCopyWithImpl<$Res, _$Error_InvalidInputImpl>
implements _$$Error_InvalidInputImplCopyWith<$Res> {
__$$Error_InvalidInputImplCopyWithImpl(
_$Error_InvalidInputImpl _value,
$Res Function(_$Error_InvalidInputImpl) _then,
) : super(_value, _then);
/// Create a copy of Error
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$Error_InvalidInputImpl extends Error_InvalidInput {
const _$Error_InvalidInputImpl() : super._();
@override
String toString() {
return 'Error.invalidInput()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$Error_InvalidInputImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String field0) cdk,
required TResult Function(String field0) database,
required TResult Function() invalidInput,
required TResult Function(String field0) network,
required TResult Function(String field0) ur,
}) {
return invalidInput();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String field0)? cdk,
TResult? Function(String field0)? database,
TResult? Function()? invalidInput,
TResult? Function(String field0)? network,
TResult? Function(String field0)? ur,
}) {
return invalidInput?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String field0)? cdk,
TResult Function(String field0)? database,
TResult Function()? invalidInput,
TResult Function(String field0)? network,
TResult Function(String field0)? ur,
required TResult orElse(),
}) {
if (invalidInput != null) {
return invalidInput();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(Error_Cdk value) cdk,
required TResult Function(Error_Database value) database,
required TResult Function(Error_InvalidInput value) invalidInput,
required TResult Function(Error_Network value) network,
required TResult Function(Error_Ur value) ur,
}) {
return invalidInput(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(Error_Cdk value)? cdk,
TResult? Function(Error_Database value)? database,
TResult? Function(Error_InvalidInput value)? invalidInput,
TResult? Function(Error_Network value)? network,
TResult? Function(Error_Ur value)? ur,
}) {
return invalidInput?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(Error_Cdk value)? cdk,
TResult Function(Error_Database value)? database,
TResult Function(Error_InvalidInput value)? invalidInput,
TResult Function(Error_Network value)? network,
TResult Function(Error_Ur value)? ur,
required TResult orElse(),
}) {
if (invalidInput != null) {
return invalidInput(this);
}
return orElse();
}
}
abstract class Error_InvalidInput extends Error {
const factory Error_InvalidInput() = _$Error_InvalidInputImpl;
const Error_InvalidInput._() : super._();
}
/// @nodoc
abstract class _$$Error_NetworkImplCopyWith<$Res> {
factory _$$Error_NetworkImplCopyWith(
_$Error_NetworkImpl value,
$Res Function(_$Error_NetworkImpl) then,
) = __$$Error_NetworkImplCopyWithImpl<$Res>;
@useResult
$Res call({String field0});
}
/// @nodoc
class __$$Error_NetworkImplCopyWithImpl<$Res>
extends _$ErrorCopyWithImpl<$Res, _$Error_NetworkImpl>
implements _$$Error_NetworkImplCopyWith<$Res> {
__$$Error_NetworkImplCopyWithImpl(
_$Error_NetworkImpl _value,
$Res Function(_$Error_NetworkImpl) _then,
) : super(_value, _then);
/// Create a copy of Error
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({Object? field0 = null}) {
return _then(
_$Error_NetworkImpl(
null == field0
? _value.field0
: field0 // ignore: cast_nullable_to_non_nullable
as String,
),
);
}
}
/// @nodoc
class _$Error_NetworkImpl extends Error_Network {
const _$Error_NetworkImpl(this.field0) : super._();
@override
final String field0;
@override
String toString() {
return 'Error.network(field0: $field0)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$Error_NetworkImpl &&
(identical(other.field0, field0) || other.field0 == field0));
}
@override
int get hashCode => Object.hash(runtimeType, field0);
/// Create a copy of Error
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$Error_NetworkImplCopyWith<_$Error_NetworkImpl> get copyWith =>
__$$Error_NetworkImplCopyWithImpl<_$Error_NetworkImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String field0) cdk,
required TResult Function(String field0) database,
required TResult Function() invalidInput,
required TResult Function(String field0) network,
required TResult Function(String field0) ur,
}) {
return network(field0);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String field0)? cdk,
TResult? Function(String field0)? database,
TResult? Function()? invalidInput,
TResult? Function(String field0)? network,
TResult? Function(String field0)? ur,
}) {
return network?.call(field0);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String field0)? cdk,
TResult Function(String field0)? database,
TResult Function()? invalidInput,
TResult Function(String field0)? network,
TResult Function(String field0)? ur,
required TResult orElse(),
}) {
if (network != null) {
return network(field0);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(Error_Cdk value) cdk,
required TResult Function(Error_Database value) database,
required TResult Function(Error_InvalidInput value) invalidInput,
required TResult Function(Error_Network value) network,
required TResult Function(Error_Ur value) ur,
}) {
return network(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(Error_Cdk value)? cdk,
TResult? Function(Error_Database value)? database,
TResult? Function(Error_InvalidInput value)? invalidInput,
TResult? Function(Error_Network value)? network,
TResult? Function(Error_Ur value)? ur,
}) {
return network?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(Error_Cdk value)? cdk,
TResult Function(Error_Database value)? database,
TResult Function(Error_InvalidInput value)? invalidInput,
TResult Function(Error_Network value)? network,
TResult Function(Error_Ur value)? ur,
required TResult orElse(),
}) {
if (network != null) {
return network(this);
}
return orElse();
}
}
abstract class Error_Network extends Error {
const factory Error_Network(final String field0) = _$Error_NetworkImpl;
const Error_Network._() : super._();
String get field0;
/// Create a copy of Error
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$Error_NetworkImplCopyWith<_$Error_NetworkImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$Error_UrImplCopyWith<$Res> {
factory _$$Error_UrImplCopyWith(
_$Error_UrImpl value,
$Res Function(_$Error_UrImpl) then,
) = __$$Error_UrImplCopyWithImpl<$Res>;
@useResult
$Res call({String field0});
}
/// @nodoc
class __$$Error_UrImplCopyWithImpl<$Res>
extends _$ErrorCopyWithImpl<$Res, _$Error_UrImpl>
implements _$$Error_UrImplCopyWith<$Res> {
__$$Error_UrImplCopyWithImpl(
_$Error_UrImpl _value,
$Res Function(_$Error_UrImpl) _then,
) : super(_value, _then);
/// Create a copy of Error
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({Object? field0 = null}) {
return _then(
_$Error_UrImpl(
null == field0
? _value.field0
: field0 // ignore: cast_nullable_to_non_nullable
as String,
),
);
}
}
/// @nodoc
class _$Error_UrImpl extends Error_Ur {
const _$Error_UrImpl(this.field0) : super._();
@override
final String field0;
@override
String toString() {
return 'Error.ur(field0: $field0)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$Error_UrImpl &&
(identical(other.field0, field0) || other.field0 == field0));
}
@override
int get hashCode => Object.hash(runtimeType, field0);
/// Create a copy of Error
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$Error_UrImplCopyWith<_$Error_UrImpl> get copyWith =>
__$$Error_UrImplCopyWithImpl<_$Error_UrImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String field0) cdk,
required TResult Function(String field0) database,
required TResult Function() invalidInput,
required TResult Function(String field0) network,
required TResult Function(String field0) ur,
}) {
return ur(field0);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String field0)? cdk,
TResult? Function(String field0)? database,
TResult? Function()? invalidInput,
TResult? Function(String field0)? network,
TResult? Function(String field0)? ur,
}) {
return ur?.call(field0);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String field0)? cdk,
TResult Function(String field0)? database,
TResult Function()? invalidInput,
TResult Function(String field0)? network,
TResult Function(String field0)? ur,
required TResult orElse(),
}) {
if (ur != null) {
return ur(field0);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(Error_Cdk value) cdk,
required TResult Function(Error_Database value) database,
required TResult Function(Error_InvalidInput value) invalidInput,
required TResult Function(Error_Network value) network,
required TResult Function(Error_Ur value) ur,
}) {
return ur(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(Error_Cdk value)? cdk,
TResult? Function(Error_Database value)? database,
TResult? Function(Error_InvalidInput value)? invalidInput,
TResult? Function(Error_Network value)? network,
TResult? Function(Error_Ur value)? ur,
}) {
return ur?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(Error_Cdk value)? cdk,
TResult Function(Error_Database value)? database,
TResult Function(Error_InvalidInput value)? invalidInput,
TResult Function(Error_Network value)? network,
TResult Function(Error_Ur value)? ur,
required TResult orElse(),
}) {
if (ur != null) {
return ur(this);
}
return orElse();
}
}
abstract class Error_Ur extends Error {
const factory Error_Ur(final String field0) = _$Error_UrImpl;
const Error_Ur._() : super._();
String get field0;
/// Create a copy of Error
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$Error_UrImplCopyWith<_$Error_UrImpl> get copyWith =>
throw _privateConstructorUsedError;
}
+20
View File
@@ -0,0 +1,20 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import '../frb_generated.dart';
import 'error.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
/// Generate a new 12-word BIP39 mnemonic
String generateMnemonic() =>
RustLib.instance.api.crateApiKeysGenerateMnemonic();
/// Convert a mnemonic to a 64-byte seed
Uint8List mnemonicToSeed({required String mnemonic}) =>
RustLib.instance.api.crateApiKeysMnemonicToSeed(mnemonic: mnemonic);
/// Derive the public key (hex) from a secret key (hex)
String getPubKey({required String secret}) =>
RustLib.instance.api.crateApiKeysGetPubKey(secret: secret);
+309
View File
@@ -0,0 +1,309 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import '../frb_generated.dart';
import 'error.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`
/// Fetch full mint info from a mint URL
Future<MintInfo> getMintInfo({required String mintUrl}) =>
RustLib.instance.api.crateApiMintInfoGetMintInfo(mintUrl: mintUrl);
/// Ping a mint to check if it's reachable (replaces Dart HTTP call)
Future<bool> pingMint({required String mintUrl}) =>
RustLib.instance.api.crateApiMintInfoPingMint(mintUrl: mintUrl);
/// Fetch keysets from a mint (replaces Dart HTTP call)
Future<List<KeysetInfo>> fetchKeysets({required String mintUrl}) =>
RustLib.instance.api.crateApiMintInfoFetchKeysets(mintUrl: mintUrl);
class ContactInfo {
final String method;
final String info;
const ContactInfo({required this.method, required this.info});
@override
int get hashCode => method.hashCode ^ info.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ContactInfo &&
runtimeType == other.runtimeType &&
method == other.method &&
info == other.info;
}
/// Keyset info returned by fetch_keysets
class KeysetInfo {
final String id;
final String unit;
final bool active;
const KeysetInfo({
required this.id,
required this.unit,
required this.active,
});
@override
int get hashCode => id.hashCode ^ unit.hashCode ^ active.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is KeysetInfo &&
runtimeType == other.runtimeType &&
id == other.id &&
unit == other.unit &&
active == other.active;
}
class MeltMethodSettings {
final String method;
final String unit;
final BigInt? minAmount;
final BigInt? maxAmount;
const MeltMethodSettings({
required this.method,
required this.unit,
this.minAmount,
this.maxAmount,
});
@override
int get hashCode =>
method.hashCode ^ unit.hashCode ^ minAmount.hashCode ^ maxAmount.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is MeltMethodSettings &&
runtimeType == other.runtimeType &&
method == other.method &&
unit == other.unit &&
minAmount == other.minAmount &&
maxAmount == other.maxAmount;
}
class MintInfo {
final String? name;
final String? pubkey;
final MintVersion? version;
final String? description;
final String? descriptionLong;
final List<ContactInfo>? contact;
final Nuts nuts;
final String? iconUrl;
final List<String>? urls;
final String? motd;
final BigInt? time;
final String? tosUrl;
const MintInfo({
this.name,
this.pubkey,
this.version,
this.description,
this.descriptionLong,
this.contact,
required this.nuts,
this.iconUrl,
this.urls,
this.motd,
this.time,
this.tosUrl,
});
@override
int get hashCode =>
name.hashCode ^
pubkey.hashCode ^
version.hashCode ^
description.hashCode ^
descriptionLong.hashCode ^
contact.hashCode ^
nuts.hashCode ^
iconUrl.hashCode ^
urls.hashCode ^
motd.hashCode ^
time.hashCode ^
tosUrl.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is MintInfo &&
runtimeType == other.runtimeType &&
name == other.name &&
pubkey == other.pubkey &&
version == other.version &&
description == other.description &&
descriptionLong == other.descriptionLong &&
contact == other.contact &&
nuts == other.nuts &&
iconUrl == other.iconUrl &&
urls == other.urls &&
motd == other.motd &&
time == other.time &&
tosUrl == other.tosUrl;
}
class MintMethodSettings {
final String method;
final String unit;
final BigInt? minAmount;
final BigInt? maxAmount;
const MintMethodSettings({
required this.method,
required this.unit,
this.minAmount,
this.maxAmount,
});
@override
int get hashCode =>
method.hashCode ^ unit.hashCode ^ minAmount.hashCode ^ maxAmount.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is MintMethodSettings &&
runtimeType == other.runtimeType &&
method == other.method &&
unit == other.unit &&
minAmount == other.minAmount &&
maxAmount == other.maxAmount;
}
class MintVersion {
final String name;
final String version;
const MintVersion({required this.name, required this.version});
@override
int get hashCode => name.hashCode ^ version.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is MintVersion &&
runtimeType == other.runtimeType &&
name == other.name &&
version == other.version;
}
class Nut04Settings {
final List<MintMethodSettings> methods;
final bool disabled;
const Nut04Settings({required this.methods, required this.disabled});
@override
int get hashCode => methods.hashCode ^ disabled.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Nut04Settings &&
runtimeType == other.runtimeType &&
methods == other.methods &&
disabled == other.disabled;
}
class Nut05Settings {
final List<MeltMethodSettings> methods;
final bool disabled;
const Nut05Settings({required this.methods, required this.disabled});
@override
int get hashCode => methods.hashCode ^ disabled.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Nut05Settings &&
runtimeType == other.runtimeType &&
methods == other.methods &&
disabled == other.disabled;
}
class Nuts {
final Nut04Settings nut04;
final Nut05Settings nut05;
final SupportedSettings nut07;
final SupportedSettings nut08;
final SupportedSettings nut09;
final SupportedSettings nut10;
final SupportedSettings nut11;
final SupportedSettings nut12;
final SupportedSettings nut14;
final SupportedSettings nut20;
const Nuts({
required this.nut04,
required this.nut05,
required this.nut07,
required this.nut08,
required this.nut09,
required this.nut10,
required this.nut11,
required this.nut12,
required this.nut14,
required this.nut20,
});
@override
int get hashCode =>
nut04.hashCode ^
nut05.hashCode ^
nut07.hashCode ^
nut08.hashCode ^
nut09.hashCode ^
nut10.hashCode ^
nut11.hashCode ^
nut12.hashCode ^
nut14.hashCode ^
nut20.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Nuts &&
runtimeType == other.runtimeType &&
nut04 == other.nut04 &&
nut05 == other.nut05 &&
nut07 == other.nut07 &&
nut08 == other.nut08 &&
nut09 == other.nut09 &&
nut10 == other.nut10 &&
nut11 == other.nut11 &&
nut12 == other.nut12 &&
nut14 == other.nut14 &&
nut20 == other.nut20;
}
class SupportedSettings {
final bool supported;
const SupportedSettings({required this.supported});
@override
int get hashCode => supported.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is SupportedSettings &&
runtimeType == other.runtimeType &&
supported == other.supported;
}
+62
View File
@@ -0,0 +1,62 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import '../frb_generated.dart';
import 'error.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
// These functions are ignored because they are not marked as `pub`: `proofs`
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `fmt`, `from_str`, `try_from`, `try_into`, `try_into`
List<String> encodeQrToken({required Token token, BigInt? maxFragmentLength}) =>
RustLib.instance.api.crateApiTokenEncodeQrToken(
token: token,
maxFragmentLength: maxFragmentLength,
);
// Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<TokenDecoder>>
abstract class TokenDecoder implements RustOpaqueInterface {
bool isComplete();
factory TokenDecoder() => RustLib.instance.api.crateApiTokenTokenDecoderNew();
void receive({required String input});
Token? value();
}
class Token {
final String encoded;
final Uint8List? raw;
final BigInt amount;
final String mintUrl;
const Token({
required this.encoded,
this.raw,
required this.amount,
required this.mintUrl,
});
static Token fromRawBytes({required List<int> raw}) =>
RustLib.instance.api.crateApiTokenTokenFromRawBytes(raw: raw);
static Token parse({required String encoded}) =>
RustLib.instance.api.crateApiTokenTokenParse(encoded: encoded);
@override
int get hashCode =>
encoded.hashCode ^ raw.hashCode ^ amount.hashCode ^ mintUrl.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Token &&
runtimeType == other.runtimeType &&
encoded == other.encoded &&
raw == other.raw &&
amount == other.amount &&
mintUrl == other.mintUrl;
}
+292
View File
@@ -0,0 +1,292 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import '../frb_generated.dart';
import 'error.dart';
import 'mint_info.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
import 'token.dart';
// These functions are ignored because they are not marked as `pub`: `mint_url`, `unit`, `update_balance_streams`
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `clone`, `clone`, `clone`, `clone`, `cmp`, `eq`, `eq`, `eq`, `fmt`, `fmt`, `from`, `from`, `from`, `from`, `from`, `from`, `into`, `partial_cmp`, `try_into`, `try_into`
// Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<PreparedSend>>
abstract class PreparedSend implements RustOpaqueInterface {
BigInt get amount;
BigInt get fee;
BigInt get sendFee;
BigInt get swapFee;
set amount(BigInt amount);
set fee(BigInt fee);
set sendFee(BigInt sendFee);
set swapFee(BigInt swapFee);
}
// Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<Wallet>>
abstract class Wallet implements RustOpaqueInterface {
String get mintUrl;
String get unit;
set mintUrl(String mintUrl);
set unit(String unit);
Future<BigInt> balance();
Future<void> cancelSend({required PreparedSend send});
Future<void> checkAllMintQuotes();
Future<void> checkPendingTransactions();
Future<void> finalizePendingMelts();
Future<MintInfo?> getMint();
Future<bool> isTokenSpent({required Token token});
Future<List<Transaction>> listTransactions({TransactionDirection? direction});
Future<BigInt> melt({required MeltQuote quote});
Future<MeltQuote> meltQuote({required String request});
Stream<MintQuote> mint({required BigInt amount, String? description});
factory Wallet({
required String mintUrl,
required String unit,
required String mnemonic,
BigInt? targetProofCount,
required WalletDatabase db,
}) => RustLib.instance.api.crateApiWalletWalletNew(
mintUrl: mintUrl,
unit: unit,
mnemonic: mnemonic,
targetProofCount: targetProofCount,
db: db,
);
Future<PreparedSend> prepareSend({required BigInt amount, SendOptions? opts});
Future<BigInt> receive({required Token token, ReceiveOptions? opts});
Future<void> recoverIncompleteSagas();
Future<void> restore();
Future<Token> send({
required PreparedSend send,
String? memo,
bool? includeMemo,
});
Stream<BigInt> streamBalance();
}
// Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<WalletDatabase>>
abstract class WalletDatabase implements RustOpaqueInterface {
String get path;
set path(String path);
static Future<WalletDatabase> newInstance({required String path}) =>
RustLib.instance.api.crateApiWalletWalletDatabaseNewInstance(path: path);
Future<void> removeMint({required String mintUrl});
}
class MeltQuote {
final String id;
final String request;
final BigInt amount;
final BigInt feeReserve;
final BigInt expiry;
const MeltQuote({
required this.id,
required this.request,
required this.amount,
required this.feeReserve,
required this.expiry,
});
@override
int get hashCode =>
id.hashCode ^
request.hashCode ^
amount.hashCode ^
feeReserve.hashCode ^
expiry.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is MeltQuote &&
runtimeType == other.runtimeType &&
id == other.id &&
request == other.request &&
amount == other.amount &&
feeReserve == other.feeReserve &&
expiry == other.expiry;
}
class MintQuote {
final String id;
final String request;
final BigInt? amount;
final BigInt? expiry;
final MintQuoteState state;
final Token? token;
final String? error;
const MintQuote({
required this.id,
required this.request,
this.amount,
this.expiry,
required this.state,
this.token,
this.error,
});
@override
int get hashCode =>
id.hashCode ^
request.hashCode ^
amount.hashCode ^
expiry.hashCode ^
state.hashCode ^
token.hashCode ^
error.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is MintQuote &&
runtimeType == other.runtimeType &&
id == other.id &&
request == other.request &&
amount == other.amount &&
expiry == other.expiry &&
state == other.state &&
token == other.token &&
error == other.error;
}
enum MintQuoteState { unpaid, paid, issued, error }
class ReceiveOptions {
final List<String>? signingKeys;
final List<String>? preimages;
const ReceiveOptions({this.signingKeys, this.preimages});
static Future<ReceiveOptions> default_() =>
RustLib.instance.api.crateApiWalletReceiveOptionsDefault();
@override
int get hashCode => signingKeys.hashCode ^ preimages.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ReceiveOptions &&
runtimeType == other.runtimeType &&
signingKeys == other.signingKeys &&
preimages == other.preimages;
}
class SendOptions {
final String? pubkey;
final bool? includeFee;
const SendOptions({this.pubkey, this.includeFee});
static Future<SendOptions> default_() =>
RustLib.instance.api.crateApiWalletSendOptionsDefault();
@override
int get hashCode => pubkey.hashCode ^ includeFee.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is SendOptions &&
runtimeType == other.runtimeType &&
pubkey == other.pubkey &&
includeFee == other.includeFee;
}
class Transaction {
final String id;
final String mintUrl;
final TransactionDirection direction;
final BigInt amount;
final BigInt fee;
final String unit;
final List<String> ys;
final BigInt timestamp;
final String? memo;
final Map<String, String> metadata;
final TransactionStatus status;
const Transaction({
required this.id,
required this.mintUrl,
required this.direction,
required this.amount,
required this.fee,
required this.unit,
required this.ys,
required this.timestamp,
this.memo,
required this.metadata,
required this.status,
});
@override
int get hashCode =>
id.hashCode ^
mintUrl.hashCode ^
direction.hashCode ^
amount.hashCode ^
fee.hashCode ^
unit.hashCode ^
ys.hashCode ^
timestamp.hashCode ^
memo.hashCode ^
metadata.hashCode ^
status.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Transaction &&
runtimeType == other.runtimeType &&
id == other.id &&
mintUrl == other.mintUrl &&
direction == other.direction &&
amount == other.amount &&
fee == other.fee &&
unit == other.unit &&
ys == other.ys &&
timestamp == other.timestamp &&
memo == other.memo &&
metadata == other.metadata &&
status == other.status;
}
enum TransactionDirection { incoming, outgoing }
enum TransactionStatus { pending, settled }
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+249 -1
View File
@@ -1,6 +1,22 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f
url: "https://pub.dev"
source: hosted
version: "85.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "974859dc0ff5f37bc4313244b3218c791810d03ab3470a579580279ba971a48d"
url: "https://pub.dev"
source: hosted
version: "7.7.1"
ansicolor:
dependency: transitive
description:
@@ -65,6 +81,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.2"
build:
dependency: transitive
description:
name: build
sha256: "51dc711996cbf609b90cbe5b335bbce83143875a9d58e4b5c6d3c4f684d3dda7"
url: "https://pub.dev"
source: hosted
version: "2.5.4"
build_cli_annotations:
dependency: transitive
description:
@@ -73,6 +97,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.0"
build_config:
dependency: transitive
description:
name: build_config
sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
url: "https://pub.dev"
source: hosted
version: "1.1.2"
build_daemon:
dependency: transitive
description:
name: build_daemon
sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957
url: "https://pub.dev"
source: hosted
version: "4.1.1"
build_resolvers:
dependency: transitive
description:
name: build_resolvers
sha256: ee4257b3f20c0c90e72ed2b57ad637f694ccba48839a821e87db762548c22a62
url: "https://pub.dev"
source: hosted
version: "2.5.4"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "382a4d649addbfb7ba71a3631df0ec6a45d5ab9b098638144faf27f02778eb53"
url: "https://pub.dev"
source: hosted
version: "2.5.4"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: "85fbbb1036d576d966332a3f5ce83f2ce66a40bea1a94ad2d5fc29a19a0d3792"
url: "https://pub.dev"
source: hosted
version: "9.1.2"
built_collection:
dependency: transitive
description:
name: built_collection
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
url: "https://pub.dev"
source: hosted
version: "5.1.1"
built_value:
dependency: transitive
description:
name: built_value
sha256: "0730c18c770d05636a8f945c32a4d7d81cb6e0f0148c8db4ad12e7748f7e49af"
url: "https://pub.dev"
source: hosted
version: "8.12.5"
cbor:
dependency: "direct main"
description:
@@ -122,6 +202,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.2"
code_builder:
dependency: transitive
description:
name: code_builder
sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d"
url: "https://pub.dev"
source: hosted
version: "4.11.1"
collection:
dependency: transitive
description:
@@ -170,6 +258,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.8"
dart_style:
dependency: transitive
description:
name: dart_style
sha256: "8a0e5fba27e8ee025d2ffb4ee820b4e6e2cf5e4246a6b1a477eb66866947e0bb"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
fake_async:
dependency: transitive
description:
@@ -302,14 +398,38 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
freezed:
dependency: "direct dev"
description:
name: freezed
sha256: "59a584c24b3acdc5250bb856d0d3e9c0b798ed14a4af1ddb7dc1c7b41df91c9c"
url: "https://pub.dev"
source: hosted
version: "2.5.8"
freezed_annotation:
dependency: transitive
dependency: "direct main"
description:
name: freezed_annotation
sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2
url: "https://pub.dev"
source: hosted
version: "2.4.4"
frontend_server_client:
dependency: transitive
description:
name: frontend_server_client
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
url: "https://pub.dev"
source: hosted
version: "4.0.0"
glob:
dependency: transitive
description:
name: glob
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
url: "https://pub.dev"
source: hosted
version: "2.1.3"
google_fonts:
dependency: "direct main"
description:
@@ -318,6 +438,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.3.0"
graphs:
dependency: transitive
description:
name: graphs
sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
hex:
dependency: transitive
description:
@@ -342,6 +470,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.6.0"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8
url: "https://pub.dev"
source: hosted
version: "3.2.2"
http_parser:
dependency: transitive
description:
@@ -374,6 +510,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.20.2"
io:
dependency: transitive
description:
name: io
sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b
url: "https://pub.dev"
source: hosted
version: "1.0.5"
js:
dependency: transitive
description:
@@ -422,6 +566,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.1.1"
logging:
dependency: transitive
description:
name: logging
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
url: "https://pub.dev"
source: hosted
version: "1.3.0"
lucide_icons:
dependency: "direct main"
description:
@@ -494,6 +646,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.1.1"
package_config:
dependency: transitive
description:
name: package_config
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
url: "https://pub.dev"
source: hosted
version: "2.2.0"
package_info_plus:
dependency: "direct main"
description:
@@ -598,6 +758,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.9.1"
pool:
dependency: transitive
description:
name: pool
sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d"
url: "https://pub.dev"
source: hosted
version: "1.5.2"
posix:
dependency: transitive
description:
@@ -614,6 +782,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.1.5+1"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
qr:
dependency: transitive
description:
@@ -702,11 +886,35 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shelf:
dependency: transitive
description:
name: shelf
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
url: "https://pub.dev"
source: hosted
version: "1.4.2"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
source_gen:
dependency: transitive
description:
name: source_gen
sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
source_span:
dependency: transitive
description:
@@ -787,6 +995,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
stream_transform:
dependency: transitive
description:
name: stream_transform
sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871
url: "https://pub.dev"
source: hosted
version: "2.1.1"
string_scanner:
dependency: transitive
description:
@@ -819,6 +1035,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.10"
timing:
dependency: transitive
description:
name: timing
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
typed_data:
dependency: transitive
description:
@@ -923,6 +1147,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "14.3.0"
watcher:
dependency: transitive
description:
name: watcher
sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635"
url: "https://pub.dev"
source: hosted
version: "1.2.1"
web:
dependency: transitive
description:
@@ -931,6 +1163,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.1"
web_socket:
dependency: transitive
description:
name: web_socket
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
url: "https://pub.dev"
source: hosted
version: "3.0.3"
win32:
dependency: transitive
description:
+3
View File
@@ -64,6 +64,7 @@ dependencies:
bip32: ^2.0.0
package_info_plus: ^9.0.0
flutter_rust_bridge: 2.11.1
freezed_annotation: ^2.4.4
dev_dependencies:
flutter_test:
@@ -71,6 +72,8 @@ dev_dependencies:
flutter_lints: ^5.0.0
flutter_launcher_icons: ^0.14.3
flutter_native_splash: ^2.4.4
freezed: ^2.5.8
build_runner: ^2.5.4
# App Icon configuration
flutter_launcher_icons:
+2
View File
@@ -951,11 +951,13 @@ dependencies = [
"cdk-common",
"cdk-sqlite",
"flutter_rust_bridge",
"getrandom 0.3.4",
"log",
"oslog",
"reqwest",
"serde_json",
"tokio",
"uuid",
]
[[package]]
+6
View File
@@ -28,6 +28,9 @@ bc-ur = { version = "0.12.0", default-features = false }
# Mnemonic
bip39 = { version = "2.1", default-features = false, features = ["std"] }
# Entropy
getrandom = { version = "0.3", default-features = false, features = ["std"] }
# Async runtime
tokio = { version = "1", default-features = false }
@@ -43,6 +46,9 @@ reqwest = { version = "0.12", default-features = false, features = [
# Serialización
serde_json = { version = "1.0", default-features = false, features = ["std"] }
# UUID (for PreparedSend operation_id)
uuid = { version = "1", default-features = false }
# Logging
log = { version = "0.4", default-features = false }
+55
View File
@@ -11,6 +11,8 @@ pub enum Error {
InvalidInput,
/// Error de red (HTTP, conexión)
Network(String),
/// Error de UR (QR multiframe)
Ur(String),
}
impl fmt::Display for Error {
@@ -20,18 +22,65 @@ impl fmt::Display for Error {
Error::Database(msg) => write!(f, "Database error: {msg}"),
Error::InvalidInput => write!(f, "Invalid input"),
Error::Network(msg) => write!(f, "Network error: {msg}"),
Error::Ur(msg) => write!(f, "UR error: {msg}"),
}
}
}
impl std::error::Error for Error {}
// --- cdk errors ---
impl From<cdk::error::Error> for Error {
fn from(e: cdk::error::Error) -> Self {
Error::Cdk(e.to_string())
}
}
impl From<cdk::amount::Error> for Error {
fn from(e: cdk::amount::Error) -> Self {
Error::Cdk(e.to_string())
}
}
impl From<cdk::cdk_database::Error> for Error {
fn from(e: cdk::cdk_database::Error) -> Self {
Error::Database(e.to_string())
}
}
impl From<cdk::mint_url::Error> for Error {
fn from(e: cdk::mint_url::Error) -> Self {
Error::Network(e.to_string())
}
}
impl From<cdk::nuts::nut00::Error> for Error {
fn from(e: cdk::nuts::nut00::Error) -> Self {
Error::Cdk(e.to_string())
}
}
impl From<cdk::nuts::nut01::Error> for Error {
fn from(e: cdk::nuts::nut01::Error) -> Self {
Error::Cdk(e.to_string())
}
}
impl From<cdk::nuts::nut11::Error> for Error {
fn from(e: cdk::nuts::nut11::Error) -> Self {
Error::Cdk(e.to_string())
}
}
// --- external errors ---
impl From<bc_ur::Error> for Error {
fn from(e: bc_ur::Error) -> Self {
Error::Ur(e.to_string())
}
}
impl From<reqwest::Error> for Error {
fn from(e: reqwest::Error) -> Self {
Error::Network(e.to_string())
@@ -43,3 +92,9 @@ impl From<serde_json::Error> for Error {
Error::Cdk(e.to_string())
}
}
impl From<std::string::FromUtf8Error> for Error {
fn from(e: std::string::FromUtf8Error) -> Self {
Error::Cdk(e.to_string())
}
}
+30
View File
@@ -0,0 +1,30 @@
use std::str::FromStr;
use bip39::Mnemonic;
use cdk_common::SecretKey;
use flutter_rust_bridge::frb;
use super::error::Error;
/// Generate a new 12-word BIP39 mnemonic
#[frb(sync)]
pub fn generate_mnemonic() -> String {
let mut entropy = [0u8; 16]; // 128 bits → 12 words
getrandom::fill(&mut entropy).expect("Failed to generate entropy");
Mnemonic::from_entropy(&entropy).unwrap().to_string()
}
/// Convert a mnemonic to a 64-byte seed
#[frb(sync)]
pub fn mnemonic_to_seed(mnemonic: String) -> Result<Vec<u8>, Error> {
let mnemonic = Mnemonic::parse(&mnemonic).map_err(|_| Error::InvalidInput)?;
Ok(mnemonic.to_seed("").to_vec())
}
/// Derive the public key (hex) from a secret key (hex)
#[frb(sync)]
pub fn get_pub_key(secret: String) -> Result<String, Error> {
let secret = SecretKey::from_str(&secret)?;
let pub_key = secret.public_key();
Ok(pub_key.to_string())
}
+243
View File
@@ -0,0 +1,243 @@
use std::str::FromStr;
use cdk::{
nuts::{nut04, nut05, nut06},
wallet::{HttpClient, MintConnector},
};
use cdk_common::{
mint_url::MintUrl, ContactInfo as CdkContactInfo, MintInfo as CdkMintInfo,
MintVersion as CdkMintVersion, Nuts as CdkNuts,
};
use super::error::Error;
// === MintInfo structs ===
pub struct MintInfo {
pub name: Option<String>,
pub pubkey: Option<String>,
pub version: Option<MintVersion>,
pub description: Option<String>,
pub description_long: Option<String>,
pub contact: Option<Vec<ContactInfo>>,
pub nuts: Nuts,
pub icon_url: Option<String>,
pub urls: Option<Vec<String>>,
pub motd: Option<String>,
pub time: Option<u64>,
pub tos_url: Option<String>,
}
impl From<CdkMintInfo> for MintInfo {
fn from(value: CdkMintInfo) -> Self {
Self {
name: value.name,
pubkey: value.pubkey.map(|p| p.to_string()),
version: value.version.map(|v| v.into()),
description: value.description,
description_long: value.description_long,
contact: value
.contact
.map(|c| c.into_iter().map(|c| c.into()).collect()),
nuts: value.nuts.into(),
icon_url: value.icon_url,
urls: value.urls,
motd: value.motd,
time: value.time,
tos_url: value.tos_url,
}
}
}
pub struct MintVersion {
pub name: String,
pub version: String,
}
impl From<CdkMintVersion> for MintVersion {
fn from(value: CdkMintVersion) -> Self {
Self {
name: value.name,
version: value.version,
}
}
}
pub struct ContactInfo {
pub method: String,
pub info: String,
}
impl From<CdkContactInfo> for ContactInfo {
fn from(c: CdkContactInfo) -> Self {
Self {
method: c.method,
info: c.info,
}
}
}
// === NUT settings ===
pub struct Nuts {
pub nut04: Nut04Settings,
pub nut05: Nut05Settings,
pub nut07: SupportedSettings,
pub nut08: SupportedSettings,
pub nut09: SupportedSettings,
pub nut10: SupportedSettings,
pub nut11: SupportedSettings,
pub nut12: SupportedSettings,
pub nut14: SupportedSettings,
pub nut20: SupportedSettings,
}
impl From<CdkNuts> for Nuts {
fn from(value: CdkNuts) -> Self {
Self {
nut04: value.nut04.into(),
nut05: value.nut05.into(),
nut07: value.nut07.into(),
nut08: value.nut08.into(),
nut09: value.nut09.into(),
nut10: value.nut10.into(),
nut11: value.nut11.into(),
nut12: value.nut12.into(),
nut14: value.nut14.into(),
nut20: value.nut20.into(),
}
}
}
pub struct SupportedSettings {
pub supported: bool,
}
impl From<nut06::SupportedSettings> for SupportedSettings {
fn from(value: nut06::SupportedSettings) -> Self {
Self {
supported: value.supported,
}
}
}
pub struct Nut04Settings {
pub methods: Vec<MintMethodSettings>,
pub disabled: bool,
}
impl From<nut04::Settings> for Nut04Settings {
fn from(value: nut04::Settings) -> Self {
Self {
methods: value.methods.into_iter().map(|m| m.into()).collect(),
disabled: value.disabled,
}
}
}
pub struct MintMethodSettings {
pub method: String,
pub unit: String,
pub min_amount: Option<u64>,
pub max_amount: Option<u64>,
}
impl From<nut04::MintMethodSettings> for MintMethodSettings {
fn from(value: nut04::MintMethodSettings) -> Self {
Self {
method: value.method.to_string(),
unit: value.unit.to_string(),
min_amount: value.min_amount.map(|v| v.into()),
max_amount: value.max_amount.map(|v| v.into()),
}
}
}
pub struct Nut05Settings {
pub methods: Vec<MeltMethodSettings>,
pub disabled: bool,
}
impl From<nut05::Settings> for Nut05Settings {
fn from(value: nut05::Settings) -> Self {
Self {
methods: value.methods.into_iter().map(|m| m.into()).collect(),
disabled: value.disabled,
}
}
}
pub struct MeltMethodSettings {
pub method: String,
pub unit: String,
pub min_amount: Option<u64>,
pub max_amount: Option<u64>,
}
impl From<nut05::MeltMethodSettings> for MeltMethodSettings {
fn from(value: nut05::MeltMethodSettings) -> Self {
Self {
method: value.method.to_string(),
unit: value.unit.to_string(),
min_amount: value.min_amount.map(|v| v.into()),
max_amount: value.max_amount.map(|v| v.into()),
}
}
}
// === API functions ===
/// Fetch full mint info from a mint URL
pub async fn get_mint_info(mint_url: &str) -> Result<MintInfo, Error> {
let mint_url = MintUrl::from_str(mint_url)?;
let client = HttpClient::new(mint_url, None);
Ok(client.get_mint_info().await?.into())
}
/// Ping a mint to check if it's reachable (replaces Dart HTTP call)
pub async fn ping_mint(mint_url: &str) -> Result<bool, Error> {
let url = format!("{}/v1/info", mint_url.trim_end_matches('/'));
let resp = reqwest::Client::new()
.get(&url)
.timeout(std::time::Duration::from_secs(3))
.send()
.await;
match resp {
Ok(r) => Ok(r.status().is_success()),
Err(_) => Ok(false),
}
}
/// Keyset info returned by fetch_keysets
pub struct KeysetInfo {
pub id: String,
pub unit: String,
pub active: bool,
}
/// Fetch keysets from a mint (replaces Dart HTTP call)
pub async fn fetch_keysets(mint_url: &str) -> Result<Vec<KeysetInfo>, Error> {
let url = format!("{}/v1/keysets", mint_url.trim_end_matches('/'));
let resp = reqwest::Client::new()
.get(&url)
.timeout(std::time::Duration::from_secs(5))
.send()
.await?
.text()
.await?;
let data: serde_json::Value = serde_json::from_str(&resp)?;
let keysets = data["keysets"]
.as_array()
.ok_or(Error::InvalidInput)?
.iter()
.filter_map(|ks| {
Some(KeysetInfo {
id: ks["id"].as_str()?.to_string(),
unit: ks["unit"].as_str()?.to_string(),
active: ks["active"].as_bool().unwrap_or(false),
})
})
.collect();
Ok(keysets)
}
+4
View File
@@ -1 +1,5 @@
pub mod error;
pub mod keys;
pub mod mint_info;
pub mod token;
pub mod wallet;
+151
View File
@@ -0,0 +1,151 @@
use std::{
str::FromStr,
sync::{Arc, RwLock},
};
use bc_ur::{prelude::CBOR, MultipartDecoder, MultipartEncoder, UR};
use cdk::nuts::Token as CdkToken;
use cdk_common::{KeySetInfo, Proofs};
use flutter_rust_bridge::frb;
use super::error::Error;
pub struct Token {
pub encoded: String,
pub raw: Option<Vec<u8>>,
pub amount: u64,
pub mint_url: String,
}
impl Token {
#[frb(sync)]
pub fn parse(encoded: &str) -> Result<Self, Error> {
let encoded = encoded.strip_prefix("cashu:").unwrap_or(encoded);
let token = CdkToken::from_str(encoded)?;
Token::try_from(token)
}
#[frb(sync)]
pub fn from_raw_bytes(raw: Vec<u8>) -> Result<Self, Error> {
let token = CdkToken::try_from(&raw)?;
Token::try_from(token)
}
pub(crate) fn proofs(&self, mint_keysets: &[KeySetInfo]) -> Result<Proofs, Error> {
let token: CdkToken = self.try_into()?;
Ok(token.proofs(mint_keysets)?)
}
}
impl std::fmt::Display for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.encoded)
}
}
impl FromStr for Token {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Error> {
Token::parse(s)
}
}
impl TryFrom<CdkToken> for Token {
type Error = Error;
fn try_from(token: CdkToken) -> Result<Self, Error> {
match token {
CdkToken::TokenV3(token_v3) => Ok(Token {
encoded: token_v3.to_string(),
raw: None,
amount: token_v3.value()?.into(),
mint_url: token_v3.mint_urls().first().unwrap().to_string(),
}),
CdkToken::TokenV4(token_v4) => Ok(Token {
encoded: token_v4.to_string(),
raw: Some(token_v4.to_raw_bytes()?),
amount: token_v4.value()?.into(),
mint_url: token_v4.mint_url.to_string(),
}),
}
}
}
impl TryInto<CdkToken> for Token {
type Error = Error;
fn try_into(self) -> Result<CdkToken, Error> {
Ok(CdkToken::from_str(&self.encoded)?)
}
}
impl TryInto<CdkToken> for &Token {
type Error = Error;
fn try_into(self) -> Result<CdkToken, Error> {
Ok(CdkToken::from_str(&self.encoded)?)
}
}
// === QR Multiframe (bc-ur) ===
pub struct TokenDecoder {
decoder: Arc<RwLock<MultipartDecoder>>,
}
impl TokenDecoder {
#[frb(sync)]
pub fn new() -> Self {
Self {
decoder: Arc::new(RwLock::new(MultipartDecoder::default())),
}
}
#[frb(sync)]
pub fn is_complete(&self) -> bool {
let decoder = self.decoder.read().expect("Lock poisoned");
decoder.is_complete()
}
#[frb(sync)]
pub fn receive(&self, input: String) -> Result<(), Error> {
let mut decoder = self.decoder.write().expect("Lock poisoned");
decoder.receive(&input)?;
Ok(())
}
#[frb(sync)]
pub fn value(&self) -> Result<Option<Token>, Error> {
let decoder = self.decoder.read().expect("Lock poisoned");
let ur = decoder.message()?;
match ur {
Some(ur) => {
let bytes = ur
.cbor()
.try_byte_string()
.map_err(|e| Error::Ur(format!("Failed to decode CBOR byte string: {}", e)))?;
let utf8_encoded = String::from_utf8(bytes)?;
let token = Token::from_str(&utf8_encoded)?;
Ok(Some(token))
}
None => Ok(None),
}
}
}
#[frb(sync)]
pub fn encode_qr_token(
token: &Token,
max_fragment_length: Option<usize>,
) -> Result<Vec<String>, Error> {
let ur = UR::new("", CBOR::to_byte_string(&token.encoded))?;
let mut encoder = MultipartEncoder::new(&ur, max_fragment_length.unwrap_or(150))?;
let mut parts = Vec::new();
for _ in 0..encoder.parts_count() {
if let Ok(part) = encoder.next_part() {
parts.push(part);
}
}
Ok(parts)
}
+649
View File
@@ -0,0 +1,649 @@
use std::{collections::HashMap, str::FromStr, sync::Arc, time::Duration};
use bip39::Mnemonic;
use cdk::{
amount::{Amount, SplitTarget},
cdk_database::WalletDatabase as _,
mint_url::MintUrl,
nuts::{
nut00::ProofsMethods, CurrencyUnit, MintQuoteState as CdkMintQuoteState, PaymentMethod,
PublicKey, SecretKey, SpendingConditions, State as ProofState, Token as CdkToken,
},
wallet::{
MeltQuote as CdkMeltQuote, MintQuote as CdkMintQuote, PreparedSend as CdkPreparedSend,
ReceiveOptions as CdkReceiveOptions, SendMemo, SendOptions as CdkSendOptions,
Wallet as CdkWallet,
},
};
use cdk_common::{
util::unix_time,
wallet::{
Transaction as CdkTransaction, TransactionDirection as CdkTransactionDirection,
},
};
use cdk_sqlite::WalletSqliteDatabase;
use flutter_rust_bridge::frb;
use log::info;
use tokio::{sync::broadcast, time::sleep};
use uuid::Uuid;
use crate::frb_generated::StreamSink;
use super::{
error::Error,
mint_info::MintInfo,
token::Token,
};
// ========================================================================
// Wallet
// ========================================================================
#[derive(Clone)]
pub struct Wallet {
pub mint_url: String,
pub unit: String,
balance_broadcast: broadcast::Sender<u64>,
inner: CdkWallet,
seed: [u8; 64],
}
impl Wallet {
#[frb(sync)]
pub fn new(
mint_url: String,
unit: String,
mnemonic: String,
target_proof_count: Option<usize>,
db: &WalletDatabase,
) -> Result<Self, Error> {
let mnemonic = Mnemonic::parse(&mnemonic).map_err(|_| Error::InvalidInput)?;
let seed: [u8; 64] = mnemonic.to_seed("").into();
let unit = CurrencyUnit::from_str(&unit).unwrap_or(CurrencyUnit::Custom(unit.to_string()));
Ok(Self {
mint_url: mint_url.clone(),
unit: unit.to_string(),
balance_broadcast: broadcast::channel(1).0,
inner: CdkWallet::new(
&mint_url,
unit,
db.inner.clone(),
seed,
target_proof_count,
)?,
seed,
})
}
// === Balance ===
pub async fn balance(&self) -> Result<u64, Error> {
Ok(self.inner.total_balance().await?.into())
}
pub async fn stream_balance(&self, sink: StreamSink<u64>) -> Result<(), Error> {
let mut receiver = self.balance_broadcast.subscribe();
let _ = sink.add(self.balance().await?);
flutter_rust_bridge::spawn(async move {
loop {
match receiver.recv().await {
Ok(balance) => {
if sink.add(balance).is_err() {
break;
}
}
Err(_) => break,
}
}
});
Ok(())
}
// === Receive ===
pub async fn receive(&self, token: Token, opts: Option<ReceiveOptions>) -> Result<u64, Error> {
let amount = self
.inner
.receive(&token.encoded, opts.unwrap_or_default().try_into()?)
.await?
.into();
self.update_balance_streams().await;
Ok(amount)
}
// === Send ===
pub async fn prepare_send(
&self,
amount: u64,
opts: Option<SendOptions>,
) -> Result<PreparedSend, Error> {
let prepared_send = self
.inner
.prepare_send(amount.into(), opts.unwrap_or_default().try_into()?)
.await?;
Ok(prepared_send.into())
}
pub async fn send(
&self,
send: PreparedSend,
memo: Option<String>,
include_memo: Option<bool>,
) -> Result<Token, Error> {
let send_memo = memo.map(|m| SendMemo {
memo: m,
include_memo: include_memo.unwrap_or_default(),
});
let token = self
.inner
.confirm_send(
send.operation_id,
send.cdk_amount,
send.cdk_options,
send.proofs_to_swap,
send.proofs_to_send,
send.cdk_swap_fee,
send.cdk_send_fee,
send_memo,
)
.await?
.to_string();
self.update_balance_streams().await;
Ok(Token::from_str(&token)?)
}
pub async fn cancel_send(&self, send: PreparedSend) -> Result<(), Error> {
self.inner
.cancel_send(
send.operation_id,
send.proofs_to_swap,
send.proofs_to_send,
)
.await?;
Ok(())
}
// === Lightning Deposit (NUT-04) ===
pub async fn mint(
&self,
amount: u64,
description: Option<String>,
sink: StreamSink<MintQuote>,
) -> Result<(), Error> {
let mint_url = self.mint_url()?;
let unit = self.unit();
let quote = self
.inner
.mint_quote(PaymentMethod::BOLT11, Some(amount.into()), description, None)
.await?;
let _ = sink.add(MintQuote::from(quote.clone()));
let _self = self.clone();
flutter_rust_bridge::spawn(async move {
loop {
sleep(Duration::from_secs(3)).await;
info!("Checking mint quote state for {}", quote.id);
match _self.inner.check_mint_quote_status(&quote.id).await {
Ok(state_res) => match state_res.state {
CdkMintQuoteState::Unpaid => {
if state_res.expiry < unix_time() {
let _ = sink.add(MintQuote {
id: quote.id,
request: quote.request,
amount: quote.amount.map(|a| a.into()),
expiry: Some(state_res.expiry),
state: MintQuoteState::Error,
token: None,
error: Some("Quote expired".to_string()),
});
break;
}
continue;
}
CdkMintQuoteState::Issued => {
break;
}
CdkMintQuoteState::Paid => {
let _ = sink.add(MintQuote {
id: quote.id.clone(),
request: quote.request.clone(),
amount: quote.amount.map(|a| a.into()),
expiry: Some(quote.expiry),
state: CdkMintQuoteState::Paid.into(),
token: None,
error: None,
});
match _self
.inner
.mint(&quote.id, SplitTarget::None, None)
.await
{
Ok(mint_proofs) => {
let mint_amount =
mint_proofs.total_amount().unwrap_or_default();
let _ = sink.add(MintQuote {
id: quote.id,
request: quote.request,
amount: Some(mint_amount.into()),
expiry: Some(quote.expiry),
state: CdkMintQuoteState::Issued.into(),
token: Token::try_from(CdkToken::new(
mint_url,
mint_proofs,
None,
unit,
))
.ok(),
error: None,
});
_self.update_balance_streams().await;
break;
}
Err(e) => {
let _ = sink.add(MintQuote {
id: quote.id,
request: quote.request,
amount: quote.amount.map(|a| a.into()),
expiry: Some(quote.expiry),
state: MintQuoteState::Error,
token: None,
error: Some(e.to_string()),
});
break;
}
}
}
},
Err(e) => {
let _ = sink.add(MintQuote {
id: quote.id,
request: quote.request,
amount: quote.amount.map(|a| a.into()),
expiry: Some(quote.expiry),
state: MintQuoteState::Error,
token: None,
error: Some(e.to_string()),
});
break;
}
}
}
});
Ok(())
}
pub async fn check_all_mint_quotes(&self) -> Result<(), Error> {
let minted = self.inner.mint_unissued_quotes().await?;
if minted > Amount::ZERO {
self.update_balance_streams().await;
}
Ok(())
}
// === Lightning Withdrawal (NUT-05) ===
pub async fn melt_quote(&self, request: String) -> Result<MeltQuote, Error> {
Ok(self
.inner
.melt_quote(PaymentMethod::BOLT11, request, None, None)
.await?
.into())
}
pub async fn melt(&self, quote: MeltQuote) -> Result<u64, Error> {
let melted = self
.inner
.prepare_melt(&quote.id, HashMap::new())
.await?
.confirm()
.await?;
self.update_balance_streams().await;
Ok(melted.total_amount().into())
}
// === Transactions ===
pub async fn list_transactions(
&self,
direction: Option<TransactionDirection>,
) -> Result<Vec<Transaction>, Error> {
let pending_ys = self
.inner
.get_pending_spent_proofs()
.await?
.into_iter()
.map(|p| p.y())
.collect::<Result<Vec<_>, _>>()?;
let cdk_txs = self
.inner
.list_transactions(direction.map(|d| d.into()))
.await?;
let mut txs = Vec::new();
for tx in cdk_txs {
let status = if pending_ys.iter().any(|y| tx.ys.contains(y)) {
TransactionStatus::Pending
} else {
TransactionStatus::Settled
};
let mut transaction: Transaction = tx.into();
transaction.status = status;
txs.push(transaction);
}
Ok(txs)
}
pub async fn check_pending_transactions(&self) -> Result<(), Error> {
self.inner.check_all_pending_proofs().await?;
self.update_balance_streams().await;
Ok(())
}
// === Recovery ===
pub async fn restore(&self) -> Result<(), Error> {
self.inner.restore().await?;
self.update_balance_streams().await;
Ok(())
}
pub async fn recover_incomplete_sagas(&self) -> Result<(), Error> {
self.inner.recover_incomplete_sagas().await?;
self.update_balance_streams().await;
Ok(())
}
pub async fn finalize_pending_melts(&self) -> Result<(), Error> {
self.inner.finalize_pending_melts().await?;
self.update_balance_streams().await;
Ok(())
}
// === Utility ===
pub async fn is_token_spent(&self, token: Token) -> Result<bool, Error> {
let token: CdkToken = token.try_into()?;
let mint_keysets = self.inner.get_mint_keysets().await?;
let proof_states = self
.inner
.check_proofs_spent(token.proofs(&mint_keysets)?)
.await?;
Ok(proof_states
.iter()
.any(|state| state.state == ProofState::Spent))
}
pub async fn get_mint(&self) -> Result<Option<MintInfo>, Error> {
Ok(self.inner.fetch_mint_info().await?.map(|info| info.into()))
}
// === Internal helpers ===
fn mint_url(&self) -> Result<MintUrl, Error> {
Ok(MintUrl::from_str(&self.mint_url)?)
}
fn unit(&self) -> CurrencyUnit {
CurrencyUnit::from_str(&self.unit).unwrap_or(CurrencyUnit::Custom(self.unit.clone()))
}
async fn update_balance_streams(&self) {
let balance = self
.inner
.total_balance()
.await
.unwrap_or(Amount::ZERO)
.into();
let _ = self.balance_broadcast.send(balance);
}
}
// ========================================================================
// Types
// ========================================================================
pub struct MintQuote {
pub id: String,
pub request: String,
pub amount: Option<u64>,
pub expiry: Option<u64>,
pub state: MintQuoteState,
pub token: Option<Token>,
pub error: Option<String>,
}
impl From<CdkMintQuote> for MintQuote {
fn from(quote: CdkMintQuote) -> Self {
Self {
id: quote.id,
request: quote.request,
amount: quote.amount.map(|a| a.into()),
expiry: Some(quote.expiry),
state: quote.state.into(),
token: None,
error: None,
}
}
}
pub enum MintQuoteState {
Unpaid,
Paid,
Issued,
Error,
}
impl From<CdkMintQuoteState> for MintQuoteState {
fn from(state: CdkMintQuoteState) -> Self {
match state {
CdkMintQuoteState::Unpaid => Self::Unpaid,
CdkMintQuoteState::Paid => Self::Paid,
CdkMintQuoteState::Issued => Self::Issued,
}
}
}
pub struct MeltQuote {
pub id: String,
pub request: String,
pub amount: u64,
pub fee_reserve: u64,
pub expiry: u64,
}
impl From<CdkMeltQuote> for MeltQuote {
fn from(quote: CdkMeltQuote) -> Self {
Self {
id: quote.id,
request: quote.request,
amount: quote.amount.into(),
fee_reserve: quote.fee_reserve.into(),
expiry: quote.expiry,
}
}
}
pub struct PreparedSend {
pub amount: u64,
pub swap_fee: u64,
pub send_fee: u64,
pub fee: u64,
operation_id: Uuid,
cdk_amount: Amount,
cdk_options: CdkSendOptions,
proofs_to_swap: cdk::nuts::Proofs,
proofs_to_send: cdk::nuts::Proofs,
cdk_swap_fee: Amount,
cdk_send_fee: Amount,
}
impl<'a> From<CdkPreparedSend<'a>> for PreparedSend {
fn from(ps: CdkPreparedSend<'a>) -> Self {
let amount = ps.amount();
let swap_fee = ps.swap_fee();
let send_fee = ps.send_fee();
let fee = ps.fee();
Self {
amount: amount.into(),
swap_fee: swap_fee.into(),
send_fee: send_fee.into(),
fee: fee.into(),
operation_id: ps.operation_id(),
cdk_amount: amount,
cdk_options: ps.options().clone(),
proofs_to_swap: ps.proofs_to_swap().to_vec(),
proofs_to_send: ps.proofs_to_send().to_vec(),
cdk_swap_fee: swap_fee,
cdk_send_fee: send_fee,
}
}
}
#[derive(Default)]
pub struct ReceiveOptions {
pub signing_keys: Option<Vec<String>>,
pub preimages: Option<Vec<String>>,
}
impl TryInto<CdkReceiveOptions> for ReceiveOptions {
type Error = Error;
fn try_into(self) -> Result<CdkReceiveOptions, Self::Error> {
let p2pk_signing_keys = self
.signing_keys
.unwrap_or_default()
.into_iter()
.map(|s| SecretKey::from_str(&s))
.collect::<Result<Vec<_>, _>>()?;
Ok(CdkReceiveOptions {
p2pk_signing_keys,
preimages: self.preimages.unwrap_or_default(),
..Default::default()
})
}
}
#[derive(Default)]
pub struct SendOptions {
pub pubkey: Option<String>,
pub include_fee: Option<bool>,
}
impl TryInto<CdkSendOptions> for SendOptions {
type Error = Error;
fn try_into(self) -> Result<CdkSendOptions, Self::Error> {
let pubkey = self.pubkey.map(|s| PublicKey::from_str(&s)).transpose()?;
Ok(CdkSendOptions {
conditions: pubkey.map(|pubkey| SpendingConditions::new_p2pk(pubkey, None)),
include_fee: self.include_fee.unwrap_or_default(),
..Default::default()
})
}
}
#[derive(PartialEq, Eq)]
pub struct Transaction {
pub id: String,
pub mint_url: String,
pub direction: TransactionDirection,
pub amount: u64,
pub fee: u64,
pub unit: String,
pub ys: Vec<String>,
pub timestamp: u64,
pub memo: Option<String>,
pub metadata: HashMap<String, String>,
pub status: TransactionStatus,
}
impl From<CdkTransaction> for Transaction {
fn from(tx: CdkTransaction) -> Self {
Self {
id: tx.id().to_string(),
mint_url: tx.mint_url.to_string(),
direction: tx.direction.into(),
amount: tx.amount.into(),
fee: tx.fee.into(),
unit: tx.unit.to_string(),
ys: tx.ys.iter().map(|y| y.to_string()).collect(),
timestamp: tx.timestamp,
memo: tx.memo,
metadata: tx.metadata,
status: TransactionStatus::Settled,
}
}
}
impl PartialOrd for Transaction {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Transaction {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.timestamp.cmp(&other.timestamp).reverse()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TransactionDirection {
Incoming,
Outgoing,
}
impl From<CdkTransactionDirection> for TransactionDirection {
fn from(direction: CdkTransactionDirection) -> Self {
match direction {
CdkTransactionDirection::Incoming => Self::Incoming,
CdkTransactionDirection::Outgoing => Self::Outgoing,
}
}
}
impl Into<CdkTransactionDirection> for TransactionDirection {
fn into(self) -> CdkTransactionDirection {
match self {
Self::Incoming => CdkTransactionDirection::Incoming,
Self::Outgoing => CdkTransactionDirection::Outgoing,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TransactionStatus {
Pending,
Settled,
}
// ========================================================================
// WalletDatabase
// ========================================================================
#[derive(Clone)]
pub struct WalletDatabase {
pub path: String,
inner: Arc<WalletSqliteDatabase>,
}
impl WalletDatabase {
pub async fn new_instance(path: String) -> Result<Self, Error> {
let inner = WalletSqliteDatabase::new(path.as_str()).await?;
Ok(Self {
inner: Arc::new(inner),
path,
})
}
pub async fn remove_mint(&self, mint_url: &str) -> Result<(), Error> {
let mint_url = MintUrl::from_str(mint_url)?;
self.inner.remove_mint(mint_url).await?;
Ok(())
}
}
+4533 -5
View File
File diff suppressed because it is too large Load Diff