implement epiccash optional ffi package import
What changed, and why it matters
This commit refactors how the Epic Cash cryptocurrency support is wired into the Stack Wallet app. Instead of importing the Epic Cash library directly everywhere, the code now uses a generated interface that can be turned on or off at build time. The visible changes are mostly code reorganization and formatting; there is no obvious new security vulnerability introduced, but the patch is incomplete because the generated implementation file is only a template and the real generated file is not shown.
Treat as a build-system/architecture change rather than a security fix. Review the generated `libepiccash_interface_impl.dart` and the code generator to ensure the optional OFF path does not accidentally leave stubbed methods reachable at runtime, and verify that exception mapping does not swallow security-relevant errors.
Security signals we found
Refactor of native/FFI dependency import to optional code-generated interface
Exception translation added for bad Epic HTTP addresses
Transaction type comparisons abstracted behind interface helpers
No new input validation, cryptographic, or network code visible
Generated implementation file referenced but not present in diff
Evidence from the diff
The change introduces an abstraction layer (libepiccash_interface.dart) and a code-generation template (EPIC_libepiccash_interface_impl.template.dart) so that the flutter_libepiccash FFI package becomes an optional dependency. Call sites in epiccash_wallet.dart, epiccash.dart, confirm_transaction_view.dart, and git_status.dart are updated to use the new libEpic facade and the wrapper types EpicTransaction/EpicMessage. Exception mapping is added (BadEpicHttpAddressException -> BadHttpAddressException) and transaction-type checks are encapsulated. The bulk of the diff is formatting noise from dart format.
Changed components
lib/wallets/wallet/impl/epiccash_wallet.dartlib/wallets/crypto_currency/coins/epiccash.dartlib/pages/send_view/confirm_transaction_view.dartlib/utilities/git_status.dartlib/wl_gen/interfaces/libepiccash_interface.darttool/wl_templates/EPIC_libepiccash_interface_impl.template.dartInspect captured patch +800 / −403
diff --git a/lib/pages/send_view/confirm_transaction_view.dart b/lib/pages/send_view/confirm_transaction_view.dart
index 93a6f05..5cb12e0 100644
--- a/lib/pages/send_view/confirm_transaction_view.dart
+++ b/lib/pages/send_view/confirm_transaction_view.dart
@@ -14,7 +14,6 @@ import 'dart:io';
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
-import 'package:flutter_libepiccash/lib.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
@@ -55,6 +54,7 @@ import '../../widgets/rounded_white_container.dart';
import '../../widgets/stack_dialog.dart';
import '../../widgets/stack_text_field.dart';
import '../../widgets/textfield_icon_button.dart';
+import '../../wl_gen/interfaces/libepiccash_interface.dart';
import '../pinpad_views/lock_screen_view.dart';
import '../wallet_view/wallet_view.dart';
import 'sub_widgets/mwc_slatepack_dialog.dart';
@@ -121,16 +121,15 @@ class _ConfirmTransactionViewState
// Create slatepack.
final slatepackResult = await wallet.createSlatepack(
amount: recipient.amount,
- recipientAddress:
- recipient.address.isNotEmpty ? recipient.address : null,
- message:
- onChainNoteController.text.isNotEmpty
- ? onChainNoteController.text
- : null,
- encrypt:
- recipient
- .address
- .isNotEmpty, // Encrypt if we have a recipient address.
+ recipientAddress: recipient.address.isNotEmpty
+ ? recipient.address
+ : null,
+ message: onChainNoteController.text.isNotEmpty
+ ? onChainNoteController.text
+ : null,
+ encrypt: recipient
+ .address
+ .isNotEmpty, // Encrypt if we have a recipient address.
);
if (!slatepackResult.success || slatepackResult.slatepack == null) {
@@ -142,8 +141,8 @@ class _ConfirmTransactionViewState
await showDialog<void>(
context: context,
barrierDismissible: false,
- builder:
- (context) => MwcSlatepackDialog(slatepackResult: slatepackResult),
+ builder: (context) =>
+ MwcSlatepackDialog(slatepackResult: slatepackResult),
);
// After slatepack dialog is closed, navigate back to wallet.
@@ -171,17 +170,16 @@ class _ConfirmTransactionViewState
await showDialog<void>(
context: context,
- builder:
- (context) => AlertDialog(
- title: const Text('Slatepack Creation Failed'),
- content: Text('Failed to create slatepack: $e'),
- actions: [
- TextButton(
- onPressed: () => Navigator.of(context).pop(),
- child: const Text('OK'),
- ),
- ],
+ builder: (context) => AlertDialog(
+ title: const Text('Slatepack Creation Failed'),
+ content: Text('Failed to create slatepack: $e'),
+ actions: [
+ TextButton(
+ onPressed: () => Navigator.of(context).pop(),
+ child: const Text('OK'),
),
+ ],
+ ),
);
}
}
@@ -245,10 +243,9 @@ class _ConfirmTransactionViewState
} else {
if (coin is Mimblewimblecoin) {
// Check if this is a slatepack transaction (manual exchange).
- final otherDataMap =
- widget.txData.otherData != null
- ? jsonDecode(widget.txData.otherData!)
- : null;
+ final otherDataMap = widget.txData.otherData != null
+ ? jsonDecode(widget.txData.otherData!)
+ : null;
final transactionMethod =
otherDataMap?['transactionMethod'] as String?;
@@ -321,7 +318,7 @@ class _ConfirmTransactionViewState
widget.onSuccessInsteadOfRouteOnSuccess!.call();
}
}
- } on BadEpicHttpAddressException catch (_) {
+ } on BadHttpAddressException catch (_) {
if (context.mounted) {
// pop building dialog
Navigator.of(context).pop();
@@ -394,10 +391,9 @@ class _ConfirmTransactionViewState
child: Text(
"Ok",
style: STextStyles.button(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.accentColorDark,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.accentColorDark,
),
),
onPressed: () {
@@ -492,81 +488,76 @@ class _ConfirmTransactionViewState
return ConditionalParent(
condition: !isDesktop,
- builder:
- (child) => Background(
- child: Scaffold(
- backgroundColor:
- Theme.of(context).extension<StackColors>()!.background,
- appBar: AppBar(
- backgroundColor:
- Theme.of(context).extension<StackColors>()!.background,
- leading: AppBarBackButton(
- onPressed: () async {
- // if (FocusScope.of(context).hasFocus) {
- // FocusScope.of(context).unfocus();
- // await Future<void>.delayed(Duration(milliseconds: 50));
- // }
- Navigator.of(context).pop();
- },
- ),
- title: Text(
- "Confirm transaction",
- style: STextStyles.navBarTitle(context),
- ),
- ),
- body: SafeArea(
- child: LayoutBuilder(
- builder: (builderContext, constraints) {
- return Padding(
- padding: const EdgeInsets.only(
- left: 12,
- top: 12,
- right: 12,
+ builder: (child) => Background(
+ child: Scaffold(
+ backgroundColor: Theme.of(
+ context,
+ ).extension<StackColors>()!.background,
+ appBar: AppBar(
+ backgroundColor: Theme.of(
+ context,
+ ).extension<StackColors>()!.background,
+ leading: AppBarBackButton(
+ onPressed: () async {
+ // if (FocusScope.of(context).hasFocus) {
+ // FocusScope.of(context).unfocus();
+ // await Future<void>.delayed(Duration(milliseconds: 50));
+ // }
+ Navigator.of(context).pop();
+ },
+ ),
+ title: Text(
+ "Confirm transaction",
+ style: STextStyles.navBarTitle(context),
+ ),
+ ),
+ body: SafeArea(
+ child: LayoutBuilder(
+ builder: (builderContext, constraints) {
+ return Padding(
+ padding: const EdgeInsets.only(left: 12, top: 12, right: 12),
+ child: SingleChildScrollView(
+ child: ConstrainedBox(
+ constraints: BoxConstraints(
+ minHeight: constraints.maxHeight - 24,
),
- child: SingleChildScrollView(
- child: ConstrainedBox(
- constraints: BoxConstraints(
- minHeight: constraints.maxHeight - 24,
- ),
- child: IntrinsicHeight(
- child: Padding(
- padding: const EdgeInsets.all(4),
- child: child,
- ),
- ),
+ child: IntrinsicHeight(
+ child: Padding(
+ padding: const EdgeInsets.all(4),
+ child: child,
),
),
- );
- },
- ),
- ),
+ ),
+ ),
+ );
+ },
),
),
+ ),
+ ),
child: ConditionalParent(
condition: isDesktop,
- builder:
- (child) => Column(
- crossAxisAlignment: CrossAxisAlignment.stretch,
- mainAxisSize: MainAxisSize.min,
+ builder: (child) => Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Row(
children: [
- Row(
- children: [
- AppBarBackButton(
- size: 40,
- iconSize: 24,
- onPressed:
- () =>
- Navigator.of(context, rootNavigator: true).pop(),
- ),
- Text(
- "Confirm $unit transaction",
- style: STextStyles.desktopH3(context),
- ),
- ],
+ AppBarBackButton(
+ size: 40,
+ iconSize: 24,
+ onPressed: () =>
+ Navigator.of(context, rootNavigator: true).pop(),
+ ),
+ Text(
+ "Confirm $unit transaction",
+ style: STextStyles.desktopH3(context),
),
- Flexible(child: SingleChildScrollView(child: child)),
],
),
+ Flexible(child: SingleChildScrollView(child: child)),
+ ],
+ ),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: isDesktop ? MainAxisSize.min : MainAxisSize.max,
@@ -592,7 +583,11 @@ class _ConfirmTransactionViewState
widget.isPaynymTransaction
? widget.txData.paynymAccountLite!.nymName
: widget.txData.recipients?.first.address ??
- widget.txData.sparkRecipients!.first.address,
+ widget
+ .txData
+ .sparkRecipients!
+ .first
+ .address,
style: STextStyles.itemSubtitle12(context),
),
],
@@ -609,12 +604,11 @@ class _ConfirmTransactionViewState
.watch(pAmountFormatter(coin))
.format(
amountWithoutChange,
- ethContract:
- widget.isTokenTx
- ? ref
- .watch(pCurrentTokenWallet)!
- .tokenContract
- : null,
+ ethContract: widget.isTokenTx
+ ? ref
+ .watch(pCurrentTokenWallet)!
+ .tokenContract
+ : null,
),
style: STextStyles.itemSubtitle12(context),
textAlign: TextAlign.right,
@@ -728,18 +722,18 @@ class _ConfirmTransactionViewState
),
child: RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
- borderColor:
- Theme.of(context).extension<StackColors>()!.background,
+ borderColor: Theme.of(
+ context,
+ ).extension<StackColors>()!.background,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
decoration: BoxDecoration(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.background,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.background,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(
Constants.size.circularBorderRadius,
@@ -799,37 +793,35 @@ class _ConfirmTransactionViewState
String fiatAmount = "N/A";
if (externalCalls) {
- final price =
- widget.isTokenTx
- ? ref
- .read(
- priceAnd24hChangeNotifierProvider,
- )
- .getTokenPrice(
- ref
- .read(pCurrentTokenWallet)!
- .tokenContract
- .address,
- )
- ?.value
- : ref
- .read(
- priceAnd24hChangeNotifierProvider,
- )
- .getPrice(coin)
- ?.value;
- if (price != null && price > Decimal.zero) {
- fiatAmount = (amountWithoutChange.decimal *
- price)
- .toAmount(fractionDigits: 2)
- .fiatString(
- locale:
+ final price = widget.isTokenTx
+ ? ref
+ .read(
+ priceAnd24hChangeNotifierProvider,
+ )
+ .getTokenPrice(
ref
+ .read(pCurrentTokenWallet)!
+ .tokenContract
+ .address,
+ )
+ ?.value
+ : ref
+ .read(
+ priceAnd24hChangeNotifierProvider,
+ )
+ .getPrice(coin)
+ ?.value;
+ if (price != null && price > Decimal.zero) {
+ fiatAmount =
+ (amountWithoutChange.decimal * price)
+ .toAmount(fractionDigits: 2)
+ .fiatString(
+ locale: ref
.read(
localeServiceChangeNotifierProvider,
)
.locale,
- );
+ );
}
}
@@ -840,23 +832,21 @@ class _ConfirmTransactionViewState
.watch(pAmountFormatter(coin))
.format(
amountWithoutChange,
- ethContract:
- widget.isTokenTx
- ? ref
- .watch(
- pCurrentTokenWallet,
- )!
- .tokenContract
- : null,
+ ethContract: widget.isTokenTx
+ ? ref
+ .watch(
+ pCurrentTokenWallet,
+ )!
+ .tokenContract
+ : null,
),
style:
STextStyles.desktopTextExtraExtraSmall(
context,
).copyWith(
- color:
- Theme.of(context)
- .extension<StackColors>()!
- .textDark,
+ color: Theme.of(context)
+ .extension<StackColors>()!
+ .textDark,
),
),
if (externalCalls)
@@ -884,10 +874,9 @@ class _ConfirmTransactionViewState
),
Container(
height: 1,
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.background,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.background,
),
Padding(
padding: const EdgeInsets.all(12),
@@ -909,19 +898,19 @@ class _ConfirmTransactionViewState
widget.isPaynymTransaction
? widget.txData.paynymAccountLite!.nymName
: widget.txData.recipients?.first.address ??
- widget
- .txData
- .sparkRecipients!
- .first
- .address,
- style: STextStyles.desktopTextExtraExtraSmall(
- context,
- ).copyWith(
- color:
- Theme.of(
+ widget
+ .txData
+ .sparkRecipients!
+ .first
+ .address,
+ style:
+ STextStyles.desktopTextExtraExtraSmall(
+ context,
+ ).copyWith(
+ color: Theme.of(
context,
).extension<StackColors>()!.textDark,
- ),
+ ),
),
],
),
@@ -929,10 +918,9 @@ class _ConfirmTransactionViewState
if (widget.isPaynymTransaction)
Container(
height: 1,
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.background,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.background,
),
if (widget.isPaynymTransaction)
Padding(
@@ -950,14 +938,14 @@ class _ConfirmTransactionViewState
const SizedBox(height: 2),
SelectableText(
ref.watch(pAmountFormatter(coin)).format(fee!),
- style: STextStyles.desktopTextExtraExtraSmall(
- context,
- ).copyWith(
- color:
- Theme.of(
+ style:
+ STextStyles.desktopTextExtraExtraSmall(
+ context,
+ ).copyWith(
+ color: Theme.of(
context,
).extension<StackColors>()!.textDark,
- ),
+ ),
),
],
),
@@ -965,10 +953,9 @@ class _ConfirmTransactionViewState
if (coin is Ethereum)
Container(
height: 1,
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.background,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.background,
),
if (coin is Ethereum)
Padding(
@@ -986,14 +973,14 @@ class _ConfirmTransactionViewState
const SizedBox(height: 2),
SelectableText(
widget.txData.nonce.toString(),
- style: STextStyles.desktopTextExtraExtraSmall(
- context,
- ).copyWith(
- color:
- Theme.of(
+ style:
+ STextStyles.desktopTextExtraExtraSmall(
+ context,
+ ).copyWith(
+ color: Theme.of(
context,
).extension<StackColors>()!.textDark,
- ),
+ ),
),
],
),
@@ -1063,33 +1050,36 @@ class _ConfirmTransactionViewState
focusNode: _onChainNoteFocusNode,
style: STextStyles.field(context),
onChanged: (_) => setState(() {}),
- decoration: standardInputDecoration(
- "Type something...",
- _onChainNoteFocusNode,
- context,
- ).copyWith(
- suffixIcon:
- onChainNoteController.text.isNotEmpty
+ decoration:
+ standardInputDecoration(
+ "Type something...",
+ _onChainNoteFocusNode,
+ context,
+ ).copyWith(
+ suffixIcon:
+ onChainNoteController.text.isNotEmpty
? Padding(
- padding: const EdgeInsets.only(right: 0),
- child: UnconstrainedBox(
- child: Row(
- children: [
- TextFieldIconButton(
- child: const XIcon(),
- onTap: () async {
- setState(() {
- onChainNoteController.text =
- "";
- });
- },
- ),
- ],
+ padding: const EdgeInsets.only(
+ right: 0,
),
- ),
- )
+ child: UnconstrainedBox(
+ child: Row(
+ children: [
+ TextFieldIconButton(
+ child: const XIcon(),
+ onTap: () async {
+ setState(() {
+ onChainNoteController.text =
+ "";
+ });
+ },
+ ),
+ ],
+ ),
+ ),
+ )
: null,
- ),
+ ),
),
),
if (coin is Epiccash || coin is Mimblewimblecoin)
@@ -1098,14 +1088,12 @@ class _ConfirmTransactionViewState
(coin is Epiccash || coin is Mimblewimblecoin)
? "Local Note (optional)"
: "Note (optional)",
- style: STextStyles.desktopTextExtraSmall(
- context,
- ).copyWith(
- color:
- Theme.of(context)
+ style: STextStyles.desktopTextExtraSmall(context)
+ .copyWith(
+ color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveSearchIconRight,
- ),
+ ),
textAlign: TextAlign.left,
),
const SizedBox(height: 10),
@@ -1120,49 +1108,48 @@ class _ConfirmTransactionViewState
enableSuggestions: isDesktop ? false : true,
controller: noteController,
focusNode: _noteFocusNode,
- style: STextStyles.desktopTextExtraSmall(
- context,
- ).copyWith(
- color:
- Theme.of(
+ style: STextStyles.desktopTextExtraSmall(context)
+ .copyWith(
+ color: Theme.of(
context,
).extension<StackColors>()!.textFieldActiveText,
- height: 1.8,
- ),
+ height: 1.8,
+ ),
onChanged: (_) => setState(() {}),
- decoration: standardInputDecoration(
- "Type something...",
- _noteFocusNode,
- context,
- desktopMed: true,
- ).copyWith(
- contentPadding: const EdgeInsets.only(
- left: 16,
- top: 11,
- bottom: 12,
- right: 5,
- ),
- suffixIcon:
- noteController.text.isNotEmpty
+ decoration:
+ standardInputDecoration(
+ "Type something...",
+ _noteFocusNode,
+ context,
+ desktopMed: true,
+ ).copyWith(
+ contentPadding: const EdgeInsets.only(
+ left: 16,
+ top: 11,
+ bottom: 12,
+ right: 5,
+ ),
+ suffixIcon: noteController.text.isNotEmpty
? Padding(
- padding: const EdgeInsets.only(right: 0),
- child: UnconstrainedBox(
- child: Row(
- children: [
- TextFieldIconButton(
- child: const XIcon(),
- onTap: () async {
- setState(
- () => noteController.text = "",
- );
- },
- ),
- ],
+ padding: const EdgeInsets.only(right: 0),
+ child: UnconstrainedBox(
+ child: Row(
+ children: [
+ TextFieldIconButton(
+ child: const XIcon(),
+ onTap: () async {
+ setState(
+ () =>
+ noteController.text = "",
+ );
+ },
+ ),
+ ],
+ ),
),
- ),
- )
+ )
: null,
- ),
+ ),
),
),
const SizedBox(height: 20),
@@ -1185,10 +1172,9 @@ class _ConfirmTransactionViewState
horizontal: 16,
vertical: 18,
),
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.textFieldDefaultBG,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldDefaultBG,
child: SelectableText(
ref.watch(pAmountFormatter(coin)).format(fee!),
style: STextStyles.itemSubtitle(context),
@@ -1217,10 +1203,9 @@ class _ConfirmTransactionViewState
horizontal: 16,
vertical: 18,
),
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.textFieldDefaultBG,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldDefaultBG,
child: SelectableText(
"~${fee!.raw.toInt() ~/ widget.txData.vSize!}",
style: STextStyles.itemSubtitle(context),
@@ -1231,64 +1216,52 @@ class _ConfirmTransactionViewState
SizedBox(height: isDesktop ? 23 : 12),
if (!widget.isTokenTx)
Padding(
- padding:
- isDesktop
- ? const EdgeInsets.symmetric(horizontal: 32)
- : const EdgeInsets.all(0),
+ padding: isDesktop
+ ? const EdgeInsets.symmetric(horizontal: 32)
+ : const EdgeInsets.all(0),
child: RoundedContainer(
- padding:
- isDesktop
- ? const EdgeInsets.symmetric(
- horizontal: 16,
- vertical: 18,
- )
- : const EdgeInsets.all(12),
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.snackBarBackSuccess,
+ padding: isDesktop
+ ? const EdgeInsets.symmetric(horizontal: 16, vertical: 18)
+ : const EdgeInsets.all(12),
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.snackBarBackSuccess,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
isDesktop ? "Total amount to send" : "Total amount",
- style:
- isDesktop
- ? STextStyles.desktopTextExtraExtraSmall(
- context,
- ).copyWith(
- color:
- Theme.of(context)
- .extension<StackColors>()!
- .textConfirmTotalAmount,
- )
- : STextStyles.titleBold12(context).copyWith(
- color:
- Theme.of(context)
- .extension<StackColors>()!
- .textConfirmTotalAmount,
- ),
+ style: isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(
+ context,
+ ).copyWith(
+ color: Theme.of(context)
+ .extension<StackColors>()!
+ .textConfirmTotalAmount,
+ )
+ : STextStyles.titleBold12(context).copyWith(
+ color: Theme.of(context)
+ .extension<StackColors>()!
+ .textConfirmTotalAmount,
+ ),
),
SelectableText(
ref
.watch(pAmountFormatter(coin))
.format(amountWithoutChange + fee!),
- style:
- isDesktop
- ? STextStyles.desktopTextExtraExtraSmall(
- context,
- ).copyWith(
- color:
- Theme.of(context)
- .extension<StackColors>()!
- .textConfirmTotalAmount,
- )
- : STextStyles.itemSubtitle12(context).copyWith(
- color:
- Theme.of(context)
- .extension<StackColors>()!
- .textConfirmTotalAmount,
- ),
+ style: isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(
+ context,
+ ).copyWith(
+ color: Theme.of(context)
+ .extension<StackColors>()!
+ .textConfirmTotalAmount,
+ )
+ : STextStyles.itemSubtitle12(context).copyWith(
+ color: Theme.of(context)
+ .extension<StackColors>()!
+ .textConfirmTotalAmount,
+ ),
textAlign: TextAlign.right,
),
],
@@ -1297,10 +1270,9 @@ class _ConfirmTransactionViewState
),
SizedBox(height: isDesktop ? 28 : 16),
Padding(
- padding:
- isDesktop
- ? const EdgeInsets.symmetric(horizontal: 32)
- : const EdgeInsets.all(0),
+ padding: isDesktop
+ ? const EdgeInsets.symmetric(horizontal: 32)
+ : const EdgeInsets.all(0),
child: PrimaryButton(
label: "Send",
buttonHeight: isDesktop ? ButtonHeight.l : null,
@@ -1308,28 +1280,27 @@ class _ConfirmTransactionViewState
if (isDesktop) {
final unlocked = await showDialog<bool?>(
context: context,
- builder:
- (context) => DesktopDialog(
- maxWidth: 580,
- maxHeight: double.infinity,
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- const Row(
- mainAxisAlignment: MainAxisAlignment.end,
- children: [DesktopDialogCloseButton()],
- ),
- Padding(
- padding: const EdgeInsets.only(
- left: 32,
- right: 32,
- bottom: 32,
- ),
- child: DesktopAuthSend(coin: coin),
- ),
- ],
+ builder: (context) => DesktopDialog(
+ maxWidth: 580,
+ maxHeight: double.infinity,
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [DesktopDialogCloseButton()],
),
- ),
+ Padding(
+ padding: const EdgeInsets.only(
+ left: 32,
+ right: 32,
+ bottom: 32,
+ ),
+ child: DesktopAuthSend(coin: coin),
+ ),
+ ],
+ ),
+ ),
);
if (context.mounted && unlocked is bool) {
if (unlocked) {
@@ -1350,18 +1321,16 @@ class _ConfirmTransactionViewState
RouteGenerator.getRoute(
shouldUseMaterialRoute:
RouteGenerator.useMaterialPageRoute,
- builder:
- (_) => const LockscreenView(
- showBackButton: true,
- popOnSuccess: true,
- routeOnSuccessArguments: true,
- routeOnSuccess: "",
- biometricsCancelButtonString: "CANCEL",
- biometricsLocalizedReason:
- "Authenticate to send transaction",
- biometricsAuthenticationTitle:
- "Confirm Transaction",
- ),
+ builder: (_) => const LockscreenView(
+ showBackButton: true,
+ popOnSuccess: true,
+ routeOnSuccessArguments: true,
+ routeOnSuccess: "",
+ biometricsCancelButtonString: "CANCEL",
+ biometricsLocalizedReason:
+ "Authenticate to send transaction",
+ biometricsAuthenticationTitle: "Confirm Transaction",
+ ),
settings: const RouteSettings(
name: "/confirmsendlockscreen",
),
@@ -1375,10 +1344,9 @@ class _ConfirmTransactionViewState
unawaited(
showFloatingFlushBar(
type: FlushBarType.warning,
- message:
- Util.isDesktop
- ? "Invalid passphrase"
- : "Invalid PIN",
+ message: Util.isDesktop
+ ? "Invalid passphrase"
+ : "Invalid PIN",
context: context,
),
);
diff --git a/lib/utilities/git_status.dart b/lib/utilities/git_status.dart
index 9efade7..a6d79f3 100644
--- a/lib/utilities/git_status.dart
+++ b/lib/utilities/git_status.dart
@@ -1,13 +1,13 @@
import 'dart:convert';
import 'package:flutter/material.dart';
-import 'package:flutter_libepiccash/git_versions.dart' as epic_versions;
import 'package:http/http.dart';
import '../../../themes/stack_colors.dart';
import '../../../utilities/logger.dart';
import '../../../utilities/text_styles.dart';
import '../app_config.dart';
+import '../wl_gen/generated/libepiccash_interface_impl.dart';
import '../wl_gen/interfaces/libmwc_interface.dart';
const kGithubAPI = "https://api.github.com";
@@ -17,7 +17,7 @@ const kGithubHead = "/repos";
enum CommitStatus { isHead, isOldCommit, notACommit, notLoaded }
abstract class GitStatus {
- static String get epicCashCommit => epic_versions.getPluginVersion();
+ static String get epicCashCommit => libEpic.getPluginVersion();
// static String get moneroCommit => monero_versions.getPluginVersion();
static String get mimblewimblecoinCommit => libMwc.getPluginVersion();
diff --git a/lib/wallets/crypto_currency/coins/epiccash.dart b/lib/wallets/crypto_currency/coins/epiccash.dart
index b715f96..e60f90f 100644
--- a/lib/wallets/crypto_currency/coins/epiccash.dart
+++ b/lib/wallets/crypto_currency/coins/epiccash.dart
@@ -1,9 +1,8 @@
-import 'package:flutter_libepiccash/lib.dart' as epic;
-
import '../../../models/isar/models/blockchain_data/address.dart';
import '../../../models/node_model.dart';
import '../../../utilities/default_nodes.dart';
import '../../../utilities/enums/derive_path_type_enum.dart';
+import '../../../wl_gen/interfaces/libepiccash_interface.dart';
import '../crypto_currency.dart';
import '../intermediate/bip39_currency.dart';
@@ -63,7 +62,7 @@ class Epiccash extends Bip39Currency {
}
}
- return epic.LibEpiccash.validateSendAddress(address: address);
+ return libEpic.validateSendAddress(address: address);
}
@override
@@ -115,10 +114,9 @@ class Epiccash extends Bip39Currency {
int get targetBlockTimeSeconds => 60;
@override
- DerivePathType get defaultDerivePathType =>
- throw UnsupportedError(
- "$runtimeType does not use bitcoin style derivation paths",
- );
+ DerivePathType get defaultDerivePathType => throw UnsupportedError(
+ "$runtimeType does not use bitcoin style derivation paths",
+ );
@override
Uri defaultBlockExplorer(String txid) {
diff --git a/lib/wallets/wallet/impl/epiccash_wallet.dart b/lib/wallets/wallet/impl/epiccash_wallet.dart
index 6f961e2..a734626 100644
--- a/lib/wallets/wallet/impl/epiccash_wallet.dart
+++ b/lib/wallets/wallet/impl/epiccash_wallet.dart
@@ -3,8 +3,6 @@ import 'dart:convert';
import 'dart:io';
import 'package:decimal/decimal.dart';
-import 'package:flutter_libepiccash/lib.dart' as epiccash;
-import 'package:flutter_libepiccash/models/transaction.dart' as epic_models;
import 'package:isar_community/isar.dart';
import 'package:mutex/mutex.dart';
import 'package:stack_wallet_backup/generate_password.dart';
@@ -33,6 +31,7 @@ import '../../../utilities/logger.dart';
import '../../../utilities/stack_file_system.dart';
import '../../../utilities/test_epic_box_connection.dart';
import '../../../utilities/tor_plain_net_option_enum.dart';
+import '../../../wl_gen/interfaces/libepiccash_interface.dart';
import '../../crypto_currency/crypto_currency.dart';
import '../../models/tx_data.dart';
import '../intermediate/bip39_wallet.dart';
@@ -86,10 +85,11 @@ class EpiccashWallet extends Bip39Wallet {
Future<String> cancelPendingTransactionAndPost(String txSlateId) async {
try {
_hackedCheckTorNodePrefs();
- final String wallet =
- (await secureStorageInterface.read(key: '${walletId}_wallet'))!;
+ final String wallet = (await secureStorageInterface.read(
+ key: '${walletId}_wallet',
+ ))!;
- final result = await epiccash.LibEpiccash.cancelTransaction(
+ final result = await libEpic.cancelTransaction(
wallet: wallet,
transactionId: txSlateId,
);
@@ -178,7 +178,7 @@ class EpiccashWallet extends Bip39Wallet {
_hackedCheckTorNodePrefs();
final available = info.cachedBalance.spendable.raw.toInt();
- final transactionFees = await epiccash.LibEpiccash.getTransactionFees(
+ final transactionFees = await libEpic.getTransactionFees(
wallet: wallet!,
amount: satoshiAmount,
minimumConfirmations: cryptoCurrency.minConfirms,
@@ -187,8 +187,9 @@ class EpiccashWallet extends Bip39Wallet {
int realFee = 0;
try {
- realFee =
- (Decimal.parse(transactionFees.fee.toString())).toBigInt().toInt();
+ realFee = (Decimal.parse(
+ transactionFees.fee.toString(),
+ )).toBigInt().toInt();
} catch (e, s) {
//todo: come back to this
Logging.instance.e("Error getting fees", error: e, stackTrace: s);
@@ -208,7 +209,7 @@ class EpiccashWallet extends Bip39Wallet {
if (!syncMutex.isLocked) {
await syncMutex.protect(() async {
// How does getWalletBalances start syncing????
- await epiccash.LibEpiccash.getWalletBalances(
+ await libEpic.getWalletBalances(
wallet: wallet!,
refreshFromNode: refreshFromNode,
minimumConfirmations: 10,
@@ -231,7 +232,7 @@ class EpiccashWallet extends Bip39Wallet {
_hackedCheckTorNodePrefs();
final wallet = await secureStorageInterface.read(key: '${walletId}_wallet');
const refreshFromNode = 0;
- return await epiccash.LibEpiccash.getWalletBalances(
+ return await libEpic.getWalletBalances(
wallet: wallet!,
refreshFromNode: refreshFromNode,
minimumConfirmations: cryptoCurrency.minConfirms,
@@ -329,7 +330,7 @@ class EpiccashWallet extends Bip39Wallet {
) async {
final wallet = await secureStorageInterface.read(key: '${walletId}_wallet');
- final walletAddress = await epiccash.LibEpiccash.getAddressInfo(
+ final walletAddress = await libEpic.getAddressInfo(
wallet: wallet!,
index: index,
epicboxConfig: epicboxConfig.toString(),
@@ -353,7 +354,7 @@ class EpiccashWallet extends Bip39Wallet {
Future<void> _startScans() async {
try {
//First stop the current listener
- epiccash.LibEpiccash.stopEpicboxListener();
+ libEpic.stopEpicboxListener();
final wallet = await secureStorageInterface.read(
key: '${walletId}_wallet',
);
@@ -375,7 +376,7 @@ class EpiccashWallet extends Bip39Wallet {
"chainHeight: $chainHeight, lastScannedBlock: $lastScannedBlock",
);
- final int nextScannedBlock = await epiccash.LibEpiccash.scanOutputs(
+ final int nextScannedBlock = await libEpic.scanOutputs(
wallet: wallet!,
startHeight: lastScannedBlock,
numberOfBlocks: scanChunkSize,
@@ -408,7 +409,7 @@ class EpiccashWallet extends Bip39Wallet {
Logging.instance.d("STARTING WALLET LISTENER ....");
final wallet = await secureStorageInterface.read(key: '${walletId}_wallet');
final EpicBoxConfigModel epicboxConfig = await getEpicBoxConfig();
- epiccash.LibEpiccash.startEpicboxListener(
+ libEpic.startEpicboxListener(
wallet: wallet!,
epicboxConfig: epicboxConfig.toString(),
);
@@ -492,7 +493,7 @@ class EpiccashWallet extends Bip39Wallet {
final String name = walletId;
- await epiccash.LibEpiccash.initializeNewWallet(
+ await libEpic.initializeNewWallet(
config: stringConfig,
mnemonic: mnemonicString,
password: password,
@@ -500,7 +501,7 @@ class EpiccashWallet extends Bip39Wallet {
);
//Open wallet
- encodedWallet = await epiccash.LibEpiccash.openWallet(
+ encodedWallet = await libEpic.openWallet(
config: stringConfig,
password: password,
);
@@ -542,7 +543,7 @@ class EpiccashWallet extends Bip39Wallet {
key: '${walletId}_password',
);
- final walletOpen = await epiccash.LibEpiccash.openWallet(
+ final walletOpen = await libEpic.openWallet(
config: config,
password: password!,
);
@@ -591,7 +592,7 @@ class EpiccashWallet extends Bip39Wallet {
if (receiverAddress.startsWith("http://") ||
receiverAddress.startsWith("https://")) {
- transaction = await epiccash.LibEpiccash.txHttpSend(
+ transaction = await libEpic.txHttpSend(
wallet: wallet!,
selectionStrategyIsAll: 0,
minimumConfirmations: cryptoCurrency.minConfirms,
@@ -600,7 +601,7 @@ class EpiccashWallet extends Bip39Wallet {
address: txData.recipients!.first.address,
);
} else {
- transaction = await epiccash.LibEpiccash.createTransaction(
+ transaction = await libEpic.createTransaction(
wallet: wallet!,
amount: txData.recipients!.first.amount.raw.toInt(),
address: txData.recipients!.first.address,
@@ -694,7 +695,7 @@ class EpiccashWallet extends Bip39Wallet {
value: epicboxConfig.toString(),
);
- await epiccash.LibEpiccash.recoverWallet(
+ await libEpic.recoverWallet(
config: stringConfig,
password: password,
mnemonic: await getMnemonic(),
@@ -717,7 +718,7 @@ class EpiccashWallet extends Bip39Wallet {
);
//Open Wallet
- final walletOpen = await epiccash.LibEpiccash.openWallet(
+ final walletOpen = await libEpic.openWallet(
config: stringConfig,
password: password,
);
@@ -905,20 +906,19 @@ class EpiccashWallet extends Bip39Wallet {
);
const refreshFromNode = 1;
- final myAddresses =
- await mainDB
- .getAddresses(walletId)
- .filter()
- .typeEqualTo(AddressType.mimbleWimble)
- .and()
- .subTypeEqualTo(AddressSubType.receiving)
- .and()
- .valueIsNotEmpty()
- .valueProperty()
- .findAll();
+ final myAddresses = await mainDB
+ .getAddresses(walletId)
+ .filter()
+ .typeEqualTo(AddressType.mimbleWimble)
+ .and()
+ .subTypeEqualTo(AddressSubType.receiving)
+ .and()
+ .valueIsNotEmpty()
+ .valueProperty()
+ .findAll();
final myAddressesSet = myAddresses.toSet();
- final transactions = await epiccash.LibEpiccash.getTransactions(
+ final transactions = await libEpic.getTransactions(
wallet: wallet!,
refreshFromNode: refreshFromNode,
);
@@ -929,12 +929,12 @@ class EpiccashWallet extends Bip39Wallet {
for (final tx in transactions) {
final isIncoming =
- tx.txType == epic_models.TransactionType.TxReceived ||
- tx.txType == epic_models.TransactionType.TxReceivedCancelled;
+ libEpic.txTypeIsReceived(tx.txType) ||
+ libEpic.txTypeIsReceiveCancelled(tx.txType);
final slateId = tx.txSlateId;
final commitId = slatesToCommits[slateId]?['commitId'] as String?;
- final numberOfMessages = tx.messages?.messages.length;
- final onChainNote = tx.messages?.messages[0].message;
+ final numberOfMessages = tx.messages?.length;
+ final onChainNote = tx.messages?.first.message;
final addressFrom = slatesToCommits[slateId]?["from"] as String?;
final addressTo = slatesToCommits[slateId]?["to"] as String?;
@@ -994,13 +994,12 @@ class EpiccashWallet extends Bip39Wallet {
"slateId": slateId,
"onChainNote": onChainNote,
"isCancelled":
- tx.txType == epic_models.TransactionType.TxSentCancelled ||
- tx.txType == epic_models.TransactionType.TxReceivedCancelled,
- "overrideFee":
- Amount(
- rawValue: BigInt.from(fee),
- fractionDigits: cryptoCurrency.fractionDigits,
- ).toJsonString(),
+ libEpic.txTypeIsSentCancelled(tx.txType) ||
+ libEpic.txTypeIsReceiveCancelled(tx.txType),
+ "overrideFee": Amount(
+ rawValue: BigInt.from(fee),
+ fractionDigits: cryptoCurrency.fractionDigits,
+ ).toJsonString(),
};
final txn = TransactionV2(
@@ -1087,9 +1086,7 @@ class EpiccashWallet extends Bip39Wallet {
Future<void> updateChainHeight() async {
_hackedCheckTorNodePrefs();
final config = await _getRealConfig();
- final latestHeight = await epiccash.LibEpiccash.getChainHeight(
- config: config,
- );
+ final latestHeight = await libEpic.getChainHeight(config: config);
await info.updateCachedChainHeight(
newHeight: latestHeight,
isar: mainDB.isar,
@@ -1132,7 +1129,7 @@ class EpiccashWallet extends Bip39Wallet {
@override
Future<void> exit() async {
- epiccash.LibEpiccash.stopEpicboxListener();
+ libEpic.stopEpicboxListener();
timer?.cancel();
timer = null;
await super.exit();
@@ -1185,7 +1182,7 @@ Future<String> deleteEpicWallet({
return "Tried to delete non existent epic wallet file with walletId=$walletId";
} else {
try {
- return epiccash.LibEpiccash.deleteWallet(wallet: wallet, config: config!);
+ return libEpic.deleteWallet(wallet: wallet, config: config!);
} catch (e, s) {
Logging.instance.e("$e\n$s", error: e, stackTrace: s);
return "deleteEpicWallet($walletId) failed...";
diff --git a/lib/wl_gen/interfaces/libepiccash_interface.dart b/lib/wl_gen/interfaces/libepiccash_interface.dart
new file mode 100644
index 0000000..af5e653
--- /dev/null
+++ b/lib/wl_gen/interfaces/libepiccash_interface.dart
@@ -0,0 +1,160 @@
+export '../generated/libepiccash_interface_impl.dart';
+
+abstract class LibEpicCashInterface {
+ const LibEpicCashInterface();
+
+ bool txTypeIsReceived(Enum value);
+ bool txTypeIsReceiveCancelled(Enum value);
+ bool txTypeIsSentCancelled(Enum value);
+
+ Future<String> initializeNewWallet({
+ required String config,
+ required String mnemonic,
+ required String password,
+ required String name,
+ });
+
+ Future<String> openWallet({required String config, required String password});
+
+ Future<void> recoverWallet({
+ required String config,
+ required String password,
+ required String mnemonic,
+ required String name,
+ });
+
+ Future<({String commitId, String slateId})> txHttpSend({
+ required String wallet,
+ required int selectionStrategyIsAll,
+ required int minimumConfirmations,
+ required String message,
+ required int amount,
+ required String address,
+ });
+
+ Future<({String commitId, String slateId})> createTransaction({
+ required String wallet,
+ required int amount,
+ required String address,
+ required int secretKeyIndex,
+ required String epicboxConfig,
+ required int minimumConfirmations,
+ required String note,
+ });
+
+ Future<String> cancelTransaction({
+ required String wallet,
+ required String transactionId,
+ });
+
+ Future<List<EpicTransaction>> getTransactions({
+ required String wallet,
+ required int refreshFromNode,
+ });
+
+ void startEpicboxListener({
+ required String wallet,
+ required String epicboxConfig,
+ });
+
+ void stopEpicboxListener();
+
+ bool validateSendAddress({required String address});
+
+ Future<({int fee, bool strategyUseAll, int total})> getTransactionFees({
+ required String wallet,
+ required int amount,
+ required int minimumConfirmations,
+ required int available,
+ });
+
+ Future<
+ ({
+ double awaitingFinalization,
+ double pending,
+ double spendable,
+ double total,
+ })
+ >
+ getWalletBalances({
+ required String wallet,
+ required int refreshFromNode,
+ required int minimumConfirmations,
+ });
+
+ Future<String> getAddressInfo({
+ required String wallet,
+ required int index,
+ required String epicboxConfig,
+ });
+
+ Future<int> scanOutputs({
+ required String wallet,
+ required int startHeight,
+ required int numberOfBlocks,
+ });
+
+ Future<int> getChainHeight({required String config});
+
+ Future<String> deleteWallet({required String wallet, required String config});
+
+ String getPluginVersion();
+}
+
+class EpicTransaction {
+ final String parentKeyId;
+ final int id;
+ final String? txSlateId;
+ final Enum txType;
+ final String creationTs;
+ final String confirmationTs;
+ final bool confirmed;
+ final int numInputs;
+ final int numOutputs;
+ final String amountCredited;
+ final String amountDebited;
+ final String? fee;
+ final String? ttlCutoffHeight;
+ final List<EpicMessage>? messages;
+ final String? storedTx;
+ final String? kernelExcess;
+ final int? kernelLookupMinHeight;
+ final String? paymentProof;
+
+ EpicTransaction({
+ required this.parentKeyId,
+ required this.id,
+ this.txSlateId,
+ required this.txType,
+ required this.creationTs,
+ required this.confirmationTs,
+ required this.confirmed,
+ required this.numInputs,
+ required this.numOutputs,
+ required this.amountCredited,
+ required this.amountDebited,
+ this.fee,
+ this.ttlCutoffHeight,
+ this.messages,
+ this.storedTx,
+ this.kernelExcess,
+ this.kernelLookupMinHeight,
+ this.paymentProof,
+ });
+}
+
+class EpicMessage {
+ final String id;
+ final String publicKey;
+ final String? message;
+ final String? messageSig;
+
+ EpicMessage({
+ required this.id,
+ required this.publicKey,
+ this.message,
+ this.messageSig,
+ });
+}
+
+class BadHttpAddressException implements Exception {}
diff --git a/tool/wl_templates/EPIC_libepiccash_interface_impl.template.dart b/tool/wl_templates/EPIC_libepiccash_interface_impl.template.dart
new file mode 100644
index 0000000..579c322
--- /dev/null
+++ b/tool/wl_templates/EPIC_libepiccash_interface_impl.template.dart
@@ -0,0 +1,274 @@
+//ON
+import 'package:flutter_libepiccash/git_versions.dart' as epic_versions;
+import 'package:flutter_libepiccash/lib.dart';
+import 'package:flutter_libepiccash/models/transaction.dart';
+
+//END_ON
+import '../interfaces/libepiccash_interface.dart';
+
+LibEpicCashInterface get libEpic => _getLib();
+
+//OFF
+LibEpicCashInterface _getLib() => throw Exception("EPIC not enabled!");
+
+//END_OFF
+//ON
+LibEpicCashInterface _getLib() => const _LibEpicCashInterfaceImpl();
+
+final class _LibEpicCashInterfaceImpl extends LibEpicCashInterface {
+ const _LibEpicCashInterfaceImpl();
+
+ @override
+ Future<String> cancelTransaction({
+ required String wallet,
+ required String transactionId,
+ }) {
+ return LibEpiccash.cancelTransaction(
+ wallet: wallet,
+ transactionId: transactionId,
+ );
+ }
+
+ @override
+ Future<({String commitId, String slateId})> createTransaction({
+ required String wallet,
+ required int amount,
+ required String address,
+ required int secretKeyIndex,
+ required String epicboxConfig,
+ required int minimumConfirmations,
+ required String note,
+ }) {
+ return LibEpiccash.createTransaction(
+ wallet: wallet,
+ amount: amount,
+ address: address,
+ secretKeyIndex: secretKeyIndex,
+ epicboxConfig: epicboxConfig,
+ minimumConfirmations: minimumConfirmations,
+ note: note,
+ );
+ }
+
+ @override
+ Future<String> deleteWallet({
+ required String wallet,
+ required String config,
+ }) {
+ return LibEpiccash.deleteWallet(wallet: wallet, config: config);
+ }
+
+ @override
+ Future<String> getAddressInfo({
+ required String wallet,
+ required int index,
+ required String epicboxConfig,
+ }) {
+ return LibEpiccash.getAddressInfo(
+ wallet: wallet,
+ index: index,
+ epicboxConfig: epicboxConfig,
+ );
+ }
+
+ @override
+ Future<int> getChainHeight({required String config}) {
+ return LibEpiccash.getChainHeight(config: config);
+ }
+
+ @override
+ Future<({int fee, bool strategyUseAll, int total})> getTransactionFees({
+ required String wallet,
+ required int amount,
+ required int minimumConfirmations,
+ required int available,
+ }) {
+ return LibEpiccash.getTransactionFees(
+ wallet: wallet,
+ amount: amount,
+ minimumConfirmations: minimumConfirmations,
+ available: available,
+ );
+ }
+
+ @override
+ Future<List<EpicTransaction>> getTransactions({
+ required String wallet,
+ required int refreshFromNode,
+ }) async {
+ final transactions = await LibEpiccash.getTransactions(
+ wallet: wallet,
+ refreshFromNode: refreshFromNode,
+ );
+
+ return transactions
+ .map(
+ (e) => EpicTransaction(
+ parentKeyId: e.parentKeyId,
+ id: e.id,
+ txType: e.txType,
+ creationTs: e.creationTs,
+ confirmationTs: e.confirmationTs,
+ confirmed: e.confirmed,
+ numInputs: e.numInputs,
+ numOutputs: e.numOutputs,
+ amountCredited: e.amountCredited,
+ amountDebited: e.amountDebited,
+ txSlateId: e.txSlateId,
+ fee: e.fee,
+ ttlCutoffHeight: e.ttlCutoffHeight,
+ messages: e.messages?.messages
+ .map(
+ (f) => EpicMessage(
+ id: f.id,
+ publicKey: f.publicKey,
+ message: f.message,
+ messageSig: f.messageSig,
+ ),
+ )
+ .toList(),
+ storedTx: e.storedTx,
+ kernelExcess: e.kernelExcess,
+ kernelLookupMinHeight: e.kernelLookupMinHeight,
+ paymentProof: e.paymentProof,
+ ),
+ )
+ .toList();
+ }
+
+ @override
+ Future<
+ ({
+ double awaitingFinalization,
+ double pending,
+ double spendable,
+ double total,
+ })
+ >
+ getWalletBalances({
+ required String wallet,
+ required int refreshFromNode,
+ required int minimumConfirmations,
+ }) {
+ return LibEpiccash.getWalletBalances(
+ wallet: wallet,
+ refreshFromNode: refreshFromNode,
+ minimumConfirmations: minimumConfirmations,
+ );
+ }
+
+ @override
+ Future<String> initializeNewWallet({
+ required String config,
+ required String mnemonic,
+ required String password,
+ required String name,
+ }) {
+ return LibEpiccash.initializeNewWallet(
+ config: config,
+ mnemonic: mnemonic,
+ password: password,
+ name: name,
+ );
+ }
+
+ @override
+ Future<String> openWallet({
+ required String config,
+ required String password,
+ }) {
+ return LibEpiccash.openWallet(config: config, password: password);
+ }
+
+ @override
+ Future<void> recoverWallet({
+ required String config,
+ required String password,
+ required String mnemonic,
+ required String name,
+ }) {
+ return LibEpiccash.recoverWallet(
+ config: config,
+ password: password,
+ mnemonic: mnemonic,
+ name: name,
+ );
+ }
+
+ @override
+ Future<int> scanOutputs({
+ required String wallet,
+ required int startHeight,
+ required int numberOfBlocks,
+ }) {
+ return LibEpiccash.scanOutputs(
+ wallet: wallet,
+ startHeight: startHeight,
+ numberOfBlocks: numberOfBlocks,
+ );
+ }
+
+ @override
+ void startEpicboxListener({
+ required String wallet,
+ required String epicboxConfig,
+ }) {
+ return LibEpiccash.startEpicboxListener(
+ wallet: wallet,
+ epicboxConfig: epicboxConfig,
+ );
+ }
+
+ @override
+ void stopEpicboxListener() {
+ return LibEpiccash.stopEpicboxListener();
+ }
+
+ @override
+ Future<({String commitId, String slateId})> txHttpSend({
+ required String wallet,
+ required int selectionStrategyIsAll,
+ required int minimumConfirmations,
+ required String message,
+ required int amount,
+ required String address,
+ }) {
+ try {
+ return LibEpiccash.txHttpSend(
+ wallet: wallet,
+ selectionStrategyIsAll: selectionStrategyIsAll,
+ minimumConfirmations: minimumConfirmations,
+ message: message,
+ amount: amount,
+ address: address,
+ );
+ } on BadEpicHttpAddressException catch (_) {
+ throw BadHttpAddressException();
+ }
+ }
+
+ @override
+ bool txTypeIsReceiveCancelled(Enum value) {
+ return value == TransactionType.TxReceivedCancelled;
+ }
+
+ @override
+ bool txTypeIsReceived(Enum value) {
+ return value == TransactionType.TxReceived;
+ }
+
+ @override
+ bool txTypeIsSentCancelled(Enum value) {
+ return value == TransactionType.TxSentCancelled;
+ }
+
+ @override
+ bool validateSendAddress({required String address}) {
+ return LibEpiccash.validateSendAddress(address: address);
+ }
+
+ @override
+ String getPluginVersion() => epic_versions.getPluginVersion();
+}
+
+//END_ON
Why this scored 16/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.