From 8f89f19b91c4b624ce175314a727a54d07eb42b5 Mon Sep 17 00:00:00 2001
From: detherminal <76167420+detherminal@users.noreply.github.com>
Date: Fri, 19 May 2023 13:20:16 +0300
Subject: [PATCH 01/22] feat: add nano
---
lib/models/isar/stack_theme.dart | 9 +
lib/models/isar/stack_theme.g.dart | 559 ++++++++++++++++--
.../add_edit_node_view.dart | 1 +
lib/services/coins/coin_service.dart | 10 +
lib/services/coins/nano/nano_wallet.dart | 416 +++++++++++++
lib/themes/coin_icon_provider.dart | 2 +
lib/themes/coin_image_provider.dart | 16 +-
lib/themes/color_theme.dart | 3 +
lib/themes/stack_colors.dart | 2 +
lib/utilities/address_utils.dart | 3 +
lib/utilities/block_explorers.dart | 2 +
lib/utilities/constants.dart | 6 +
lib/utilities/default_nodes.dart | 15 +
lib/utilities/enums/coin_enum.dart | 21 +
.../enums/derive_path_type_enum.dart | 1 +
pubspec.lock | 16 +
pubspec.yaml | 1 +
17 files changed, 1012 insertions(+), 71 deletions(-)
create mode 100644 lib/services/coins/nano/nano_wallet.dart
diff --git a/lib/models/isar/stack_theme.dart b/lib/models/isar/stack_theme.dart
index d91e61cd3..c443d41f7 100644
--- a/lib/models/isar/stack_theme.dart
+++ b/lib/models/isar/stack_theme.dart
@@ -2008,6 +2008,7 @@ class ThemeAssets {
late final String wownero;
late final String namecoin;
late final String particl;
+ late final String nano;
late final String bitcoinImage;
late final String bitcoincashImage;
late final String dogecoinImage;
@@ -2019,6 +2020,7 @@ class ThemeAssets {
late final String wowneroImage;
late final String namecoinImage;
late final String particlImage;
+ late final String nanoImage;
late final String bitcoinImageSecondary;
late final String bitcoincashImageSecondary;
late final String dogecoinImageSecondary;
@@ -2030,6 +2032,7 @@ class ThemeAssets {
late final String wowneroImageSecondary;
late final String namecoinImageSecondary;
late final String particlImageSecondary;
+ late final String nanoImageSecondary;
late final String? loadingGif;
late final String? background;
@@ -2101,6 +2104,8 @@ class ThemeAssets {
"$applicationThemesDirectoryPath/$themeId/assets/${json["namecoin"] as String}"
..particl =
"$applicationThemesDirectoryPath/$themeId/assets/${json["particl"] as String}"
+ ..nano =
+ "$applicationThemesDirectoryPath/$themeId/assets/${json["bitcoin"] as String}" // TODO: Change this to nano
..bitcoinImage =
"$applicationThemesDirectoryPath/$themeId/assets/${json["bitcoin_image"] as String}"
..bitcoincashImage =
@@ -2123,6 +2128,8 @@ class ThemeAssets {
"$applicationThemesDirectoryPath/$themeId/assets/${json["namecoin_image"] as String}"
..particlImage =
"$applicationThemesDirectoryPath/$themeId/assets/${json["particl_image"] as String}"
+ ..nanoImage =
+ "$applicationThemesDirectoryPath/$themeId/assets/${json["bitcoin_image"] as String}" // TODO: Change this to nano
..bitcoinImageSecondary =
"$applicationThemesDirectoryPath/$themeId/assets/${json["bitcoin_image_secondary"] as String}"
..bitcoincashImageSecondary =
@@ -2145,6 +2152,8 @@ class ThemeAssets {
"$applicationThemesDirectoryPath/$themeId/assets/${json["namecoin_image_secondary"] as String}"
..particlImageSecondary =
"$applicationThemesDirectoryPath/$themeId/assets/${json["particl_image_secondary"] as String}"
+ ..nanoImageSecondary =
+ "$applicationThemesDirectoryPath/$themeId/assets/${json["bitcoin_image_secondary"] as String}" // TODO: Change this to nano
..loadingGif = json["loading_gif"] is String
? "$applicationThemesDirectoryPath/$themeId/assets/${json["loading_gif"] as String}"
: null
diff --git a/lib/models/isar/stack_theme.g.dart b/lib/models/isar/stack_theme.g.dart
index 33646c12e..cbde6674d 100644
--- a/lib/models/isar/stack_theme.g.dart
+++ b/lib/models/isar/stack_theme.g.dart
@@ -17807,108 +17807,123 @@ const ThemeAssetsSchema = Schema(
name: r'namecoinImageSecondary',
type: IsarType.string,
),
- r'particl': PropertySchema(
+ r'nano': PropertySchema(
id: 32,
+ name: r'nano',
+ type: IsarType.string,
+ ),
+ r'nanoImage': PropertySchema(
+ id: 33,
+ name: r'nanoImage',
+ type: IsarType.string,
+ ),
+ r'nanoImageSecondary': PropertySchema(
+ id: 34,
+ name: r'nanoImageSecondary',
+ type: IsarType.string,
+ ),
+ r'particl': PropertySchema(
+ id: 35,
name: r'particl',
type: IsarType.string,
),
r'particlImage': PropertySchema(
- id: 33,
+ id: 36,
name: r'particlImage',
type: IsarType.string,
),
r'particlImageSecondary': PropertySchema(
- id: 34,
+ id: 37,
name: r'particlImageSecondary',
type: IsarType.string,
),
r'personaEasy': PropertySchema(
- id: 35,
+ id: 38,
name: r'personaEasy',
type: IsarType.string,
),
r'personaIncognito': PropertySchema(
- id: 36,
+ id: 39,
name: r'personaIncognito',
type: IsarType.string,
),
r'receive': PropertySchema(
- id: 37,
+ id: 40,
name: r'receive',
type: IsarType.string,
),
r'receiveCancelled': PropertySchema(
- id: 38,
+ id: 41,
name: r'receiveCancelled',
type: IsarType.string,
),
r'receivePending': PropertySchema(
- id: 39,
+ id: 42,
name: r'receivePending',
type: IsarType.string,
),
r'send': PropertySchema(
- id: 40,
+ id: 43,
name: r'send',
type: IsarType.string,
),
r'sendCancelled': PropertySchema(
- id: 41,
+ id: 44,
name: r'sendCancelled',
type: IsarType.string,
),
r'sendPending': PropertySchema(
- id: 42,
+ id: 45,
name: r'sendPending',
type: IsarType.string,
),
r'stack': PropertySchema(
- id: 43,
+ id: 46,
name: r'stack',
type: IsarType.string,
),
r'stackIcon': PropertySchema(
- id: 44,
+ id: 47,
name: r'stackIcon',
type: IsarType.string,
),
r'themePreview': PropertySchema(
- id: 45,
+ id: 48,
name: r'themePreview',
type: IsarType.string,
),
r'themeSelector': PropertySchema(
- id: 46,
+ id: 49,
name: r'themeSelector',
type: IsarType.string,
),
r'txExchange': PropertySchema(
- id: 47,
+ id: 50,
name: r'txExchange',
type: IsarType.string,
),
r'txExchangeFailed': PropertySchema(
- id: 48,
+ id: 51,
name: r'txExchangeFailed',
type: IsarType.string,
),
r'txExchangePending': PropertySchema(
- id: 49,
+ id: 52,
name: r'txExchangePending',
type: IsarType.string,
),
r'wownero': PropertySchema(
- id: 50,
+ id: 53,
name: r'wownero',
type: IsarType.string,
),
r'wowneroImage': PropertySchema(
- id: 51,
+ id: 54,
name: r'wowneroImage',
type: IsarType.string,
),
r'wowneroImageSecondary': PropertySchema(
- id: 52,
+ id: 55,
name: r'wowneroImageSecondary',
type: IsarType.string,
)
@@ -17967,6 +17982,9 @@ int _themeAssetsEstimateSize(
bytesCount += 3 + object.namecoin.length * 3;
bytesCount += 3 + object.namecoinImage.length * 3;
bytesCount += 3 + object.namecoinImageSecondary.length * 3;
+ bytesCount += 3 + object.nano.length * 3;
+ bytesCount += 3 + object.nanoImage.length * 3;
+ bytesCount += 3 + object.nanoImageSecondary.length * 3;
bytesCount += 3 + object.particl.length * 3;
bytesCount += 3 + object.particlImage.length * 3;
bytesCount += 3 + object.particlImageSecondary.length * 3;
@@ -18029,27 +18047,30 @@ void _themeAssetsSerialize(
writer.writeString(offsets[29], object.namecoin);
writer.writeString(offsets[30], object.namecoinImage);
writer.writeString(offsets[31], object.namecoinImageSecondary);
- writer.writeString(offsets[32], object.particl);
- writer.writeString(offsets[33], object.particlImage);
- writer.writeString(offsets[34], object.particlImageSecondary);
- writer.writeString(offsets[35], object.personaEasy);
- writer.writeString(offsets[36], object.personaIncognito);
- writer.writeString(offsets[37], object.receive);
- writer.writeString(offsets[38], object.receiveCancelled);
- writer.writeString(offsets[39], object.receivePending);
- writer.writeString(offsets[40], object.send);
- writer.writeString(offsets[41], object.sendCancelled);
- writer.writeString(offsets[42], object.sendPending);
- writer.writeString(offsets[43], object.stack);
- writer.writeString(offsets[44], object.stackIcon);
- writer.writeString(offsets[45], object.themePreview);
- writer.writeString(offsets[46], object.themeSelector);
- writer.writeString(offsets[47], object.txExchange);
- writer.writeString(offsets[48], object.txExchangeFailed);
- writer.writeString(offsets[49], object.txExchangePending);
- writer.writeString(offsets[50], object.wownero);
- writer.writeString(offsets[51], object.wowneroImage);
- writer.writeString(offsets[52], object.wowneroImageSecondary);
+ writer.writeString(offsets[32], object.nano);
+ writer.writeString(offsets[33], object.nanoImage);
+ writer.writeString(offsets[34], object.nanoImageSecondary);
+ writer.writeString(offsets[35], object.particl);
+ writer.writeString(offsets[36], object.particlImage);
+ writer.writeString(offsets[37], object.particlImageSecondary);
+ writer.writeString(offsets[38], object.personaEasy);
+ writer.writeString(offsets[39], object.personaIncognito);
+ writer.writeString(offsets[40], object.receive);
+ writer.writeString(offsets[41], object.receiveCancelled);
+ writer.writeString(offsets[42], object.receivePending);
+ writer.writeString(offsets[43], object.send);
+ writer.writeString(offsets[44], object.sendCancelled);
+ writer.writeString(offsets[45], object.sendPending);
+ writer.writeString(offsets[46], object.stack);
+ writer.writeString(offsets[47], object.stackIcon);
+ writer.writeString(offsets[48], object.themePreview);
+ writer.writeString(offsets[49], object.themeSelector);
+ writer.writeString(offsets[50], object.txExchange);
+ writer.writeString(offsets[51], object.txExchangeFailed);
+ writer.writeString(offsets[52], object.txExchangePending);
+ writer.writeString(offsets[53], object.wownero);
+ writer.writeString(offsets[54], object.wowneroImage);
+ writer.writeString(offsets[55], object.wowneroImageSecondary);
}
ThemeAssets _themeAssetsDeserialize(
@@ -18091,27 +18112,30 @@ ThemeAssets _themeAssetsDeserialize(
object.namecoin = reader.readString(offsets[29]);
object.namecoinImage = reader.readString(offsets[30]);
object.namecoinImageSecondary = reader.readString(offsets[31]);
- object.particl = reader.readString(offsets[32]);
- object.particlImage = reader.readString(offsets[33]);
- object.particlImageSecondary = reader.readString(offsets[34]);
- object.personaEasy = reader.readString(offsets[35]);
- object.personaIncognito = reader.readString(offsets[36]);
- object.receive = reader.readString(offsets[37]);
- object.receiveCancelled = reader.readString(offsets[38]);
- object.receivePending = reader.readString(offsets[39]);
- object.send = reader.readString(offsets[40]);
- object.sendCancelled = reader.readString(offsets[41]);
- object.sendPending = reader.readString(offsets[42]);
- object.stack = reader.readString(offsets[43]);
- object.stackIcon = reader.readString(offsets[44]);
- object.themePreview = reader.readString(offsets[45]);
- object.themeSelector = reader.readString(offsets[46]);
- object.txExchange = reader.readString(offsets[47]);
- object.txExchangeFailed = reader.readString(offsets[48]);
- object.txExchangePending = reader.readString(offsets[49]);
- object.wownero = reader.readString(offsets[50]);
- object.wowneroImage = reader.readString(offsets[51]);
- object.wowneroImageSecondary = reader.readString(offsets[52]);
+ object.nano = reader.readString(offsets[32]);
+ object.nanoImage = reader.readString(offsets[33]);
+ object.nanoImageSecondary = reader.readString(offsets[34]);
+ object.particl = reader.readString(offsets[35]);
+ object.particlImage = reader.readString(offsets[36]);
+ object.particlImageSecondary = reader.readString(offsets[37]);
+ object.personaEasy = reader.readString(offsets[38]);
+ object.personaIncognito = reader.readString(offsets[39]);
+ object.receive = reader.readString(offsets[40]);
+ object.receiveCancelled = reader.readString(offsets[41]);
+ object.receivePending = reader.readString(offsets[42]);
+ object.send = reader.readString(offsets[43]);
+ object.sendCancelled = reader.readString(offsets[44]);
+ object.sendPending = reader.readString(offsets[45]);
+ object.stack = reader.readString(offsets[46]);
+ object.stackIcon = reader.readString(offsets[47]);
+ object.themePreview = reader.readString(offsets[48]);
+ object.themeSelector = reader.readString(offsets[49]);
+ object.txExchange = reader.readString(offsets[50]);
+ object.txExchangeFailed = reader.readString(offsets[51]);
+ object.txExchangePending = reader.readString(offsets[52]);
+ object.wownero = reader.readString(offsets[53]);
+ object.wowneroImage = reader.readString(offsets[54]);
+ object.wowneroImageSecondary = reader.readString(offsets[55]);
return object;
}
@@ -18228,6 +18252,12 @@ P _themeAssetsDeserializeProp
(
return (reader.readString(offset)) as P;
case 52:
return (reader.readString(offset)) as P;
+ case 53:
+ return (reader.readString(offset)) as P;
+ case 54:
+ return (reader.readString(offset)) as P;
+ case 55:
+ return (reader.readString(offset)) as P;
default:
throw IsarError('Unknown property with id $propertyId');
}
@@ -22614,6 +22644,409 @@ extension ThemeAssetsQueryFilter
});
}
+ QueryBuilder nanoEqualTo(
+ String value, {
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.equalTo(
+ property: r'nano',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder nanoGreaterThan(
+ String value, {
+ bool include = false,
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.greaterThan(
+ include: include,
+ property: r'nano',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder nanoLessThan(
+ String value, {
+ bool include = false,
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.lessThan(
+ include: include,
+ property: r'nano',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder nanoBetween(
+ String lower,
+ String upper, {
+ bool includeLower = true,
+ bool includeUpper = true,
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.between(
+ property: r'nano',
+ lower: lower,
+ includeLower: includeLower,
+ upper: upper,
+ includeUpper: includeUpper,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder nanoStartsWith(
+ String value, {
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.startsWith(
+ property: r'nano',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder nanoEndsWith(
+ String value, {
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.endsWith(
+ property: r'nano',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder nanoContains(
+ String value,
+ {bool caseSensitive = true}) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.contains(
+ property: r'nano',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder nanoMatches(
+ String pattern,
+ {bool caseSensitive = true}) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.matches(
+ property: r'nano',
+ wildcard: pattern,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder nanoIsEmpty() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.equalTo(
+ property: r'nano',
+ value: '',
+ ));
+ });
+ }
+
+ QueryBuilder
+ nanoIsNotEmpty() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.greaterThan(
+ property: r'nano',
+ value: '',
+ ));
+ });
+ }
+
+ QueryBuilder
+ nanoImageEqualTo(
+ String value, {
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.equalTo(
+ property: r'nanoImage',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ nanoImageGreaterThan(
+ String value, {
+ bool include = false,
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.greaterThan(
+ include: include,
+ property: r'nanoImage',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ nanoImageLessThan(
+ String value, {
+ bool include = false,
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.lessThan(
+ include: include,
+ property: r'nanoImage',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ nanoImageBetween(
+ String lower,
+ String upper, {
+ bool includeLower = true,
+ bool includeUpper = true,
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.between(
+ property: r'nanoImage',
+ lower: lower,
+ includeLower: includeLower,
+ upper: upper,
+ includeUpper: includeUpper,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ nanoImageStartsWith(
+ String value, {
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.startsWith(
+ property: r'nanoImage',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ nanoImageEndsWith(
+ String value, {
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.endsWith(
+ property: r'nanoImage',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ nanoImageContains(String value, {bool caseSensitive = true}) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.contains(
+ property: r'nanoImage',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ nanoImageMatches(String pattern, {bool caseSensitive = true}) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.matches(
+ property: r'nanoImage',
+ wildcard: pattern,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ nanoImageIsEmpty() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.equalTo(
+ property: r'nanoImage',
+ value: '',
+ ));
+ });
+ }
+
+ QueryBuilder
+ nanoImageIsNotEmpty() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.greaterThan(
+ property: r'nanoImage',
+ value: '',
+ ));
+ });
+ }
+
+ QueryBuilder
+ nanoImageSecondaryEqualTo(
+ String value, {
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.equalTo(
+ property: r'nanoImageSecondary',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ nanoImageSecondaryGreaterThan(
+ String value, {
+ bool include = false,
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.greaterThan(
+ include: include,
+ property: r'nanoImageSecondary',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ nanoImageSecondaryLessThan(
+ String value, {
+ bool include = false,
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.lessThan(
+ include: include,
+ property: r'nanoImageSecondary',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ nanoImageSecondaryBetween(
+ String lower,
+ String upper, {
+ bool includeLower = true,
+ bool includeUpper = true,
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.between(
+ property: r'nanoImageSecondary',
+ lower: lower,
+ includeLower: includeLower,
+ upper: upper,
+ includeUpper: includeUpper,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ nanoImageSecondaryStartsWith(
+ String value, {
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.startsWith(
+ property: r'nanoImageSecondary',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ nanoImageSecondaryEndsWith(
+ String value, {
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.endsWith(
+ property: r'nanoImageSecondary',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ nanoImageSecondaryContains(String value, {bool caseSensitive = true}) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.contains(
+ property: r'nanoImageSecondary',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ nanoImageSecondaryMatches(String pattern, {bool caseSensitive = true}) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.matches(
+ property: r'nanoImageSecondary',
+ wildcard: pattern,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ nanoImageSecondaryIsEmpty() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.equalTo(
+ property: r'nanoImageSecondary',
+ value: '',
+ ));
+ });
+ }
+
+ QueryBuilder
+ nanoImageSecondaryIsNotEmpty() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.greaterThan(
+ property: r'nanoImageSecondary',
+ value: '',
+ ));
+ });
+ }
+
QueryBuilder particlEqualTo(
String value, {
bool caseSensitive = true,
diff --git a/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart b/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart
index 690b54be6..e40bb78f2 100644
--- a/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart
+++ b/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart
@@ -717,6 +717,7 @@ class _NodeFormState extends ConsumerState {
case Coin.firoTestNet:
case Coin.dogecoinTestNet:
case Coin.epicCash:
+ case Coin.nano:
return false;
case Coin.ethereum:
diff --git a/lib/services/coins/coin_service.dart b/lib/services/coins/coin_service.dart
index ff75822fd..e1238e37f 100644
--- a/lib/services/coins/coin_service.dart
+++ b/lib/services/coins/coin_service.dart
@@ -13,6 +13,7 @@ import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/services/coins/litecoin/litecoin_wallet.dart';
import 'package:stackwallet/services/coins/monero/monero_wallet.dart';
import 'package:stackwallet/services/coins/namecoin/namecoin_wallet.dart';
+import 'package:stackwallet/services/coins/nano/nano_wallet.dart';
import 'package:stackwallet/services/coins/particl/particl_wallet.dart';
import 'package:stackwallet/services/coins/wownero/wownero_wallet.dart';
import 'package:stackwallet/services/transaction_notification_tracker.dart';
@@ -223,6 +224,15 @@ abstract class CoinServiceAPI {
client: client,
);
+ case Coin.nano:
+ return NanoWallet(
+ walletId: walletId,
+ walletName: walletName,
+ coin: coin,
+ tracker: tracker,
+ secureStore: secureStorageInterface
+ );
+
case Coin.dogecoinTestNet:
return DogecoinWallet(
walletId: walletId,
diff --git a/lib/services/coins/nano/nano_wallet.dart b/lib/services/coins/nano/nano_wallet.dart
new file mode 100644
index 000000000..38a4c5a98
--- /dev/null
+++ b/lib/services/coins/nano/nano_wallet.dart
@@ -0,0 +1,416 @@
+import 'dart:convert';
+
+import 'package:isar/isar.dart';
+import 'package:nanodart/nanodart.dart';
+import 'package:http/http.dart' as http;
+
+import 'package:stackwallet/models/balance.dart';
+import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart';
+import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart';
+import 'package:stackwallet/models/paymint/fee_object_model.dart';
+import 'package:stackwallet/services/coins/coin_service.dart';
+import 'package:stackwallet/services/mixins/coin_control_interface.dart';
+import 'package:stackwallet/services/mixins/wallet_cache.dart';
+import 'package:stackwallet/services/mixins/wallet_db.dart';
+import 'package:stackwallet/utilities/amount/amount.dart';
+import 'package:stackwallet/utilities/enums/coin_enum.dart';
+
+import '../../../db/isar/main_db.dart';
+import '../../../models/isar/models/blockchain_data/address.dart';
+import '../../../models/node_model.dart';
+import '../../../utilities/default_nodes.dart';
+import '../../../utilities/flutter_secure_storage_interface.dart';
+import '../../../utilities/prefs.dart';
+import '../../node_service.dart';
+import '../../transaction_notification_tracker.dart';
+
+import 'dart:async';
+
+import 'package:stackwallet/models/isar/models/isar_models.dart';
+
+const int MINIMUM_CONFIRMATIONS = 1;
+
+class NanoWallet extends CoinServiceAPI with WalletCache, WalletDB, CoinControlInterface {
+ NanoWallet({
+ required String walletId,
+ required String walletName,
+ required Coin coin,
+ required TransactionNotificationTracker tracker,
+ required SecureStorageInterface secureStore,
+ MainDB? mockableOverride,
+ }) {
+ txTracker = tracker;
+ _walletId = walletId;
+ _walletName = walletName;
+ _coin = coin;
+ _secureStore = secureStore;
+ initCache(walletId, coin);
+ initWalletDB(mockableOverride: mockableOverride);
+ }
+
+ NodeModel? _xnoNode;
+
+ @override
+ Future get mnemonicPassphrase => _secureStore.read(
+ key: '${_walletId}_mnemonicPassphrase',
+ );
+
+ @override
+ Future get mnemonicString =>
+ _secureStore.read(key: '${_walletId}_mnemonic');
+
+ Future getSeedFromMnemonic() async {
+ var mnemonic = await mnemonicString;
+ return NanoMnemomics.mnemonicListToSeed(mnemonic!.split(" "));
+ }
+
+ Future getPrivateKeyFromMnemonic() async {
+ var mnemonic = await mnemonicString;
+ var seed = NanoMnemomics.mnemonicListToSeed(mnemonic!.split(" "));
+ return NanoKeys.seedToPrivate(seed, 0);
+ }
+
+ Future getAddressFromMnemonic() async {
+ var mnemonic = await mnemonicString;
+ var seed = NanoMnemomics.mnemonicListToSeed(mnemonic!.split(' '));
+ var address = NanoAccounts.createAccount(NanoAccountType.NANO, NanoKeys.createPublicKey(NanoKeys.seedToPrivate(seed, 0)));
+ return address;
+ }
+
+ Future getPublicKeyFromMnemonic() async {
+ var mnemonic = await mnemonicString;
+ if (mnemonic == null) {
+ return "";
+ } else {
+ var seed = NanoMnemomics.mnemonicListToSeed(mnemonic.split(" "));
+ return NanoKeys.createPublicKey(NanoKeys.seedToPrivate(seed, 0));
+ }
+ }
+
+ @override
+ String get walletId => _walletId;
+ late String _walletId;
+
+ @override
+ String get walletName => _walletName;
+ late String _walletName;
+
+ @override
+ set walletName(String name) => _walletName = name;
+
+ @override
+ set isFavorite(bool markFavorite) {
+ _isFavorite = markFavorite;
+ updateCachedIsFavorite(markFavorite);
+ }
+
+ @override
+ bool get isFavorite => _isFavorite ??= getCachedIsFavorite();
+ bool? _isFavorite;
+
+ @override
+ Coin get coin => _coin;
+ late Coin _coin;
+
+ late SecureStorageInterface _secureStore;
+ late final TransactionNotificationTracker txTracker;
+ final _prefs = Prefs.instance;
+
+ bool _shouldAutoSync = false;
+
+ @override
+ bool get shouldAutoSync => _shouldAutoSync;
+
+ @override
+ set shouldAutoSync(bool shouldAutoSync) => _shouldAutoSync = shouldAutoSync;
+
+ @override
+ Balance get balance => _balance ??= getCachedBalance();
+ Balance? _balance;
+
+ @override
+ Future confirmSend({required Map txData}) {
+ // TODO: implement confirmSend
+ throw UnimplementedError();
+ }
+
+ @override
+ Future get currentReceivingAddress => getAddressFromMnemonic();
+
+ @override
+ Future estimateFeeFor(Amount amount, int feeRate) {
+ // TODO: implement estimateFeeFor
+ throw UnimplementedError();
+ }
+
+ @override
+ Future exit() async {
+ _hasCalledExit = true;
+ }
+
+ @override
+ // TODO: implement fees
+ Future get fees => throw UnimplementedError();
+
+ Future updateBalance() async {
+ final body = jsonEncode({
+ "action": "account_balance",
+ "account": await getAddressFromMnemonic(),
+ });
+ final headers = {
+ "Content-Type": "application/json",
+ };
+ final response = await http.post(Uri.parse(getCurrentNode().host), headers: headers, body: body);
+ final data = jsonDecode(response.body);
+ _balance = Balance(
+ total: Amount(rawValue: (BigInt.parse(data["balance"].toString()) + BigInt.parse(data["receivable"].toString())) ~/ BigInt.from(10).pow(23), fractionDigits: 7),
+ spendable: Amount(rawValue: BigInt.parse(data["balance"].toString()) ~/ BigInt.from(10).pow(23), fractionDigits: 7),
+ blockedTotal: Amount(rawValue: BigInt.parse("0"), fractionDigits: 30),
+ pendingSpendable: Amount(rawValue: BigInt.parse(data["receivable"].toString()) ~/ BigInt.from(10).pow(23), fractionDigits: 7),
+ );
+ await updateCachedBalance(_balance!);
+ }
+
+ Future confirmAllReceivable() async {
+ // TODO: Implement this function
+ }
+
+ Future updateTransactions() async {
+ await confirmAllReceivable();
+ final response = await http.post(Uri.parse(getCurrentNode().host), headers: {"Content-Type": "application/json"}, body: jsonEncode({"action": "account_history", "account": await getAddressFromMnemonic(), "count": "-1"}));
+ final data = await jsonDecode(response.body);
+ final transactions = data["history"] as List;
+ if (transactions.isEmpty) {
+ return;
+ } else {
+ List transactionList = [];
+ for (var tx in transactions) {
+ var typeString = tx["type"].toString();
+ TransactionType type = TransactionType.unknown;
+ if (typeString == "send") {
+ type = TransactionType.outgoing;
+ } else if (typeString == "receive") {
+ type = TransactionType.incoming;
+ }
+ var intAmount = int.parse((BigInt.parse(tx["amount"].toString()) ~/ BigInt.from(10).pow(23)).toString());
+ var strAmount = jsonEncode({
+ "raw": intAmount.toString(),
+ "fractionDigits": 7,
+ });
+ var transaction = Transaction(
+ walletId: walletId,
+ txid: tx["hash"].toString(),
+ timestamp: int.parse(tx["local_timestamp"].toString()),
+ type: type,
+ subType: TransactionSubType.none,
+ amount: intAmount,
+ amountString: strAmount,
+ fee: 0, // TODO: Use real fee?
+ height: int.parse(tx["height"].toString()),
+ isCancelled: false,
+ isLelantus: false,
+ slateId: "",
+ otherData: "",
+ inputs: [],
+ outputs: [],
+ nonce: 0
+ );
+ transactionList.add(transaction);
+ }
+ await db.putTransactions(transactionList);
+ return;
+ }
+ }
+
+ @override
+ Future fullRescan(int maxUnusedAddressGap, int maxNumberOfIndexesToCheck) async {
+ await _prefs.init();
+ await updateBalance();
+ await updateTransactions();
+ }
+
+ @override
+ Future generateNewAddress() {
+ // TODO: implement generateNewAddress
+ throw UnimplementedError();
+ }
+
+ @override
+ bool get hasCalledExit => _hasCalledExit;
+ bool _hasCalledExit = false;
+
+ @override
+ Future initializeExisting() async {
+ await _prefs.init();
+ }
+
+ @override
+ Future initializeNew() async {
+ if ((await mnemonicString) != null || (await mnemonicPassphrase) != null) {
+ throw Exception(
+ "Attempted to overwrite mnemonic on generate new wallet!");
+ }
+
+ await _prefs.init();
+
+ String seed = NanoSeeds.generateSeed();
+ final mnemonic = NanoMnemomics.seedToMnemonic(seed);
+ await _secureStore.write(
+ key: '${_walletId}_mnemonic',
+ value: mnemonic.join(' '),
+ );
+ await _secureStore.write(
+ key: '${_walletId}_mnemonicPassphrase',
+ value: "",
+ );
+ String privateKey = NanoKeys.seedToPrivate(seed, 0);
+ String publicKey = NanoKeys.createPublicKey(privateKey);
+ String publicAddress = NanoAccounts.createAccount(NanoAccountType.NANO, publicKey);
+
+ final address = Address(
+ walletId: walletId,
+ value: publicAddress,
+ publicKey: [], // TODO: add public key
+ derivationIndex: 0,
+ derivationPath: DerivationPath(),
+ type: AddressType.unknown,
+ subType: AddressSubType.receiving,
+ );
+
+ await db.putAddress(address);
+
+ await Future.wait([
+ updateCachedId(walletId),
+ updateCachedIsFavorite(false)
+ ]);
+ }
+
+ @override
+ bool get isConnected => _isConnected;
+
+ bool _isConnected = false;
+
+ @override
+ bool get isRefreshing => refreshMutex;
+
+ bool refreshMutex = false;
+
+ @override
+ // TODO: implement maxFee
+ Future get maxFee => throw UnimplementedError();
+
+ @override
+ Future> get mnemonic => _getMnemonicList();
+
+ Future> _getMnemonicList() async {
+ final _mnemonicString = await mnemonicString;
+ if (_mnemonicString == null) {
+ return [];
+ }
+ final List data = _mnemonicString.split(' ');
+ return data;
+ }
+
+ @override
+ Future