implement sal ffi package optional import and some xmr/wow fixes
What changed, and why it matters
This commit is mostly a large internal refactor that swaps direct calls to the cs_monero and cs_salvium libraries for a new generated interface layer. It also adds Salvium wallet support and fixes some restore-height logic. There is no obvious malicious code, but the change touches sensitive wallet code (keys, addresses, transaction creation, balance queries) and introduces a new abstraction layer whose generated implementation is only partially shown. A few spots look like real bug fixes or hardening (for example, checking whether a wallet instance exists before overwriting it during restore), while other spots move security-critical operations behind a new interface that reviewers cannot fully inspect from this diff alone.
Treat this as a high-touch refactor requiring focused review of the generated `cs_salvium_interface_impl.dart` and `cs_monero_interface_impl.dart` files (not shown in the diff). Verify that the new interface correctly forwards all security-sensitive calls without altering semantics, that `walletInstanceExists` checks prevent accidental wallet overwrite races, and that the 16-word Salvium seed type is intentional and validated. Run functional tests for Monero/Wownero/Salvium restore, send, and view-only flows.
Security signals we found
Refactor of security-critical wallet operations (key export, address derivation, transaction creation, balance queries) behind a new generated abstraction layer
Addition of Salvium wallet lifecycle support and new `cs_salvium_interface.dart` API surface
Removal of direct `cs_monero`/`cs_salvium` imports from UI and coin classes in favor of `wl_gen` interfaces
New guard checks (`walletInstanceExists`) before wallet restore/overwrite in `lib_salvium_wallet.dart`
Salvium seed type now allows 16-word mnemonics in addition to 25-word
Large formatting-only churn makes functional changes harder to audit
Generated implementation files are referenced but not included in the diff, limiting complete review
Evidence from the diff
The patch refactors Monero/Wownero/Salvium wallet handling to use a new wl_gen interface (csMonero/csSalvium) instead of direct FFI/library imports. It removes deprecated get_height_by_date imports and routes address validation, height-by-date, wallet lifecycle, balance, tx creation, and listener registration through the generated interface. The Salvium wallet implementation is rewritten to use the new interface and now accepts 16-word seeds in addition to 25-word seeds. Several call sites now check walletInstanceExists(walletId) before overwriting state during restore. The actual generated implementation files are referenced but not fully present in the diff; only templates are shown. Formatting changes dominate the line count.
Changed components
lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dartlib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dartlib/wallets/crypto_currency/coins/monero.dartlib/wallets/crypto_currency/coins/salvium.dartlib/wallets/crypto_currency/coins/wownero.dartlib/wallets/models/tx_data.dartlib/wallets/wallet/impl/salvium_wallet.dartlib/wallets/wallet/intermediate/lib_monero_wallet.dartlib/wallets/wallet/intermediate/lib_salvium_wallet.dartlib/wl_gen/interfaces/cs_monero_interface.dartlib/wl_gen/interfaces/cs_salvium_interface.darttool/wl_templates/SAL_cs_salvium_interface_impl.template.darttool/wl_templates/XMR_cs_monero_interface_impl.template.dartInspect captured patch +1292 / −721
diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart
index c230578..1972ae5 100644
--- a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart
+++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart
@@ -8,8 +8,6 @@
*
*/
-import 'package:cs_monero/src/deprecated/get_height_by_date.dart'
- as cs_monero_deprecated;
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -42,6 +40,8 @@ import '../../../../widgets/rounded_white_container.dart';
import '../../../../widgets/stack_text_field.dart';
import '../../../../widgets/textfield_icon_button.dart';
import '../../../../widgets/toggle.dart';
+import '../../../../wl_gen/interfaces/cs_monero_interface.dart';
+import '../../../../wl_gen/interfaces/cs_salvium_interface.dart';
import '../../create_or_restore_wallet_view/sub_widgets/coin_image.dart';
import '../restore_view_only_wallet_view.dart';
import '../restore_wallet_view.dart';
@@ -213,10 +213,15 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
int height = 0;
if (date != null) {
if (widget.coin is Monero) {
- height = cs_monero_deprecated.getMoneroHeightByDate(date: date);
+ height = csMonero.getHeightByDate(date, csCoin: CsCoin.monero);
}
if (widget.coin is Wownero) {
- height = cs_monero_deprecated.getWowneroHeightByDate(date: date);
+ height = csMonero.getHeightByDate(date, csCoin: CsCoin.wownero);
+ }
+ if (widget.coin is Salvium) {
+ height = csSalvium.getHeightByDate(
+ DateTime.now().subtract(const Duration(days: 7)),
+ );
}
if (height < 0) {
height = 0;
@@ -256,27 +261,26 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
return MasterScaffold(
isDesktop: isDesktop,
- appBar:
- isDesktop
- ? const DesktopAppBar(
- isCompactHeight: false,
- leading: AppBarBackButton(),
- trailing: ExitToMyStackButton(),
- )
- : AppBar(
- leading: AppBarBackButton(
- onPressed: () {
- if (textFieldFocusNode.hasFocus) {
- textFieldFocusNode.unfocus();
- Future<void>.delayed(
- const Duration(milliseconds: 100),
- ).then((value) => Navigator.of(context).pop());
- } else {
- Navigator.of(context).pop();
- }
- },
- ),
+ appBar: isDesktop
+ ? const DesktopAppBar(
+ isCompactHeight: false,
+ leading: AppBarBackButton(),
+ trailing: ExitToMyStackButton(),
+ )
+ : AppBar(
+ leading: AppBarBackButton(
+ onPressed: () {
+ if (textFieldFocusNode.hasFocus) {
+ textFieldFocusNode.unfocus();
+ Future<void>.delayed(
+ const Duration(milliseconds: 100),
+ ).then((value) => Navigator.of(context).pop());
+ } else {
+ Navigator.of(context).pop();
+ }
+ },
),
+ ),
body: RestoreOptionsPlatformLayout(
isDesktop: isDesktop,
child: ConstrainedBox(
@@ -292,10 +296,9 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
Text(
"Restore options",
textAlign: TextAlign.center,
- style:
- isDesktop
- ? STextStyles.desktopH2(context)
- : STextStyles.pageTitleH1(context),
+ style: isDesktop
+ ? STextStyles.desktopH2(context)
+ : STextStyles.pageTitleH1(context),
),
SizedBox(height: isDesktop ? 40 : 24),
if (coin is ViewOnlyOptionCurrencyInterface)
@@ -306,12 +309,12 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
key: UniqueKey(),
onText: "Seed",
offText: "View Only",
- onColor:
- Theme.of(context).extension<StackColors>()!.popupBG,
- offColor:
- Theme.of(
- context,
- ).extension<StackColors>()!.textFieldDefaultBG,
+ onColor: Theme.of(
+ context,
+ ).extension<StackColors>()!.popupBG,
+ offColor: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldDefaultBG,
isOn: _showViewOnlyOption,
onValueChanged: (value) {
setState(() {
@@ -330,32 +333,33 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
SizedBox(height: isDesktop ? 40 : 24),
_showViewOnlyOption
? ViewOnlyRestoreOption(
- coin: coin,
- dateController: _dateController,
- dateChooserFunction:
- isDesktop ? chooseDesktopDate : chooseDate,
- blockHeightController: _blockHeightController,
- blockHeightFocusNode: _blockHeightFocusNode,
- )
+ coin: coin,
+ dateController: _dateController,
+ dateChooserFunction: isDesktop
+ ? chooseDesktopDate
+ : chooseDate,
+ blockHeightController: _blockHeightController,
+ blockHeightFocusNode: _blockHeightFocusNode,
+ )
: SeedRestoreOption(
- coin: coin,
- dateController: _dateController,
- blockHeightController: _blockHeightController,
- blockHeightFocusNode: _blockHeightFocusNode,
- pwController: passwordController,
- pwFocusNode: passwordFocusNode,
- dateChooserFunction:
- isDesktop ? chooseDesktopDate : chooseDate,
- chooseMnemonicLength: chooseMnemonicLength,
- ),
+ coin: coin,
+ dateController: _dateController,
+ blockHeightController: _blockHeightController,
+ blockHeightFocusNode: _blockHeightFocusNode,
+ pwController: passwordController,
+ pwFocusNode: passwordFocusNode,
+ dateChooserFunction: isDesktop
+ ? chooseDesktopDate
+ : chooseDate,
+ chooseMnemonicLength: chooseMnemonicLength,
+ ),
if (!isDesktop) const Spacer(flex: 3),
SizedBox(height: isDesktop ? 32 : 12),
RestoreOptionsNextButton(
isDesktop: isDesktop,
- onPressed:
- ref.watch(_pIsUsingDate) || _hasBlockHeight
- ? nextPressed
- : null,
+ onPressed: ref.watch(_pIsUsingDate) || _hasBlockHeight
+ ? nextPressed
+ : null,
),
if (isDesktop) const Spacer(flex: 15),
],
@@ -427,24 +431,22 @@ class _SeedRestoreOptionState extends ConsumerState<SeedRestoreOption> {
children: [
Text(
ref.watch(_pIsUsingDate) ? "Choose start date" : "Block height",
- style:
- Util.isDesktop
- ? STextStyles.desktopTextExtraSmall(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.textDark3,
- )
- : STextStyles.smallMed12(context),
+ style: Util.isDesktop
+ ? STextStyles.desktopTextExtraSmall(context).copyWith(
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textDark3,
+ )
+ : STextStyles.smallMed12(context),
textAlign: TextAlign.left,
),
CustomTextButton(
- text:
- ref.watch(_pIsUsingDate) ? "Use block height" : "Use date",
- onTap:
- () =>
- ref.read(_pIsUsingDate.notifier).state =
- !ref.read(_pIsUsingDate),
+ text: ref.watch(_pIsUsingDate)
+ ? "Use block height"
+ : "Use date",
+ onTap: () => ref.read(_pIsUsingDate.notifier).state = !ref.read(
+ _pIsUsingDate,
+ ),
),
],
),
@@ -457,60 +459,59 @@ class _SeedRestoreOptionState extends ConsumerState<SeedRestoreOption> {
widget.coin is Mimblewimblecoin)
ref.watch(_pIsUsingDate)
? RestoreFromDatePicker(
- onTap: widget.dateChooserFunction,
- controller: widget.dateController,
- )
+ onTap: widget.dateChooserFunction,
+ controller: widget.dateController,
+ )
: ClipRRect(
- borderRadius: BorderRadius.circular(
- Constants.size.circularBorderRadius,
- ),
- child: TextField(
- focusNode: widget.blockHeightFocusNode,
- controller: widget.blockHeightController,
- keyboardType: TextInputType.number,
- inputFormatters: [FilteringTextInputFormatter.digitsOnly],
- textInputAction: TextInputAction.done,
- style:
- Util.isDesktop
- ? STextStyles.desktopTextMedium(
+ borderRadius: BorderRadius.circular(
+ Constants.size.circularBorderRadius,
+ ),
+ child: TextField(
+ focusNode: widget.blockHeightFocusNode,
+ controller: widget.blockHeightController,
+ keyboardType: TextInputType.number,
+ inputFormatters: [FilteringTextInputFormatter.digitsOnly],
+ textInputAction: TextInputAction.done,
+ style: Util.isDesktop
+ ? STextStyles.desktopTextMedium(
context,
).copyWith(height: 2)
- : STextStyles.field(context),
- onChanged: (value) {
- setState(() {
- _blockFieldEmpty = value.isEmpty;
- });
- },
- decoration: standardInputDecoration(
- "Start scanning from...",
- widget.blockHeightFocusNode,
- context,
- ).copyWith(
- suffixIcon: UnconstrainedBox(
- child: TextFieldIconButton(
- child: Semantics(
- label:
- "Clear Block Height Field Button. Clears the block height field",
- excludeSemantics: true,
- child:
- !_blockFieldEmpty
- ? XIcon(
- width: Util.isDesktop ? 24 : 16,
- height: Util.isDesktop ? 24 : 16,
- )
- : const SizedBox.shrink(),
+ : STextStyles.field(context),
+ onChanged: (value) {
+ setState(() {
+ _blockFieldEmpty = value.isEmpty;
+ });
+ },
+ decoration:
+ standardInputDecoration(
+ "Start scanning from...",
+ widget.blockHeightFocusNode,
+ context,
+ ).copyWith(
+ suffixIcon: UnconstrainedBox(
+ child: TextFieldIconButton(
+ child: Semantics(
+ label:
+ "Clear Block Height Field Button. Clears the block height field",
+ excludeSemantics: true,
+ child: !_blockFieldEmpty
+ ? XIcon(
+ width: Util.isDesktop ? 24 : 16,
+ height: Util.isDesktop ? 24 : 16,
+ )
+ : const SizedBox.shrink(),
+ ),
+ onTap: () {
+ widget.blockHeightController.text = "";
+ setState(() {
+ _blockFieldEmpty = true;
+ });
+ },
+ ),
+ ),
),
- onTap: () {
- widget.blockHeightController.text = "";
- setState(() {
- _blockFieldEmpty = true;
- });
- },
- ),
- ),
),
),
- ),
if (isCnAnd25 ||
widget.coin is Epiccash ||
widget.coin is Mimblewimblecoin)
@@ -525,15 +526,13 @@ class _SeedRestoreOptionState extends ConsumerState<SeedRestoreOption> {
ref.watch(_pIsUsingDate)
? "Choose the date you made the wallet (approximate is fine)"
: "Enter the initial block height of the wallet",
- style:
- Util.isDesktop
- ? STextStyles.desktopTextExtraSmall(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.textSubtitle1,
- )
- : STextStyles.smallMed12(context).copyWith(fontSize: 10),
+ style: Util.isDesktop
+ ? STextStyles.desktopTextExtraSmall(context).copyWith(
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textSubtitle1,
+ )
+ : STextStyles.smallMed12(context).copyWith(fontSize: 10),
),
),
),
@@ -543,13 +542,11 @@ class _SeedRestoreOptionState extends ConsumerState<SeedRestoreOption> {
SizedBox(height: Util.isDesktop ? 24 : 16),
Text(
"Choose recovery phrase length",
- style:
- Util.isDesktop
- ? STextStyles.desktopTextExtraSmall(context).copyWith(
- color:
- Theme.of(context).extension<StackColors>()!.textDark3,
- )
- : STextStyles.smallMed12(context),
+ style: Util.isDesktop
+ ? STextStyles.desktopTextExtraSmall(context).copyWith(
+ color: Theme.of(context).extension<StackColors>()!.textDark3,
+ )
+ : STextStyles.smallMed12(context),
textAlign: TextAlign.left,
),
SizedBox(height: Util.isDesktop ? 16 : 8),
@@ -577,19 +574,17 @@ class _SeedRestoreOptionState extends ConsumerState<SeedRestoreOption> {
iconStyleData: IconStyleData(
icon: ConditionalParent(
condition: Util.isDesktop,
- builder:
- (child) => Padding(
- padding: const EdgeInsets.only(right: 10),
- child: child,
- ),
+ builder: (child) => Padding(
+ padding: const EdgeInsets.only(right: 10),
+ child: child,
+ ),
child: SvgPicture.asset(
Assets.svg.chevronDown,
width: 12,
height: 6,
- color:
- Theme.of(context)
- .extension<StackColors>()!
- .textFieldActiveSearchIconRight,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldActiveSearchIconRight,
),
),
),
@@ -597,10 +592,9 @@ class _SeedRestoreOptionState extends ConsumerState<SeedRestoreOption> {
offset: const Offset(0, -10),
elevation: 0,
decoration: BoxDecoration(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.textFieldDefaultBG,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldDefaultBG,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
@@ -637,17 +631,15 @@ class _SeedRestoreOptionState extends ConsumerState<SeedRestoreOption> {
children: [
Text(
"Advanced",
- style:
- Util.isDesktop
- ? STextStyles.desktopTextExtraExtraSmall(
+ style: Util.isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(
+ context,
+ ).copyWith(
+ color: Theme.of(
context,
- ).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.textDark3,
- )
- : STextStyles.smallMed12(context),
+ ).extension<StackColors>()!.textDark3,
+ )
+ : STextStyles.smallMed12(context),
textAlign: TextAlign.left,
),
SvgPicture.asset(
@@ -656,10 +648,9 @@ class _SeedRestoreOptionState extends ConsumerState<SeedRestoreOption> {
: Assets.svg.chevronDown,
width: 12,
height: 6,
- color:
- Theme.of(context)
- .extension<StackColors>()!
- .textFieldActiveSearchIconRight,
+ color: Theme.of(context)
+ .extension<StackColors>()!
+ .textFieldActiveSearchIconRight,
),
],
),
@@ -678,57 +669,56 @@ class _SeedRestoreOptionState extends ConsumerState<SeedRestoreOption> {
key: const Key("mnemonicPassphraseFieldKey1"),
focusNode: widget.pwFocusNode,
controller: widget.pwController,
- style:
- Util.isDesktop
- ? STextStyles.desktopTextMedium(
- context,
- ).copyWith(height: 2)
- : STextStyles.field(context),
+ style: Util.isDesktop
+ ? STextStyles.desktopTextMedium(
+ context,
+ ).copyWith(height: 2)
+ : STextStyles.field(context),
obscureText: _hidePassword,
enableSuggestions: false,
autocorrect: false,
- decoration: standardInputDecoration(
- widget.coin is CryptonoteCurrency
- ? "Seed Offset"
- : "BIP39 passphrase",
- widget.pwFocusNode,
- context,
- ).copyWith(
- suffixIcon: UnconstrainedBox(
- child: ConditionalParent(
- condition: Util.isDesktop,
- builder:
- (child) => SizedBox(height: 70, child: child),
- child: Row(
- children: [
- SizedBox(width: Util.isDesktop ? 24 : 16),
- GestureDetector(
- key: const Key(
- "mnemonicPassphraseFieldShowPasswordButtonKey",
- ),
- onTap: () async {
- setState(() {
- _hidePassword = !_hidePassword;
- });
- },
- child: SvgPicture.asset(
- _hidePassword
- ? Assets.svg.eye
- : Assets.svg.eyeSlash,
- color:
- Theme.of(
+ decoration:
+ standardInputDecoration(
+ widget.coin is CryptonoteCurrency
+ ? "Seed Offset"
+ : "BIP39 passphrase",
+ widget.pwFocusNode,
+ context,
+ ).copyWith(
+ suffixIcon: UnconstrainedBox(
+ child: ConditionalParent(
+ condition: Util.isDesktop,
+ builder: (child) =>
+ SizedBox(height: 70, child: child),
+ child: Row(
+ children: [
+ SizedBox(width: Util.isDesktop ? 24 : 16),
+ GestureDetector(
+ key: const Key(
+ "mnemonicPassphraseFieldShowPasswordButtonKey",
+ ),
+ onTap: () async {
+ setState(() {
+ _hidePassword = !_hidePassword;
+ });
+ },
+ child: SvgPicture.asset(
+ _hidePassword
+ ? Assets.svg.eye
+ : Assets.svg.eyeSlash,
+ color: Theme.of(
context,
).extension<StackColors>()!.textDark3,
- width: Util.isDesktop ? 24 : 16,
- height: Util.isDesktop ? 24 : 16,
- ),
+ width: Util.isDesktop ? 24 : 16,
+ height: Util.isDesktop ? 24 : 16,
+ ),
+ ),
+ const SizedBox(width: 12),
+ ],
),
- const SizedBox(width: 12),
- ],
+ ),
),
),
- ),
- ),
),
),
const SizedBox(height: 8),
@@ -737,23 +727,21 @@ class _SeedRestoreOptionState extends ConsumerState<SeedRestoreOption> {
child: Text(
widget.coin is CryptonoteCurrency
? "(Optional) An offset used to derive a different "
- "wallet from the given mnemonic, allowing recovery "
- "of a hidden or alternate wallet based on the same "
- "seed phrase."
+ "wallet from the given mnemonic, allowing recovery "
+ "of a hidden or alternate wallet based on the same "
+ "seed phrase."
: "If the recovery phrase you are about to restore "
- "was created with an optional BIP39 passphrase "
- "you can enter it here.",
- style:
- Util.isDesktop
- ? STextStyles.desktopTextExtraSmall(
+ "was created with an optional BIP39 passphrase "
+ "you can enter it here.",
+ style: Util.isDesktop
+ ? STextStyles.desktopTextExtraSmall(
+ context,
+ ).copyWith(
+ color: Theme.of(
context,
- ).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.textSubtitle1,
- )
- : STextStyles.itemSubtitle(context),
+ ).extension<StackColors>()!.textSubtitle1,
+ )
+ : STextStyles.itemSubtitle(context),
),
),
),
@@ -810,25 +798,23 @@ class _ViewOnlyRestoreOptionState extends ConsumerState<ViewOnlyRestoreOption> {
children: [
Text(
ref.watch(_pIsUsingDate) ? "Choose start date" : "Block height",
- style:
- Util.isDesktop
- ? STextStyles.desktopTextExtraExtraSmall(
+ style: Util.isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(context).copyWith(
+ color: Theme.of(
context,
- ).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.textDark3,
- )
- : STextStyles.smallMed12(context),
+ ).extension<StackColors>()!.textDark3,
+ )
+ : STextStyles.smallMed12(context),
textAlign: TextAlign.left,
),
CustomTextButton(
- text:
- ref.watch(_pIsUsingDate) ? "Use block height" : "Use date",
+ text: ref.watch(_pIsUsingDate)
+ ? "Use block height"
+ : "Use date",
onTap: () {
- ref.read(_pIsUsingDate.notifier).state =
- !ref.read(_pIsUsingDate);
+ ref.read(_pIsUsingDate.notifier).state = !ref.read(
+ _pIsUsingDate,
+ );
},
),
],
@@ -837,60 +823,59 @@ class _ViewOnlyRestoreOptionState extends ConsumerState<ViewOnlyRestoreOption> {
if (showDateOption)
ref.watch(_pIsUsingDate)
? RestoreFromDatePicker(
- onTap: widget.dateChooserFunction,
- controller: widget.dateController,
- )
+ onTap: widget.dateChooserFunction,
+ controller: widget.dateController,
+ )
: ClipRRect(
- borderRadius: BorderRadius.circular(
- Constants.size.circularBorderRadius,
- ),
- child: TextField(
- focusNode: widget.blockHeightFocusNode,
- controller: widget.blockHeightController,
- keyboardType: TextInputType.number,
- inputFormatters: [FilteringTextInputFormatter.digitsOnly],
- textInputAction: TextInputAction.done,
- style:
- Util.isDesktop
- ? STextStyles.desktopTextMedium(
+ borderRadius: BorderRadius.circular(
+ Constants.size.circularBorderRadius,
+ ),
+ child: TextField(
+ focusNode: widget.blockHeightFocusNode,
+ controller: widget.blockHeightController,
+ keyboardType: TextInputType.number,
+ inputFormatters: [FilteringTextInputFormatter.digitsOnly],
+ textInputAction: TextInputAction.done,
+ style: Util.isDesktop
+ ? STextStyles.desktopTextMedium(
context,
).copyWith(height: 2)
- : STextStyles.field(context),
- onChanged: (value) {
- setState(() {
- _blockFieldEmpty = value.isEmpty;
- });
- },
- decoration: standardInputDecoration(
- "Start scanning from...",
- widget.blockHeightFocusNode,
- context,
- ).copyWith(
- suffixIcon: UnconstrainedBox(
- child: TextFieldIconButton(
- child: Semantics(
- label:
- "Clear Block Height Field Button. Clears the block height field",
- excludeSemantics: true,
- child:
- !_blockFieldEmpty
- ? XIcon(
- width: Util.isDesktop ? 24 : 16,
- height: Util.isDesktop ? 24 : 16,
- )
- : const SizedBox.shrink(),
+ : STextStyles.field(context),
+ onChanged: (value) {
+ setState(() {
+ _blockFieldEmpty = value.isEmpty;
+ });
+ },
+ decoration:
+ standardInputDecoration(
+ "Start scanning from...",
+ widget.blockHeightFocusNode,
+ context,
+ ).copyWith(
+ suffixIcon: UnconstrainedBox(
+ child: TextFieldIconButton(
+ child: Semantics(
+ label:
+ "Clear Block Height Field Button. Clears the block height field",
+ excludeSemantics: true,
+ child: !_blockFieldEmpty
+ ? XIcon(
+ width: Util.isDesktop ? 24 : 16,
+ height: Util.isDesktop ? 24 : 16,
+ )
+ : const SizedBox.shrink(),
+ ),
+ onTap: () {
+ widget.blockHeightController.text = "";
+ setState(() {
+ _blockFieldEmpty = true;
+ });
+ },
+ ),
+ ),
),
- onTap: () {
- widget.blockHeightController.text = "";
- setState(() {
- _blockFieldEmpty = true;
- });
- },
- ),
- ),
),
),
- ),
if (showDateOption) const SizedBox(height: 8),
if (showDateOption)
RoundedWhiteContainer(
@@ -899,17 +884,13 @@ class _ViewOnlyRestoreOptionState extends ConsumerState<ViewOnlyRestoreOption> {
ref.watch(_pIsUsingDate)
? "Choose the date you made the wallet (approximate is fine)"
: "Enter the initial block height of the wallet",
- style:
- Util.isDesktop
- ? STextStyles.desktopTextExtraSmall(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.textSubtitle1,
- )
- : STextStyles.smallMed12(
+ style: Util.isDesktop
+ ? STextStyles.desktopTextExtraSmall(context).copyWith(
+ color: Theme.of(
context,
- ).copyWith(fontSize: 10),
+ ).extension<StackColors>()!.textSubtitle1,
+ )
+ : STextStyles.smallMed12(context).copyWith(fontSize: 10),
),
),
),
diff --git a/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart b/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart
index 9d79a31..9494b66 100644
--- a/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart
+++ b/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart
@@ -12,10 +12,6 @@ import 'dart:async';
import 'dart:convert';
import 'dart:math';
-import 'package:cs_monero/src/deprecated/get_height_by_date.dart'
- as cs_monero_deprecated;
-import 'package:cs_salvium/src/deprecated/get_height_by_date.dart'
- as cs_salvium_deprecated;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -50,6 +46,8 @@ import '../../../widgets/custom_buttons/app_bar_icon_button.dart';
import '../../../widgets/desktop/desktop_app_bar.dart';
import '../../../widgets/desktop/desktop_scaffold.dart';
import '../../../widgets/stack_dialog.dart';
+import '../../../wl_gen/interfaces/cs_monero_interface.dart';
+import '../../../wl_gen/interfaces/cs_salvium_interface.dart';
import '../../home_view/home_view.dart';
import '../add_token_view/edit_wallet_tokens_view.dart';
import '../new_wallet_options/new_wallet_options_view.dart';
@@ -116,18 +114,20 @@ class _VerifyRecoveryPhraseViewState
} else if (widget.wallet is LibMoneroWallet ||
widget.wallet is LibSalviumWallet) {
if (widget.wallet.cryptoCurrency is Monero) {
- height = cs_monero_deprecated.getMoneroHeightByDate(
- date: DateTime.now().subtract(const Duration(days: 7)),
+ height = csMonero.getHeightByDate(
+ DateTime.now().subtract(const Duration(days: 7)),
+ csCoin: CsCoin.monero,
);
}
if (widget.wallet.cryptoCurrency is Wownero) {
- height = cs_monero_deprecated.getWowneroHeightByDate(
- date: DateTime.now().subtract(const Duration(days: 7)),
+ height = csMonero.getHeightByDate(
+ DateTime.now().subtract(const Duration(days: 7)),
+ csCoin: CsCoin.wownero,
);
}
if (widget.wallet.cryptoCurrency is Salvium) {
- height = cs_salvium_deprecated.getSalviumHeightByDate(
- date: DateTime.now().subtract(const Duration(days: 7)),
+ height = csSalvium.getHeightByDate(
+ DateTime.now().subtract(const Duration(days: 7)),
);
}
if (height < 0) height = 0;
@@ -151,8 +151,8 @@ class _VerifyRecoveryPhraseViewState
final ViewOnlyWalletData viewOnlyData;
if (widget.wallet is ExtendedKeysInterface) {
- final extendedKeyInfo =
- await (widget.wallet as ExtendedKeysInterface).getXPubs();
+ final extendedKeyInfo = await (widget.wallet as ExtendedKeysInterface)
+ .getXPubs();
final testPath = (_coin as Bip39HDCurrency).constructDerivePath(
derivePathType: (_coin as Bip39HDCurrency).defaultDerivePathType,
chain: 0,
@@ -178,9 +178,8 @@ class _VerifyRecoveryPhraseViewState
} else if (widget.wallet is LibMoneroWallet) {
final w = widget.wallet as LibMoneroWallet;
- final info =
- await w
- .hackToCreateNewViewOnlyWalletDataFromNewlyCreatedWalletThisFunctionShouldNotBeCalledUnlessYouKnowWhatYouAreDoing();
+ final info = await w
+ .hackToCreateNewViewOnlyWalletDataFromNewlyCreatedWalletThisFunctionShouldNotBeCalledUnlessYouKnowWhatYouAreDoing();
final address = info.$1;
final privateViewKey = info.$2;
@@ -194,9 +193,8 @@ class _VerifyRecoveryPhraseViewState
} else if (widget.wallet is LibSalviumWallet) {
final w = widget.wallet as LibSalviumWallet;
- final info =
- await w
- .hackToCreateNewViewOnlyWalletDataFromNewlyCreatedWalletThisFunctionShouldNotBeCalledUnlessYouKnowWhatYouAreDoing();
+ final info = await w
+ .hackToCreateNewViewOnlyWalletDataFromNewlyCreatedWalletThisFunctionShouldNotBeCalledUnlessYouKnowWhatYouAreDoing();
final address = info.$1;
final privateViewKey = info.$2;
@@ -310,8 +308,8 @@ class _VerifyRecoveryPhraseViewState
ref
.read(newEthWalletTriggerTempUntilHiveCompletelyDeleted.state)
.state = !ref
- .read(newEthWalletTriggerTempUntilHiveCompletelyDeleted.state)
- .state;
+ .read(newEthWalletTriggerTempUntilHiveCompletelyDeleted.state)
+ .state;
}
if (mounted &&
@@ -338,11 +336,10 @@ class _VerifyRecoveryPhraseViewState
if (mounted) {
await showDialog<void>(
context: context,
- builder:
- (_) => StackOkDialog(
- title: e.toString(),
- desktopPopRootNavigator: Util.isDesktop,
- ),
+ builder: (_) => StackOkDialog(
+ title: e.toString(),
+ desktopPopRootNavigator: Util.isDesktop,
+ ),
);
}
@@ -485,53 +482,54 @@ class _VerifyRecoveryPhraseViewState
@override
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
- final correctIndex =
- ref.watch(verifyMnemonicWordIndexStateProvider.state).state;
+ final correctIndex = ref
+ .watch(verifyMnemonicWordIndexStateProvider.state)
+ .state;
return WillPopScope(
onWillPop: onWillPop,
child: MasterScaffold(
isDesktop: isDesktop,
- appBar:
- isDesktop
- ? DesktopAppBar(
- isCompactHeight: false,
- leading: AppBarBackButton(
- onPressed: () async {
- Navigator.of(context).popUntil(
- ModalRoute.withName(
- NewWalletRecoveryPhraseView.routeName,
- ),
- );
- },
- ),
- trailing: ExitToMyStackButton(
- onPressed: () async {
- await delete();
- if (context.mounted) {
- Navigator.of(context).popUntil(
- ModalRoute.withName(DesktopHomeView.routeName),
- );
- }
- },
- ),
- )
- : AppBar(
- leading: AppBarBackButton(
- onPressed: () async {
+ appBar: isDesktop
+ ? DesktopAppBar(
+ isCompactHeight: false,
+ leading: AppBarBackButton(
+ onPressed: () async {
+ Navigator.of(context).popUntil(
+ ModalRoute.withName(
+ NewWalletRecoveryPhraseView.routeName,
+ ),
+ );
+ },
+ ),
+ trailing: ExitToMyStackButton(
+ onPressed: () async {
+ await delete();
+ if (context.mounted) {
Navigator.of(context).popUntil(
- ModalRoute.withName(
- NewWalletRecoveryPhraseView.routeName,
- ),
+ ModalRoute.withName(DesktopHomeView.routeName),
);
- },
- ),
+ }
+ },
+ ),
+ )
+ : AppBar(
+ leading: AppBarBackButton(
+ onPressed: () async {
+ Navigator.of(context).popUntil(
+ ModalRoute.withName(
+ NewWalletRecoveryPhraseView.routeName,
+ ),
+ );
+ },
),
+ ),
body: SizedBox(
width: isDesktop ? 410 : null,
child: Padding(
- padding:
- isDesktop ? const EdgeInsets.all(0) : const EdgeInsets.all(16),
+ padding: isDesktop
+ ? const EdgeInsets.all(0)
+ : const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
@@ -540,27 +538,24 @@ class _VerifyRecoveryPhraseViewState
Text(
"Verify recovery phrase",
textAlign: TextAlign.center,
- style:
- isDesktop
- ? STextStyles.desktopH2(context)
- : STextStyles.label(context).copyWith(fontSize: 12),
+ style: isDesktop
+ ? STextStyles.desktopH2(context)
+ : STextStyles.label(context).copyWith(fontSize: 12),
),
SizedBox(height: isDesktop ? 16 : 4),
Text(
isDesktop ? "Select word number" : "Tap word number ",
textAlign: TextAlign.center,
- style:
- isDesktop
- ? STextStyles.desktopSubtitleH1(context)
- : STextStyles.pageTitleH1(context),
+ style: isDesktop
+ ? STextStyles.desktopSubtitleH1(context)
+ : STextStyles.pageTitleH1(context),
),
SizedBox(height: isDesktop ? 16 : 12),
Container(
decoration: BoxDecoration(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.textFieldDefaultBG,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldDefaultBG,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
@@ -591,61 +586,51 @@ class _VerifyRecoveryPhraseViewState
Expanded(
child: Consumer(
builder: (_, ref, __) {
- final selectedWord =
- ref
- .watch(
- verifyMnemonicSelectedWordStateProvider
- .state,
- )
- .state;
- final correctWord =
- ref
- .watch(
- verifyMnemonicCorrectWordStateProvider
- .state,
- )
- .state;
+ final selectedWord = ref
+ .watch(
+ verifyMnemonicSelectedWordStateProvider.state,
+ )
+ .state;
+ final correctWord = ref
+ .watch(
+ verifyMnemonicCorrectWordStateProvider.state,
+ )
+ .state;
return ConstrainedBox(
constraints: BoxConstraints(
minHeight: isDesktop ? 70 : 0,
),
child: TextButton(
- onPressed:
- selectedWord.isNotEmpty
- ? () async {
- await _continue(
- correctWord == selectedWord,
- );
- }
- : null,
- style:
- selectedWord.isNotEmpty
- ? Theme.of(context)
- .extension<StackColors>()!
- .getPrimaryEnabledButtonStyle(context)
- : Theme.of(context)
- .extension<StackColors>()!
- .getPrimaryDisabledButtonStyle(
- context,
- ),
- child:
- isDesktop
- ? Text(
- "Verify",
- style:
- selectedWord.isNotEmpty
- ? STextStyles.desktopButtonEnabled(
- context,
- )
- : STextStyles.desktopButtonDisabled(
- context,
- ),
- )
- : Text(
- "Continue",
- style: STextStyles.button(context),
- ),
+ onPressed: selectedWord.isNotEmpty
+ ? () async {
+ await _continue(
+ correctWord == selectedWord,
+ );
+ }
+ : null,
+ style: selectedWord.isNotEmpty
+ ? Theme.of(context)
+ .extension<StackColors>()!
+ .getPrimaryEnabledButtonStyle(context)
+ : Theme.of(context)
+ .extension<StackColors>()!
+ .getPrimaryDisabledButtonStyle(context),
+ child: isDesktop
+ ? Text(
+ "Verify",
+ style: selectedWord.isNotEmpty
+ ? STextStyles.desktopButtonEnabled(
+ context,
+ )
+ : STextStyles.desktopButtonDisabled(
+ context,
+ ),
+ )
+ : Text(
+ "Continue",
+ style: STextStyles.button(context),
+ ),
),
);
},
diff --git a/lib/wallets/crypto_currency/coins/monero.dart b/lib/wallets/crypto_currency/coins/monero.dart
index 4d15351..3a983cf 100644
--- a/lib/wallets/crypto_currency/coins/monero.dart
+++ b/lib/wallets/crypto_currency/coins/monero.dart
@@ -1,9 +1,7 @@
-import 'package:cs_monero/src/ffi_bindings/monero_wallet_bindings.dart'
- as xmr_wallet_ffi;
-
import '../../../models/node_model.dart';
import '../../../utilities/default_nodes.dart';
import '../../../utilities/enums/derive_path_type_enum.dart';
+import '../../../wl_gen/interfaces/cs_monero_interface.dart';
import '../crypto_currency.dart';
import '../intermediate/cryptonote_currency.dart';
@@ -54,7 +52,7 @@ class Monero extends CryptonoteCurrency {
}
switch (network) {
case CryptoCurrencyNetwork.main:
- return xmr_wallet_ffi.validateAddress(address, 0);
+ return csMonero.validateAddress(address, 0, csCoin: CsCoin.monero);
default:
throw Exception("Unsupported network: $network");
}
@@ -107,10 +105,9 @@ class Monero extends CryptonoteCurrency {
int get targetBlockTimeSeconds => 120;
@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/crypto_currency/coins/salvium.dart b/lib/wallets/crypto_currency/coins/salvium.dart
index db794dc..931208c 100644
--- a/lib/wallets/crypto_currency/coins/salvium.dart
+++ b/lib/wallets/crypto_currency/coins/salvium.dart
@@ -1,9 +1,7 @@
-import 'package:cs_salvium/src/ffi_bindings/salvium_wallet_bindings.dart'
- as sal_wallet_ffi;
-
import '../../../models/node_model.dart';
import '../../../utilities/default_nodes.dart';
import '../../../utilities/enums/derive_path_type_enum.dart';
+import '../../../wl_gen/interfaces/cs_salvium_interface.dart';
import '../crypto_currency.dart';
import '../intermediate/cryptonote_currency.dart';
@@ -54,7 +52,7 @@ class Salvium extends CryptonoteCurrency {
}
switch (network) {
case CryptoCurrencyNetwork.main:
- return sal_wallet_ffi.validateAddress(address, 0);
+ return csSalvium.validateAddress(address, 0);
default:
throw Exception("Unsupported network: $network");
}
@@ -107,10 +105,9 @@ class Salvium extends CryptonoteCurrency {
int get targetBlockTimeSeconds => 120;
@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/crypto_currency/coins/wownero.dart b/lib/wallets/crypto_currency/coins/wownero.dart
index 0c702af..66d27c3 100644
--- a/lib/wallets/crypto_currency/coins/wownero.dart
+++ b/lib/wallets/crypto_currency/coins/wownero.dart
@@ -1,9 +1,7 @@
-import 'package:cs_monero/src/ffi_bindings/wownero_wallet_bindings.dart'
- as wow_wallet_ffi;
-
import '../../../models/node_model.dart';
import '../../../utilities/default_nodes.dart';
import '../../../utilities/enums/derive_path_type_enum.dart';
+import '../../../wl_gen/interfaces/cs_monero_interface.dart';
import '../crypto_currency.dart';
import '../intermediate/cryptonote_currency.dart';
@@ -54,7 +52,7 @@ class Wownero extends CryptonoteCurrency {
}
switch (network) {
case CryptoCurrencyNetwork.main:
- return wow_wallet_ffi.validateAddress(address, 0);
+ return csMonero.validateAddress(address, 0, csCoin: CsCoin.wownero);
default:
throw Exception("Unsupported network: $network");
}
@@ -107,10 +105,9 @@ class Wownero extends CryptonoteCurrency {
int get targetBlockTimeSeconds => 120;
@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/models/tx_data.dart b/lib/wallets/models/tx_data.dart
index 8ebed1e..68ba7b7 100644
--- a/lib/wallets/models/tx_data.dart
+++ b/lib/wallets/models/tx_data.dart
@@ -1,4 +1,3 @@
-import 'package:cs_salvium/cs_salvium.dart' as lib_salvium;
import 'package:tezart/tezart.dart' as tezart;
import 'package:web3dart/web3dart.dart' as web3dart;
@@ -71,7 +70,7 @@ class TxData {
final CsPendingTransaction? pendingTransaction;
// salvium
- final lib_salvium.PendingTransaction? pendingSalviumTransaction;
+ final CsPendingTransaction? pendingSalviumTransaction;
// tezos specific
final tezart.OperationsList? tezosOperationsList;
@@ -260,7 +259,7 @@ class TxData {
int? nonce,
BigInt? chainId,
CsPendingTransaction? pendingTransaction,
- lib_salvium.PendingTransaction? pendingSalviumTransaction,
+ CsPendingTransaction? pendingSalviumTransaction,
int? jMintValue,
List<int>? spendCoinIndexes,
int? height,
diff --git a/lib/wallets/wallet/impl/salvium_wallet.dart b/lib/wallets/wallet/impl/salvium_wallet.dart
index f340776..94aef46 100644
--- a/lib/wallets/wallet/impl/salvium_wallet.dart
+++ b/lib/wallets/wallet/impl/salvium_wallet.dart
@@ -1,48 +1,26 @@
import 'dart:async';
-import 'package:compat/compat.dart' as lib_monero_compat;
-import 'package:cs_salvium/cs_salvium.dart' as lib_salvium;
-
import '../../../utilities/amount/amount.dart';
+import '../../../wl_gen/generated/cs_salvium_interface_impl.dart';
import '../../crypto_currency/crypto_currency.dart';
import '../intermediate/lib_salvium_wallet.dart';
class SalviumWallet extends LibSalviumWallet {
- SalviumWallet(CryptoCurrencyNetwork network)
- : super(Salvium(network));
+ SalviumWallet(CryptoCurrencyNetwork network) : super(Salvium(network));
@override
Future<Amount> estimateFeeFor(Amount amount, BigInt feeRate) async {
- if (libSalviumWallet == null /*||
- syncStatus is! lib_monero_compat.SyncedSyncStatus*/) {
+ if (!csSalvium.walletInstanceExists(walletId) /*||
+ syncStatus is! lib_monero_compat.SyncedSyncStatus*/ ) {
return Amount.zeroWith(fractionDigits: cryptoCurrency.fractionDigits);
}
- lib_salvium.TransactionPriority priority;
- switch (feeRate.toInt()) {
- case 1:
- priority = lib_salvium.TransactionPriority.low;
- break;
- case 2:
- priority = lib_salvium.TransactionPriority.medium;
- break;
- case 3:
- priority = lib_salvium.TransactionPriority.high;
- break;
- case 4:
- priority = lib_salvium.TransactionPriority.last;
- break;
- case 0:
- default:
- priority = lib_salvium.TransactionPriority.normal;
- break;
- }
-
int approximateFee = 0;
await estimateFeeMutex.protect(() async {
- approximateFee = await libSalviumWallet!.estimateFee(
- priority,
- amount.raw.toInt(),
+ approximateFee = await csSalvium.estimateFee(
+ feeRate.toInt(),
+ amount.raw,
+ walletId: walletId,
);
});
@@ -53,72 +31,58 @@ class SalviumWallet extends LibSalviumWallet {
}
@override
- bool walletExists(String path) => lib_salvium.SalviumWallet.isWalletExist(path);
+ bool walletExists(String path) => csSalvium.walletExists(path);
@override
- Future<void> loadWallet({
- required String path,
- required String password,
- }) async {
- libSalviumWallet = await lib_salvium.SalviumWallet.loadWallet(
- path: path,
- password: password,
- );
- }
+ Future<void> loadWallet({required String path, required String password}) =>
+ csSalvium.loadWallet(walletId, path: path, password: password);
@override
- Future<lib_salvium.Wallet> getCreatedWallet({
+ Future<void> getCreatedWallet({
required String path,
required String password,
required int wordCount,
required String seedOffset,
- }) async {
- final lib_salvium.SalviumSeedType type;
- switch (wordCount) {
- case 25:
- type = lib_salvium.SalviumSeedType.twentyFive;
- break;
-
- default:
- throw Exception("Invalid mnemonic word count: $wordCount");
- }
-
- return await lib_salvium.SalviumWallet.create(
- path: path,
- password: password,
- seedType: type,
- seedOffset: seedOffset,
- );
- }
+ required final void Function(int refreshFromBlockHeight, String seed)
+ onCreated,
+ }) => csSalvium.getCreatedWallet(
+ path: path,
+ password: password,
+ wordCount: wordCount,
+ seedOffset: seedOffset,
+ onCreated: onCreated,
+ );
@override
- Future<lib_salvium.Wallet> getRestoredWallet({
+ Future<void> getRestoredWallet({
required String path,
required String password,
required String mnemonic,
required String seedOffset,
int height = 0,
- }) async => await lib_salvium.SalviumWallet.restoreWalletFromSeed(
+ }) async => await csSalvium.getRestoredWallet(
path: path,
password: password,
- seed: mnemonic,
- restoreHeight: height,
+ mnemonic: mnemonic,
+ height: height,
seedOffset: seedOffset,
+ walletId: walletId,
);
@override
- Future<lib_salvium.Wallet> getRestoredFromViewKeyWallet({
+ Future<void> getRestoredFromViewKeyWallet({
required String path,
required String password,
required String address,
required String privateViewKey,
int height = 0,
- }) async => lib_salvium.SalviumWallet.createViewOnlyWallet(
+ }) async => csSalvium.getRestoredFromViewKeyWallet(
+ walletId: walletId,
path: path,
password: password,
address: address,
- viewKey: privateViewKey,
- restoreHeight: height,
+ privateViewKey: privateViewKey,
+ height: height,
);
@override
diff --git a/lib/wallets/wallet/intermediate/lib_monero_wallet.dart b/lib/wallets/wallet/intermediate/lib_monero_wallet.dart
index 943de13..1d8a5da 100644
--- a/lib/wallets/wallet/intermediate/lib_monero_wallet.dart
+++ b/lib/wallets/wallet/intermediate/lib_monero_wallet.dart
@@ -177,7 +177,7 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
!csMonero.hasListeners(walletId)) {
csMonero.addListener(
walletId,
- CsMoneroWalletListener(
+ CsWalletListener(
onSyncingUpdate: onSyncingUpdate,
onNewBlock: onNewBlock,
onBalancesChanged: onBalancesChanged,
diff --git a/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart b/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart
index afc15e5..784e12c 100644
--- a/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart
+++ b/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart
@@ -3,7 +3,6 @@ import 'dart:convert';
import 'dart:io';
import 'dart:math';
-import 'package:cs_salvium/cs_salvium.dart' as lib_salvium;
import 'package:isar_community/isar.dart';
import 'package:mutex/mutex.dart';
import 'package:stack_wallet_backup/generate_password.dart';
@@ -32,6 +31,8 @@ import '../../../utilities/amount/amount.dart';
import '../../../utilities/enums/fee_rate_type_enum.dart';
import '../../../utilities/logger.dart';
import '../../../utilities/stack_file_system.dart';
+import '../../../wl_gen/interfaces/cs_monero_interface.dart';
+import '../../../wl_gen/interfaces/cs_salvium_interface.dart';
import '../../crypto_currency/intermediate/cryptonote_currency.dart';
import '../../isar/models/wallet_info.dart';
import '../../models/tx_data.dart';
@@ -97,14 +98,12 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
await onUTXOsChanged(utxos);
await updateBalance(shouldUpdateUtxos: false);
} catch (e, s) {
- lib_salvium.Logging.log?.i("_startInit", error: e, stackTrace: s);
+ Logging.instance.e("_startInit", error: e, stackTrace: s);
}
});
});
}
- lib_salvium.Wallet? libSalviumWallet;
-
SyncStatus? get syncStatus => _syncStatus;
SyncStatus? _syncStatus;
int _syncedCount = 0;
@@ -134,14 +133,16 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
Future<void> loadWallet({required String path, required String password});
- Future<lib_salvium.Wallet> getCreatedWallet({
+ Future<void> getCreatedWallet({
required String path,
required String password,
required int wordCount,
required String seedOffset,
+ required final void Function(int refreshFromBlockHeight, String seed)
+ onCreated,
});
- Future<lib_salvium.Wallet> getRestoredWallet({
+ Future<void> getRestoredWallet({
required String path,
required String password,
required String mnemonic,
@@ -149,7 +150,7 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
int height = 0,
});
- Future<lib_salvium.Wallet> getRestoredFromViewKeyWallet({
+ Future<void> getRestoredFromViewKeyWallet({
required String path,
required String password,
required String address,
@@ -162,16 +163,18 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
bool walletExists(String path);
String getTxKeyFor({required String txid}) {
- if (libSalviumWallet == null) {
+ if (!csSalvium.walletInstanceExists(walletId)) {
throw Exception("Cannot get tx key in uninitialized libSalviumWallet");
}
- return libSalviumWallet!.getTxKey(txid);
+ return csSalvium.getTxKey(walletId, txid);
}
void _setListener() {
- if (libSalviumWallet != null && libSalviumWallet!.getListeners().isEmpty) {
- libSalviumWallet?.addListener(
- lib_salvium.WalletListener(
+ if (csSalvium.walletInstanceExists(walletId) &&
+ !csSalvium.hasListeners(walletId)) {
+ csSalvium.addListener(
+ walletId,
+ CsWalletListener(
onSyncingUpdate: onSyncingUpdate,
onNewBlock: onNewBlock,
onBalancesChanged: onBalancesChanged,
@@ -187,17 +190,16 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
Future<void> open() async {
bool wasNull = false;
- if (libSalviumWallet == null) {
+ if (!csSalvium.walletInstanceExists(walletId)) {
wasNull = true;
// await libSalviumWallet?.close();
final path = await pathForWallet(name: walletId);
final String password;
try {
- password =
- (await secureStorageInterface.read(
- key: _libSalviumWalletPasswordKey(walletId.toUpperCase()),
- ))!;
+ password = (await secureStorageInterface.read(
+ key: _libSalviumWalletPasswordKey(walletId.toUpperCase()),
+ ))!;
} catch (e, s) {
throw Exception("Password not found $e, $s");
}
@@ -224,15 +226,15 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
if (wasNull) {
try {
_setSyncStatus(ConnectingSyncStatus());
- libSalviumWallet?.startSyncing();
+ csSalvium.startSyncing(walletId);
} catch (_) {
_setSyncStatus(FailedSyncStatus());
// TODO log
}
}
_setListener();
- libSalviumWallet?.startListeners();
- libSalviumWallet?.startAutoSaving();
+ csSalvium.startListeners(walletId);
+ csSalvium.startAutoSaving(walletId);
unawaited(refresh());
}
@@ -242,16 +244,17 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
final appRoot = await StackFileSystem.applicationRootDirectory();
await _backupWalletFiles(name: walletId, appRoot: appRoot);
}
- await libSalviumWallet!.save();
+ await csSalvium.save(walletId);
}
Address addressFor({required int index, int account = 0}) {
- final address = libSalviumWallet!.getAddress(
+ final address = csSalvium.getAddress(
+ walletId,
accountIndex: account,
addressIndex: index,
);
- if (address.value.contains("111")) {
+ if (address.contains("111")) {
throw Exception("111 address found!");
}
@@ -259,7 +262,7 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
walletId: walletId,
derivationIndex: index,
derivationPath: null,
- value: address.value,
+ value: address,
publicKey: [],
type: AddressType.cryptonote,
subType: AddressSubType.receiving,
@@ -269,18 +272,16 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
}
Future<CWKeyData?> getKeys() async {
- final base = libSalviumWallet;
-
- if (base == null) {
+ if (!csSalvium.walletInstanceExists(walletId)) {
return null;
}
try {
return CWKeyData(
walletId: walletId,
- publicViewKey: base.getPublicViewKey(),
- privateViewKey: base.getPrivateViewKey(),
- publicSpendKey: base.getPublicSpendKey(),
- privateSpendKey: base.getPrivateSpendKey(),
+ publicViewKey: csSalvium.getPublicViewKey(walletId),
+ privateViewKey: csSalvium.getPrivateViewKey(walletId),
+ publicSpendKey: csSalvium.getPublicSpendKey(walletId),
+ privateSpendKey: csSalvium.getPrivateSpendKey(walletId),
);
} catch (e, s) {
Logging.instance.f("getKeys failed: ", error: e, stackTrace: s);
@@ -299,16 +300,17 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
final path = await pathForWallet(name: walletId);
final String password;
try {
- password =
- (await secureStorageInterface.read(
- key: _libSalviumWalletPasswordKey(walletId),
- ))!;
+ password = (await secureStorageInterface.read(
+ key: _libSalviumWalletPasswordKey(walletId),
+ ))!;
} catch (e, s) {
throw Exception("Password not found $e, $s");
}
await loadWallet(path: path, password: password);
- final wallet = libSalviumWallet!;
- return (wallet.getAddress().value, wallet.getPrivateViewKey());
+ return (
+ csSalvium.getAddress(walletId),
+ csSalvium.getPrivateViewKey(walletId),
+ );
}
@override
@@ -322,17 +324,23 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
key: _libSalviumWalletPasswordKey(walletId),
value: password,
);
- final wallet = await getCreatedWallet(
+
+ late final int refreshFromBlockHeight;
+ late final String seedPhrase;
+
+ await getCreatedWallet(
path: path,
password: password,
wordCount: wordCount,
seedOffset: "", // default for non restored wallets for now
+ onCreated: (height, seed) {
+ refreshFromBlockHeight = height;
+ seedPhrase = seed;
+ },
);
- final height = wallet.getRefreshFromBlockHeight();
-
await info.updateRestoreHeight(
- newRestoreHeight: height,
+ newRestoreHeight: refreshFromBlockHeight,
isar: mainDB.isar,
);
@@ -340,7 +348,7 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
// before wallet.init() is called
await secureStorageInterface.write(
key: Wallet.mnemonicKey(walletId: walletId),
- value: wallet.getSeed().trim(),
+ value: seedPhrase,
);
await secureStorageInterface.write(
key: Wallet.mnemonicPassphraseKey(walletId: walletId),
@@ -363,8 +371,8 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
await mainDB.deleteWalletBlockchainData(walletId);
highestPercentCached = 0;
- unawaited(libSalviumWallet?.rescanBlockchain());
- libSalviumWallet?.startSyncing();
+ unawaited(csSalvium.rescanBlockchain(walletId));
+ csSalvium.startSyncing(walletId);
// unawaited(save());
});
unawaited(refresh());
@@ -401,7 +409,12 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
key: _libSalviumWalletPasswordKey(walletId),
value: password,
);
- final wallet = await getRestoredWallet(
+
+ if (!csSalvium.walletInstanceExists(walletId)) {
+ await exit();
+ }
+
+ await getRestoredWallet(
path: path,
password: password,
mnemonic: mnemonic,
@@ -409,12 +422,6 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
seedOffset: seedOffset,
);
- if (libSalviumWallet != null) {
- await exit();
- }
-
- libSalviumWallet = wallet;
-
_setListener();
final newReceivingAddress =
@@ -423,7 +430,7 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
walletId: walletId,
derivationIndex: 0,
derivationPath: null,
- value: wallet.getAddress().value,
+ value: csSalvium.getAddress(walletId),
publicKey: [],
type: AddressType.cryptonote,
subType: AddressSubType.receiving,
@@ -442,12 +449,12 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
_setListener();
// libSalviumWallet?.setRecoveringFromSeed(isRecovery: true);
- unawaited(libSalviumWallet?.rescanBlockchain());
- libSalviumWallet?.startSyncing();
+ unawaited(csSalvium.rescanBlockchain(walletId));
+ csSalvium.startSyncing(walletId);
// await save();
- libSalviumWallet?.startListeners();
- libSalviumWallet?.startAutoSaving();
+ csSalvium.startListeners(walletId);
+ csSalvium.startAutoSaving(walletId);
} catch (e, s) {
Logging.instance.e(
"Exception rethrown from recoverFromMnemonic(): ",
@@ -464,7 +471,7 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
@override
Future<bool> pingCheck() async {
if (_canPing) {
- return (await libSalviumWallet?.isConnectedToDaemon()) ?? false;
+ return csSalvium.isConnectedToDaemon(walletId);
} else {
return false;
}
@@ -478,50 +485,50 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
throw Exception("TOR – clearnet mismatch");
}
- final host =
- node.host.endsWith(".onion") ? node.host : Uri.parse(node.host).host;
+ final host = node.host.endsWith(".onion")
+ ? node.host
+ : Uri.parse(node.host).host;
({InternetAddress host, int port})? proxy;
- proxy =
- prefs.useTor && !node.forceNoTor
- ? TorService.sharedInstance.getProxyInfo()
- : null;
+ proxy = prefs.useTor && !node.forceNoTor
+ ? TorService.sharedInstance.getProxyInfo()
+ : null;
_setSyncStatus(ConnectingSyncStatus());
try {
if (_requireMutex) {
await _torConnectingLock.protect(() async {
- await libSalviumWallet?.connect(
+ await csSalvium.connect(
+ walletId,
daemonAddress: "$host:${node.port}",
daemonUsername: node.loginName,
daemonPassword: await node.getPassword(secureStorageInterface),
trusted: node.trusted ?? false,
useSSL: node.useSSL,
- socksProxyAddress:
- node.forceNoTor
- ? null
- : proxy == null
- ? null
- : "${proxy.host.address}:${proxy.port}",
+ socksProxyAddress: node.forceNoTor
+ ? null
+ : proxy == null
+ ? null
+ : "${proxy.host.address}:${proxy.port}",
);
});
} else {
- await libSalviumWallet?.connect(
+ await csSalvium.connect(
+ walletId,
daemonAddress: "$host:${node.port}",
daemonUsername: node.loginName,
daemonPassword: await node.getPassword(secureStorageInterface),
trusted: node.trusted ?? false,
useSSL: node.useSSL,
- socksProxyAddress:
- node.forceNoTor
- ? null
- : proxy == null
- ? null
- : "${proxy.host.address}:${proxy.port}",
+ socksProxyAddress: node.forceNoTor
+ ? null
+ : proxy == null
+ ? null
+ : "${proxy.host.address}:${proxy.port}",
);
}
- libSalviumWallet?.startSyncing();
- libSalviumWallet?.startListeners();
- libSalviumWallet?.startAutoSaving();
+ csSalvium.startSyncing(walletId);
+ csSalvium.startListeners(walletId);
+ csSalvium.startAutoSaving(walletId);
// _setSyncStatus(ConnectedSyncStatus());
} catch (e, s) {
@@ -538,22 +545,19 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
@override
Future<void> updateTransactions() async {
- final base = libSalviumWallet;
-
- if (base == null) {
+ if (!csSalvium.walletInstanceExists(walletId)) {
return;
}
- final localTxids =
- await mainDB.isar.transactionV2s
- .where()
- .walletIdEqualTo(walletId)
- .filter()
- .heightGreaterThan(0)
- .txidProperty()
- .findAll();
+ final localTxids = await mainDB.isar.transactionV2s
+ .where()
+ .walletIdEqualTo(walletId)
+ .filter()
+ .heightGreaterThan(0)
+ .txidProperty()
+ .findAll();
- final allTxids = await base.getAllTxids(refresh: true);
+ final allTxids = await csSalvium.getAllTxids(walletId, refresh: true);
final txidsToFetch = allTxids.toSet().difference(localTxids.toSet());
@@ -561,9 +565,17 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
return;
}
- final transactions = await base.getTxs(txids: txidsToFetch, refresh: false);
+ final transactions = await csSalvium.getTxs(
+ walletId,
+ txids: txidsToFetch,
+ refresh: false,
+ );
- final allOutputs = await base.getOutputs(includeSpent: true, refresh: true);
+ final allOutputs = await csSalvium.getOutputs(
+ walletId,
+ includeSpent: true,
+ refresh: true,
+ );
// final cachedTransactions =
// DB.instance.get<dynamic>(boxName: walletId, key: 'latest_tx_model')
@@ -649,16 +661,14 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
type: type,
subType: TransactionSubType.none,
otherData: jsonEncode({
- TxV2OdKeys.overrideFee:
- Amount(
- rawValue: tx.fee,
- fractionDigits: cryptoCurrency.fractionDigits,
- ).toJsonString(),
- TxV2OdKeys.moneroAmount:
- Amount(
- rawValue: tx.amount,
- fractionDigits: cryptoCurrency.fractionDigits,
- ).toJsonString(),
+ TxV2OdKeys.overrideFee: Amount(
+ rawValue: tx.fee,
+ fractionDigits: cryptoCurrency.fractionDigits,
+ ).toJsonString(),
+ TxV2OdKeys.moneroAmount: Amount(
+ rawValue: tx.amount,
+ fractionDigits: cryptoCurrency.fractionDigits,
+ ).toJsonString(),
TxV2OdKeys.moneroAccountIndex: tx.accountIndex,
TxV2OdKeys.isMoneroTransaction: true,
}),
@@ -673,7 +683,7 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
Future<Amount> get availableBalance async {
try {
return Amount(
- rawValue: libSalviumWallet!.getUnlockedBalance(),
+ rawValue: csSalvium.getUnlockedBalance(walletId)!,
fractionDigits: cryptoCurrency.fractionDigits,
);
} catch (_) {
@@ -683,14 +693,14 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
Future<Amount> get totalBalance async {
try {
- final full = libSalviumWallet?.getBalance();
+ final full = csSalvium.getBalance(walletId);
if (full != null) {
return Amount(
rawValue: full,
fractionDigits: cryptoCurrency.fractionDigits,
);
} else {
- final transactions = await libSalviumWallet!.getAllTxs(refresh: true);
+ final transactions = await csSalvium.getAllTxs(walletId, refresh: true);
BigInt transactionBalance = BigInt.zero;
for (final tx in transactions) {
if (!tx.isSpend) {
@@ -713,10 +723,10 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
@override
Future<void> exit() async {
Logging.instance.i("exit called on $walletId");
- libSalviumWallet?.stopAutoSaving();
- libSalviumWallet?.stopListeners();
- libSalviumWallet?.stopSyncing();
- await libSalviumWallet?.save();
+ csSalvium.stopAutoSaving(walletId);
+ csSalvium.stopListeners(walletId);
+ csSalvium.stopSyncing(walletId);
+ await csSalvium.save(walletId);
}
Future<String> pathForWalletDir({required String name}) async {
@@ -786,7 +796,7 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
final _utxosUpdateLock = Mutex();
Future<void> onUTXOsChanged(List<UTXO> utxos) async {
await _utxosUpdateLock.protect(() async {
- final cwUtxos = await libSalviumWallet?.getOutputs(refresh: true) ?? [];
+ final cwUtxos = await csSalvium.getOutputs(walletId, refresh: true);
// bool changed = false;
@@ -803,12 +813,12 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
if (u.isBlocked) {
if (!cw.isFrozen) {
- await libSalviumWallet?.freezeOutput(cw.keyImage);
+ await csSalvium.freezeOutput(walletId, cw.keyImage);
// changed = true;
}
} else {
if (cw.isFrozen) {
- await libSalviumWallet?.thawOutput(cw.keyImage);
+ await csSalvium.thawOutput(walletId, cw.keyImage);
// changed = true;
}
}
@@ -964,9 +974,9 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
if (mismatch) {
_canPing = false;
- libSalviumWallet?.stopAutoSaving();
- libSalviumWallet?.stopListeners();
- libSalviumWallet?.stopSyncing();
+ csSalvium.stopAutoSaving(walletId);
+ csSalvium.stopListeners(walletId);
+ csSalvium.stopSyncing(walletId);
_setSyncStatus(FailedSyncStatus());
}
@@ -984,27 +994,23 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
@override
Future<bool> updateUTXOs() async {
final List<UTXO> outputArray = [];
- final utxos =
- await libSalviumWallet?.getOutputs(refresh: true) ??
- <lib_salvium.Output>[];
+ final utxos = await csSalvium.getOutputs(walletId, refresh: true);
for (final rawUTXO in utxos) {
if (!rawUTXO.spent) {
- final current =
- await mainDB.isar.utxos
- .where()
- .walletIdEqualTo(walletId)
- .filter()
- .voutEqualTo(rawUTXO.vout)
- .and()
- .txidEqualTo(rawUTXO.hash)
- .findFirst();
- final tx =
- await mainDB.isar.transactionV2s
- .where()
- .walletIdEqualTo(walletId)
- .filter()
- .txidEqualTo(rawUTXO.hash)
- .findFirst();
+ final current = await mainDB.isar.utxos
+ .where()
+ .walletIdEqualTo(walletId)
+ .filter()
+ .voutEqualTo(rawUTXO.vout)
+ .and()
+ .txidEqualTo(rawUTXO.hash)
+ .findFirst();
+ final tx = await mainDB.isar.transactionV2s
+ .where()
+ .walletIdEqualTo(walletId)
+ .filter()
+ .txidEqualTo(rawUTXO.hash)
+ .findFirst();
final otherDataMap = {
UTXOOtherDataKeys.keyImage: rawUTXO.keyImage,
@@ -1077,7 +1083,7 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
// Slight possibility of race but should be irrelevant
await refreshMutex.acquire();
- libSalviumWallet?.startSyncing();
+ csSalvium.startSyncing(walletId);
_setSyncStatus(StartingSyncStatus());
await updateTransactions();
@@ -1091,7 +1097,7 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
refreshMutex.release();
}
- final synced = await libSalviumWallet?.isSynced();
+ final synced = await csSalvium.isSynced(walletId);
if (synced == true) {
_setSyncStatus(SyncedSyncStatus());
@@ -1103,8 +1109,9 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
try {
final currentReceiving = await getCurrentReceivingAddress();
- final newReceivingIndex =
- currentReceiving == null ? 0 : currentReceiving.derivationIndex + 1;
+ final newReceivingIndex = currentReceiving == null
+ ? 0
+ : currentReceiving.derivationIndex + 1;
final newReceivingAddress = addressFor(index: newReceivingIndex);
@@ -1139,17 +1146,15 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
try {
int highestIndex = -1;
- final entries = await libSalviumWallet?.getAllTxs(refresh: true);
- if (entries != null) {
- for (final element in entries) {
- if (!element.isSpend) {
- final int curAddressIndex =
- element.addressIndexes.isEmpty
- ? 0
- : element.addressIndexes.reduce(max);
- if (curAddressIndex > highestIndex) {
- highestIndex = curAddressIndex;
- }
+ final entries = await csSalvium.getAllTxs(walletId, refresh: true);
+
+ for (final element in entries) {
+ if (!element.isSpend) {
+ final int curAddressIndex = element.addressIndexes.isEmpty
+ ? 0
+ : element.addressIndexes.reduce(max);
+ if (curAddressIndex > highestIndex) {
+ highestIndex = curAddressIndex;
}
}
}
@@ -1165,12 +1170,11 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
// Use new index to derive a new receiving address
final newReceivingAddress = addressFor(index: newReceivingIndex);
- final existing =
- await mainDB
- .getAddresses(walletId)
- .filter()
- .valueEqualTo(newReceivingAddress.value)
- .findFirst();
+ final existing = await mainDB
+ .getAddresses(walletId)
+ .filter()
+ .valueEqualTo(newReceivingAddress.value)
+ .findFirst();
if (existing == null) {
// Add that new change address
await mainDB.putAddress(newReceivingAddress);
@@ -1206,9 +1210,9 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
numberOfBlocksFast: 10,
numberOfBlocksAverage: 15,
numberOfBlocksSlow: 20,
- fast: BigInt.from(lib_salvium.TransactionPriority.high.value),
- medium: BigInt.from(lib_salvium.TransactionPriority.medium.value),
- slow: BigInt.from(lib_salvium.TransactionPriority.normal.value),
+ fast: BigInt.from(csSalvium.getTxPriorityHigh()),
+ medium: BigInt.from(csSalvium.getTxPriorityMedium()),
+ slow: BigInt.from(csSalvium.getTxPriorityNormal()),
);
@override
@@ -1234,16 +1238,16 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
try {
final feeRate = txData.feeRateType;
if (feeRate is FeeRateType) {
- lib_salvium.TransactionPriority feePriority;
+ final int feePriority;
switch (feeRate) {
case FeeRateType.fast:
- feePriority = lib_salvium.TransactionPriority.high;
+ feePriority = csSalvium.getTxPriorityHigh();
break;
case FeeRateType.average:
- feePriority = lib_salvium.TransactionPriority.medium;
+ feePriority = csSalvium.getTxPriorityMedium();
break;
case FeeRateType.slow:
- feePriority = lib_salvium.TransactionPriority.normal;
+ feePriority = csSalvium.getTxPriorityNormal();
break;
default:
throw ArgumentError("Invalid use of custom fee");
@@ -1268,12 +1272,9 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
throw Exception("Send all not supported with multiple recipients");
}
- final List<lib_salvium.Recipient> outputs = [];
+ final List<CsRecipient> outputs = [];
for (final recipient in txData.recipients!) {
- final output = lib_salvium.Recipient(
- address: recipient.address,
- amount: recipient.amount.raw,
- );
+ final output = CsRecipient(recipient.address, recipient.amount.raw);
outputs.add(output);
}
@@ -1283,44 +1284,27 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
}
final height = await chainHeight;
- final inputs =
- txData.utxos
- ?.whereType<StandardInput>()
- .map(
- (e) => lib_salvium.Output(
- address: e.utxo.address!,
- hash: e.utxo.txid,
- keyImage: e.utxo.keyImage!,
- value: e.value,
- isFrozen: e.utxo.isBlocked,
- isUnlocked:
- e.utxo.blockHeight != null &&
- (height - (e.utxo.blockHeight ?? 0)) >=
- cryptoCurrency.minConfirms,
- height: e.utxo.blockHeight ?? 0,
- vout: e.utxo.vout,
- spent: e.utxo.used ?? false,
- spentHeight: null, // doesn't matter here
- coinbase: e.utxo.isCoinbase,
- ),
- )
- .toList();
+ final inputs = txData.utxos?.whereType<StandardInput>().toList();
return await prepareSendMutex.protect(() async {
- final lib_salvium.PendingTransaction pendingTransaction;
+ final CsPendingTransaction pendingTransaction;
if (outputs.length == 1) {
- pendingTransaction = await libSalviumWallet!.createTx(
+ pendingTransaction = await csSalvium.createTx(
+ walletId,
output: outputs.first,
- paymentId: "",
+ minConfirms: cryptoCurrency.minConfirms,
+ currentHeight: height,
sweep: sweep,
priority: feePriority,
preferredInputs: inputs,
accountIndex: 0, // sw only uses account 0 at this time
);
} else {
- pendingTransaction = await libSalviumWallet!.createTxMultiDest(
+ pendingTransaction = await csSalvium.createTxMultiDest(
+ walletId,
outputs: outputs,
- paymentId: "",
+ minConfirms: cryptoCurrency.minConfirms,
+ currentHeight: height,
priority: feePriority,
preferredInputs: inputs,
sweep: sweep,
@@ -1363,7 +1347,7 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
Future<TxData> confirmSend({required TxData txData}) async {
try {
try {
- await libSalviumWallet!.commitTx(txData.pendingSalviumTransaction!);
+ await csSalvium.commitTx(walletId, txData.pendingSalviumTransaction!);
Logging.instance.d(
"transaction ${txData.pendingSalviumTransaction!.txid} has been sent",
@@ -1412,7 +1396,12 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
key: _libSalviumWalletPasswordKey(walletId.toUpperCase()),
value: password,
);
- final wallet = await getRestoredFromViewKeyWallet(
+
+ if (csSalvium.walletInstanceExists(walletId)) {
+ await exit();
+ }
+
+ await getRestoredFromViewKeyWallet(
path: path,
password: password,
address: data.address,
@@ -1420,12 +1409,6 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
height: height,
);
- if (libSalviumWallet != null) {
- await exit();
- }
-
- libSalviumWallet = wallet;
-
_setListener();
final newReceivingAddress =
@@ -1434,7 +1417,7 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
walletId: walletId,
derivationIndex: 0,
derivationPath: null,
- value: wallet.getAddress().value,
+ value: csSalvium.getAddress(walletId),
publicKey: [],
type: AddressType.cryptonote,
subType: AddressSubType.receiving,
@@ -1449,12 +1432,12 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
await updateNode();
_setListener();
- unawaited(libSalviumWallet?.rescanBlockchain());
- libSalviumWallet?.startSyncing();
+ unawaited(csSalvium.rescanBlockchain(walletId));
+ csSalvium.startSyncing(walletId);
// await save();
- libSalviumWallet?.startListeners();
- libSalviumWallet?.startAutoSaving();
+ csSalvium.startListeners(walletId);
+ csSalvium.startAutoSaving(walletId);
} catch (e, s) {
Logging.instance.e(
"Exception rethrown from recoverViewOnly(): ",
diff --git a/lib/wl_gen/interfaces/cs_monero_interface.dart b/lib/wl_gen/interfaces/cs_monero_interface.dart
index 7622962..9ed2bb4 100644
--- a/lib/wl_gen/interfaces/cs_monero_interface.dart
+++ b/lib/wl_gen/interfaces/cs_monero_interface.dart
@@ -76,7 +76,7 @@ abstract class CsMoneroInterface {
void stopAutoSaving(String walletId);
bool hasListeners(String walletId);
- void addListener(String walletId, CsMoneroWalletListener listener);
+ void addListener(String walletId, CsWalletListener listener);
void startListeners(String walletId);
void stopListeners(String walletId);
@@ -148,13 +148,17 @@ abstract class CsMoneroInterface {
List<String> getMoneroWordList(String language);
List<String> getWowneroWordList(String language, int seedLength);
+
+ int getHeightByDate(DateTime date, {required CsCoin csCoin});
+
+ bool validateAddress(String address, int network, {required CsCoin csCoin});
}
enum CsCoin { monero, wownero }
// forwarding class
-final class CsMoneroWalletListener {
- CsMoneroWalletListener({
+final class CsWalletListener {
+ CsWalletListener({
this.onSyncingUpdate,
this.onNewBlock,
this.onBalancesChanged,
diff --git a/lib/wl_gen/interfaces/cs_salvium_interface.dart b/lib/wl_gen/interfaces/cs_salvium_interface.dart
new file mode 100644
index 0000000..108ff16
--- /dev/null
+++ b/lib/wl_gen/interfaces/cs_salvium_interface.dart
@@ -0,0 +1,154 @@
+import '../../models/input.dart';
+import 'cs_monero_interface.dart';
+
+export '../generated/cs_salvium_interface_impl.dart';
+
+abstract class CsSalviumInterface {
+ void setUseCsSalviumLoggerInternal(bool enable);
+
+ // tx prio forwarding
+ int getTxPriorityHigh();
+ int getTxPriorityMedium();
+ int getTxPriorityNormal();
+
+ bool walletInstanceExists(String walletId);
+
+ bool walletExists(String path);
+
+ Future<int> estimateFee(int rate, BigInt amount, {required String walletId});
+
+ Future<void> loadWallet(
+ String walletId, {
+
+ required String path,
+ required String password,
+ });
+
+ String getAddress(
+ String walletId, {
+ int accountIndex = 0,
+ int addressIndex = 0,
+ });
+
+ Future<void> getCreatedWallet({
+ required String path,
+ required String password,
+ required int wordCount,
+ required String seedOffset,
+ required final void Function(int refreshFromBlockHeight, String seed)
+ onCreated,
+ });
+
+ Future<void> getRestoredWallet({
+ required String walletId,
+
+ required String path,
+ required String password,
+ required String mnemonic,
+ required String seedOffset,
+ int height = 0,
+ });
+
+ Future<void> getRestoredFromViewKeyWallet({
+ required String walletId,
+
+ required String path,
+ required String password,
+ required String address,
+ required String privateViewKey,
+ int height = 0,
+ });
+
+ String getTxKey(String walletId, String txid);
+
+ Future<void> save(String walletId);
+
+ String getPublicViewKey(String walletId);
+ String getPrivateViewKey(String walletId);
+ String getPublicSpendKey(String walletId);
+ String getPrivateSpendKey(String walletId);
+
+ Future<bool> isSynced(String walletId);
+ void startSyncing(String walletId);
+ void stopSyncing(String walletId);
+
+ void startAutoSaving(String walletId);
+ void stopAutoSaving(String walletId);
+
+ bool hasListeners(String walletId);
+ void addListener(String walletId, CsWalletListener listener);
+ void startListeners(String walletId);
+ void stopListeners(String walletId);
+
+ Future<bool> rescanBlockchain(String walletId);
+ Future<bool> isConnectedToDaemon(String walletId);
+
+ int getRefreshFromBlockHeight(String walletId);
+ void setRefreshFromBlockHeight(String walletId, int height);
+
+ Future<void> connect(
+ String walletId, {
+ required String daemonAddress,
+ required bool trusted,
+ String? daemonUsername,
+ String? daemonPassword,
+ bool useSSL = false,
+ bool isLightWallet = false,
+ String? socksProxyAddress,
+ });
+
+ Future<List<String>> getAllTxids(String walletId, {bool refresh = false});
+
+ BigInt? getBalance(String walletId, {int accountIndex = 0});
+ BigInt? getUnlockedBalance(String walletId, {int accountIndex = 0});
+
+ Future<List<CsTransaction>> getAllTxs(
+ String walletId, {
+ bool refresh = false,
+ });
+
+ Future<List<CsTransaction>> getTxs(
+ String walletId, {
+ required Set<String> txids,
+ bool refresh = false,
+ });
+
+ Future<CsPendingTransaction> createTx(
+ String walletId, {
+ required CsRecipient output,
+ required int priority,
+ required bool sweep,
+ List<StandardInput>? preferredInputs,
+ required int accountIndex,
+ required int minConfirms,
+ required int currentHeight,
+ });
+
+ Future<CsPendingTransaction> createTxMultiDest(
+ String walletId, {
+ required List<CsRecipient> outputs,
+ required int priority,
+ required bool sweep,
+ List<StandardInput>? preferredInputs,
+ required int accountIndex,
+ required int minConfirms,
+ required int currentHeight,
+ });
+
+ Future<void> commitTx(String walletId, CsPendingTransaction tx);
+
+ Future<List<CsOutput>> getOutputs(
+ String walletId, {
+ bool refresh = false,
+ bool includeSpent = false,
+ });
+
+ Future<void> freezeOutput(String walletId, String keyImage);
+ Future<void> thawOutput(String walletId, String keyImage);
+
+ List<String> getSalviumWordList(String language);
+
+ int getHeightByDate(DateTime date);
+
+ bool validateAddress(String address, int network);
+}
diff --git a/tool/wl_templates/SAL_cs_salvium_interface_impl.template.dart b/tool/wl_templates/SAL_cs_salvium_interface_impl.template.dart
new file mode 100644
index 0000000..6d56cb0
--- /dev/null
+++ b/tool/wl_templates/SAL_cs_salvium_interface_impl.template.dart
@@ -0,0 +1,488 @@
+//ON
+import 'package:cs_salvium/cs_salvium.dart' as lib_salvium;
+import 'package:cs_salvium/src/deprecated/get_height_by_date.dart'
+ as cs_salvium_deprecated;
+import 'package:cs_salvium/src/ffi_bindings/salvium_wallet_bindings.dart'
+ as sal_wallet_ffi;
+
+//END_ON
+import '../../models/input.dart';
+import '../interfaces/cs_monero_interface.dart';
+import '../interfaces/cs_salvium_interface.dart';
+
+CsSalviumInterface get csSalvium => _getInterface();
+
+//OFF
+CsSalviumInterface _getInterface() => throw Exception("XMR/WOW not enabled!");
+
+//END_OFF
+//ON
+CsSalviumInterface _getInterface() => _CsSalviumInterfaceImpl();
+
+class _CsSalviumInterfaceImpl extends CsSalviumInterface {
+ final Map<String, lib_salvium.Wallet> _wallets = {};
+
+ @override
+ void setUseCsSalviumLoggerInternal(bool enable) =>
+ lib_salvium.Logging.useLogger = enable;
+
+ @override
+ bool walletInstanceExists(String walletId) => _wallets[walletId] != null;
+
+ @override
+ bool walletExists(String path) =>
+ lib_salvium.SalviumWallet.isWalletExist(path);
+
+ @override
+ Future<int> estimateFee(int rate, BigInt amount, {required String walletId}) {
+ lib_salvium.TransactionPriority priority;
+ switch (rate) {
+ case 1:
+ priority = lib_salvium.TransactionPriority.low;
+ break;
+ case 2:
+ priority = lib_salvium.TransactionPriority.medium;
+ break;
+ case 3:
+ priority = lib_salvium.TransactionPriority.high;
+ break;
+ case 4:
+ priority = lib_salvium.TransactionPriority.last;
+ break;
+ case 0:
+ default:
+ priority = lib_salvium.TransactionPriority.normal;
+ break;
+ }
+
+ return _wallets[walletId]!.estimateFee(priority, amount.toInt());
+ }
+
+ @override
+ Future<void> loadWallet(
+ String walletId, {
+ required String path,
+ required String password,
+ }) async {
+ _wallets[walletId] = await lib_salvium.SalviumWallet.loadWallet(
+ path: path,
+ password: password,
+ );
+ }
+
+ @override
+ int getTxPriorityHigh() => lib_salvium.TransactionPriority.high.value;
+
+ @override
+ int getTxPriorityMedium() => lib_salvium.TransactionPriority.medium.value;
+
+ @override
+ int getTxPriorityNormal() => lib_salvium.TransactionPriority.normal.value;
+
+ @override
+ String getAddress(
+ String walletId, {
+ int accountIndex = 0,
+ int addressIndex = 0,
+ }) => _wallets[walletId]!
+ .getAddress(accountIndex: accountIndex, addressIndex: addressIndex)
+ .value;
+
+ @override
+ Future<void> getCreatedWallet({
+ required String path,
+ required String password,
+ required int wordCount,
+ required String seedOffset,
+ required final void Function(int refreshFromBlockHeight, String seed)
+ onCreated,
+ }) async {
+ final type = switch (wordCount) {
+ 16 => lib_salvium.SalviumSeedType.sixteen,
+ 25 => lib_salvium.SalviumSeedType.twentyFive,
+ _ => throw Exception("Invalid mnemonic word count: $wordCount"),
+ };
+
+ final wallet = await lib_salvium.SalviumWallet.create(
+ path: path,
+ password: password,
+ seedType: type,
+ seedOffset: seedOffset,
+ );
+
+ onCreated(
+ wallet.getRefreshFromBlockHeight(),
+ wallet.getSeed(seedOffset: seedOffset).trim(),
+ );
+ }
+
+ @override
+ Future<void> getRestoredWallet({
+ required String walletId,
+ required String path,
+ required String password,
+ required String mnemonic,
+ required String seedOffset,
+ int height = 0,
+ }) async {
+ _wallets[walletId] = await lib_salvium.SalviumWallet.restoreWalletFromSeed(
+ path: path,
+ password: password,
+ seed: mnemonic,
+ restoreHeight: height,
+ seedOffset: seedOffset,
+ );
+ }
+
+ @override
+ Future<void> getRestoredFromViewKeyWallet({
+ required String walletId,
+ required String path,
+ required String password,
+ required String address,
+ required String privateViewKey,
+ int height = 0,
+ }) async {
+ _wallets[walletId] = await lib_salvium.SalviumWallet.createViewOnlyWallet(
+ path: path,
+ password: password,
+ address: address,
+ viewKey: privateViewKey,
+ restoreHeight: height,
+ );
+ }
+
+ @override
+ String getTxKey(String walletId, String txid) =>
+ _wallets[walletId]!.getTxKey(txid);
+
+ @override
+ Future<void> save(String walletId) => _wallets[walletId]!.save();
+
+ @override
+ String getPublicViewKey(String walletId) =>
+ _wallets[walletId]!.getPublicViewKey();
+
+ @override
+ String getPrivateViewKey(String walletId) =>
+ _wallets[walletId]!.getPrivateViewKey();
+
+ @override
+ String getPublicSpendKey(String walletId) =>
+ _wallets[walletId]!.getPublicSpendKey();
+
+ @override
+ String getPrivateSpendKey(String walletId) =>
+ _wallets[walletId]!.getPrivateSpendKey();
+
+ @override
+ Future<bool> isSynced(String walletId) =>
+ _wallets[walletId]?.isSynced() ?? Future.value(false);
+
+ @override
+ void startSyncing(String walletId) => _wallets[walletId]?.startSyncing();
+
+ @override
+ void stopSyncing(String walletId) => _wallets[walletId]?.stopSyncing();
+
+ @override
+ void startAutoSaving(String walletId) =>
+ _wallets[walletId]?.startAutoSaving();
+
+ @override
+ void stopAutoSaving(String walletId) => _wallets[walletId]?.stopAutoSaving();
+
+ @override
+ bool hasListeners(String walletId) =>
+ _wallets[walletId]!.getListeners().isNotEmpty;
+
+ @override
+ void addListener(String walletId, CsWalletListener listener) =>
+ _wallets[walletId]?.addListener(
+ lib_salvium.WalletListener(
+ onSyncingUpdate: listener.onSyncingUpdate,
+ onNewBlock: listener.onNewBlock,
+ onBalancesChanged: listener.onBalancesChanged,
+ onError: listener.onError,
+ ),
+ );
+
+ @override
+ void startListeners(String walletId) => _wallets[walletId]?.startListeners();
+
+ @override
+ void stopListeners(String walletId) => _wallets[walletId]?.stopListeners();
+
+ @override
+ int getRefreshFromBlockHeight(String walletId) =>
+ _wallets[walletId]!.getRefreshFromBlockHeight();
+
+ @override
+ void setRefreshFromBlockHeight(String walletId, int height) =>
+ _wallets[walletId]!.setRefreshFromBlockHeight(height);
+
+ @override
+ Future<bool> rescanBlockchain(String walletId) =>
+ _wallets[walletId]?.rescanBlockchain() ?? Future.value(false);
+
+ @override
+ Future<bool> isConnectedToDaemon(String walletId) =>
+ _wallets[walletId]?.isConnectedToDaemon() ?? Future.value(false);
+
+ @override
+ Future<void> connect(
+ String walletId, {
+ required String daemonAddress,
+ required bool trusted,
+ String? daemonUsername,
+ String? daemonPassword,
+ bool useSSL = false,
+ bool isLightWallet = false,
+ String? socksProxyAddress,
+ }) async {
+ await _wallets[walletId]?.connect(
+ daemonAddress: daemonAddress,
+ trusted: trusted,
+ daemonUsername: daemonUsername,
+ daemonPassword: daemonPassword,
+ useSSL: useSSL,
+ socksProxyAddress: socksProxyAddress,
+ isLightWallet: isLightWallet,
+ );
+ }
+
+ @override
+ Future<List<String>> getAllTxids(String walletId, {bool refresh = false}) =>
+ _wallets[walletId]!.getAllTxids(refresh: refresh);
+
+ @override
+ BigInt? getBalance(String walletId, {int accountIndex = 0}) =>
+ _wallets[walletId]?.getBalance(accountIndex: accountIndex);
+
+ @override
+ BigInt? getUnlockedBalance(String walletId, {int accountIndex = 0}) =>
+ _wallets[walletId]?.getUnlockedBalance(accountIndex: accountIndex);
+
+ @override
+ Future<List<CsTransaction>> getAllTxs(
+ String walletId, {
+ bool refresh = false,
+ }) async {
+ final transactions = await _wallets[walletId]?.getAllTxs(refresh: refresh);
+ if (transactions == null) return [];
+ return transactions
+ .map(
+ (e) => CsTransaction(
+ displayLabel: e.displayLabel,
+ description: e.description,
+ fee: e.fee,
+ confirmations: e.confirmations,
+ blockHeight: e.blockHeight,
+ accountIndex: e.accountIndex,
+ addressIndexes: e.addressIndexes,
+ paymentId: e.paymentId,
+ amount: e.amount,
+ isSpend: e.isSpend,
+ hash: e.hash,
+ key: e.key,
+ timeStamp: e.timeStamp,
+ minConfirms: e.minConfirms.value,
+ ),
+ )
+ .toList();
+ }
+
+ @override
+ Future<List<CsTransaction>> getTxs(
+ String walletId, {
+ required Set<String> txids,
+ bool refresh = false,
+ }) async {
+ final transactions = await _wallets[walletId]?.getTxs(
+ txids: txids,
+ refresh: refresh,
+ );
+ if (transactions == null) return [];
+ return transactions
+ .map(
+ (e) => CsTransaction(
+ displayLabel: e.displayLabel,
+ description: e.description,
+ fee: e.fee,
+ confirmations: e.confirmations,
+ blockHeight: e.blockHeight,
+ accountIndex: e.accountIndex,
+ addressIndexes: e.addressIndexes,
+ paymentId: e.paymentId,
+ amount: e.amount,
+ isSpend: e.isSpend,
+ hash: e.hash,
+ key: e.key,
+ timeStamp: e.timeStamp,
+ minConfirms: e.minConfirms.value,
+ ),
+ )
+ .toList();
+ }
+
+ @override
+ Future<CsPendingTransaction> createTx(
+ String walletId, {
+ required CsRecipient output,
+ required int priority,
+ required bool sweep,
+ List<StandardInput>? preferredInputs,
+ required int accountIndex,
+ required int minConfirms,
+ required int currentHeight,
+ }) async {
+ final pending = await _wallets[walletId]!.createTx(
+ output: lib_salvium.Recipient(
+ address: output.address,
+ amount: output.amount,
+ ),
+ paymentId: "",
+ sweep: sweep,
+ priority: lib_salvium.TransactionPriority.values.firstWhere(
+ (e) => e.value == priority,
+ ),
+ preferredInputs: preferredInputs
+ ?.map(
+ (e) => lib_salvium.Output(
+ address: e.address!,
+ hash: e.utxo.txid,
+ keyImage: e.utxo.keyImage!,
+ value: e.value,
+ isFrozen: e.utxo.isBlocked,
+ isUnlocked:
+ e.utxo.blockHeight != null &&
+ (currentHeight - (e.utxo.blockHeight ?? 0)) >= minConfirms,
+ height: e.utxo.blockHeight ?? 0,
+ vout: e.utxo.vout,
+ spent: e.utxo.used ?? false,
+ spentHeight: null, // doesn't matter here
+ coinbase: e.utxo.isCoinbase,
+ ),
+ )
+ .toList(),
+ accountIndex: accountIndex,
+ );
+
+ return CsPendingTransaction(
+ pending,
+ pending.amount,
+ pending.fee,
+ pending.txid,
+ );
+ }
+
+ @override
+ Future<CsPendingTransaction> createTxMultiDest(
+ String walletId, {
+ required List<CsRecipient> outputs,
+ required int priority,
+ required bool sweep,
+ List<StandardInput>? preferredInputs,
+ required int accountIndex,
+ required int minConfirms,
+ required int currentHeight,
+ }) async {
+ final pending = await _wallets[walletId]!.createTxMultiDest(
+ outputs: outputs
+ .map(
+ (e) => lib_salvium.Recipient(address: e.address, amount: e.amount),
+ )
+ .toList(),
+ paymentId: "",
+ sweep: sweep,
+ priority: lib_salvium.TransactionPriority.values.firstWhere(
+ (e) => e.value == priority,
+ ),
+ preferredInputs: preferredInputs
+ ?.map(
+ (e) => lib_salvium.Output(
+ address: e.address!,
+ hash: e.utxo.txid,
+ keyImage: e.utxo.keyImage!,
+ value: e.value,
+ isFrozen: e.utxo.isBlocked,
+ isUnlocked:
+ e.utxo.blockHeight != null &&
+ (currentHeight - (e.utxo.blockHeight ?? 0)) >= minConfirms,
+ height: e.utxo.blockHeight ?? 0,
+ vout: e.utxo.vout,
+ spent: e.utxo.used ?? false,
+ spentHeight: null, // doesn't matter here
+ coinbase: e.utxo.isCoinbase,
+ ),
+ )
+ .toList(),
+ accountIndex: accountIndex,
+ );
+
+ return CsPendingTransaction(
+ pending,
+ pending.amount,
+ pending.fee,
+ pending.txid,
+ );
+ }
+
+ @override
+ Future<void> commitTx(String walletId, CsPendingTransaction tx) =>
+ _wallets[walletId]!.commitTx(tx.value as lib_salvium.PendingTransaction);
+
+ @override
+ Future<List<CsOutput>> getOutputs(
+ String walletId, {
+ bool refresh = false,
+ bool includeSpent = false,
+ }) async {
+ final outputs = await _wallets[walletId]?.getOutputs(
+ includeSpent: includeSpent,
+ refresh: refresh,
+ );
+
+ if (outputs == null) return [];
+
+ return outputs
+ .map(
+ (e) => CsOutput(
+ address: e.address,
+ hash: e.hash,
+ keyImage: e.keyImage,
+ value: e.value,
+ isFrozen: e.isFrozen,
+ isUnlocked: e.isUnlocked,
+ height: e.height,
+ spentHeight: e.spentHeight,
+ vout: e.vout,
+ spent: e.spent,
+ coinbase: e.coinbase,
+ ),
+ )
+ .toList();
+ }
+
+ @override
+ Future<void> freezeOutput(String walletId, String keyImage) =>
+ _wallets[walletId]!.freezeOutput(keyImage);
+
+ @override
+ Future<void> thawOutput(String walletId, String keyImage) =>
+ _wallets[walletId]!.thawOutput(keyImage);
+
+ @override
+ List<String> getSalviumWordList(String language) =>
+ lib_salvium.getSalviumWordList(language);
+
+ @override
+ int getHeightByDate(DateTime date) =>
+ cs_salvium_deprecated.getSalviumHeightByDate(date: date);
+
+ @override
+ bool validateAddress(String address, int network) =>
+ sal_wallet_ffi.validateAddress(address, network);
+}
+
+//END_ON
diff --git a/tool/wl_templates/XMR_cs_monero_interface_impl.template.dart b/tool/wl_templates/XMR_cs_monero_interface_impl.template.dart
index 5d6fbfc..3ca3909 100644
--- a/tool/wl_templates/XMR_cs_monero_interface_impl.template.dart
+++ b/tool/wl_templates/XMR_cs_monero_interface_impl.template.dart
@@ -1,5 +1,11 @@
//ON
import 'package:cs_monero/cs_monero.dart' as lib_monero;
+import 'package:cs_monero/src/deprecated/get_height_by_date.dart'
+ as cs_monero_deprecated;
+import 'package:cs_monero/src/ffi_bindings/monero_wallet_bindings.dart'
+ as xmr_wallet_ffi;
+import 'package:cs_monero/src/ffi_bindings/wownero_wallet_bindings.dart'
+ as wow_wallet_ffi;
//END_ON
import '../../models/input.dart';
@@ -250,7 +256,7 @@ class _CsMoneroInterfaceImpl extends CsMoneroInterface {
_wallets[walletId]!.getListeners().isNotEmpty;
@override
- void addListener(String walletId, CsMoneroWalletListener listener) =>
+ void addListener(String walletId, CsWalletListener listener) =>
_wallets[walletId]?.addListener(
lib_monero.WalletListener(
onSyncingUpdate: listener.onSyncingUpdate,
@@ -532,6 +538,22 @@ class _CsMoneroInterfaceImpl extends CsMoneroInterface {
@override
List<String> getWowneroWordList(String language, int seedLength) =>
lib_monero.getWowneroWordList(language, seedWordsLength: seedLength);
+
+ @override
+ int getHeightByDate(DateTime date, {required CsCoin csCoin}) =>
+ switch (csCoin) {
+ CsCoin.monero => cs_monero_deprecated.getMoneroHeightByDate(date: date),
+ CsCoin.wownero => cs_monero_deprecated.getWowneroHeightByDate(
+ date: date,
+ ),
+ };
+
+ @override
+ bool validateAddress(String address, int network, {required CsCoin csCoin}) =>
+ switch (csCoin) {
+ CsCoin.monero => xmr_wallet_ffi.validateAddress(address, network),
+ CsCoin.wownero => wow_wallet_ffi.validateAddress(address, network),
+ };
}
//END_ON
Why this scored 28/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.