Minor nano fixes + abstract nano utils out into package (#1250)
* fixes * refactors * fixes * more fixes * fixes for monero.com * update confirmed balance to receivable balance * fix balance display bug * remove print statements * prevent unnecessary writes * review fixes * forgot to add pubspec changes * fix * fix sync status desyncing on rpc fail * fix * update translations + txDetails fixes * squash balance bug * add source address on tx info * fix
Matthew Fosse committed
Jan 11, 2024 at 15:00 UTC
fe2e26f14600b19125a3bce2369fb61a742ceda1
42 files changed
+306
-518
configure_cake_wallet.sh
renamed
+21
-1
@@ -1,4 +1,24 @@
1
-cd scripts/android
1
+IOS="ios"
2
+ANDROID="android"
3
+
4
+PLATFORMS=($IOS $ANDROID)
5
+PLATFORM=$1
6
+
7
+if ! [[ " ${PLATFORMS[*]} " =~ " ${PLATFORM} " ]]; then
8
+ echo "specify platform: ./configure_cake_wallet.sh ios|android"
9
+ exit 1
10
+fi
11
+
12
+if [ "$PLATFORM" == "$IOS" ]; then
13
+ echo "Configuring for iOS"
14
+ cd scripts/ios
15
+fi
16
+
17
+if [ "$PLATFORM" == "$ANDROID" ]; then
18
+ echo "Configuring for Android"
19
+ cd scripts/android
20
+fi
21
+
22
source ./app_env.sh cakewallet
23
./app_config.sh
24
cd ../.. && flutter pub get
cw_core/lib/transaction_info.dart
+1
@@ -15,6 +15,7 @@ abstract class TransactionInfo extends Object with Keyable {
15
String? feeFormatted();
16
void changeFiatAmount(String amount);
17
String? to;
18
+ String? from;
19
20
@override
21
dynamic get keyIndex => id;
cw_nano/lib/banano_balance.dart
+4
-5
@@ -1,20 +1,19 @@
1
import 'package:cw_core/balance.dart';
2
-import 'package:cw_nano/nano_util.dart';
2
+import 'package:nanoutil/nanoutil.dart';
3
4
class BananoBalance extends Balance {
5
final BigInt currentBalance;
6
final BigInt receivableBalance;
7
8
- BananoBalance({required this.currentBalance, required this.receivableBalance}) : super(0, 0) {
9
- }
8
+ BananoBalance({required this.currentBalance, required this.receivableBalance}) : super(0, 0);
9
10
@override
11
String get formattedAvailableBalance {
13
- return NanoUtil.getRawAsUsableString(currentBalance.toString(), NanoUtil.rawPerBanano);
12
+ return NanoAmounts.getRawAsUsableString(currentBalance.toString(), NanoAmounts.rawPerBanano);
13
}
14
15
@override
16
String get formattedAdditionalBalance {
18
- return NanoUtil.getRawAsUsableString(receivableBalance.toString(), NanoUtil.rawPerBanano);
17
+ return NanoAmounts.getRawAsUsableString(receivableBalance.toString(), NanoAmounts.rawPerBanano);
18
}
19
}
cw_nano/lib/nano_balance.dart
+13
-12
@@ -1,34 +1,35 @@
1
import 'package:cw_core/balance.dart';
2
-import 'package:cw_nano/nano_util.dart';
2
+import 'package:nanoutil/nanoutil.dart';
3
4
BigInt stringAmountToBigInt(String amount) {
5
- return BigInt.parse(NanoUtil.getAmountAsRaw(amount, NanoUtil.rawPerNano));
5
+ return BigInt.parse(NanoAmounts.getAmountAsRaw(amount, NanoAmounts.rawPerNano));
6
}
7
8
class NanoBalance extends Balance {
9
final BigInt currentBalance;
10
final BigInt receivableBalance;
11
- late String formattedCurrentBalance;
12
- late String formattedReceivableBalance;
11
14
- NanoBalance({required this.currentBalance, required this.receivableBalance}) : super(0, 0) {
15
- this.formattedCurrentBalance = "";
16
- this.formattedReceivableBalance = "";
17
- }
12
+ NanoBalance({required this.currentBalance, required this.receivableBalance}) : super(0, 0);
13
19
- NanoBalance.fromString(
20
- {required this.formattedCurrentBalance, required this.formattedReceivableBalance})
14
+ NanoBalance.fromFormattedString(
15
+ {required String formattedCurrentBalance, required String formattedReceivableBalance})
16
: currentBalance = stringAmountToBigInt(formattedCurrentBalance),
17
receivableBalance = stringAmountToBigInt(formattedReceivableBalance),
18
super(0, 0);
19
20
+ NanoBalance.fromRawString(
21
+ {required String currentBalance, required String receivableBalance})
22
+ : currentBalance = BigInt.parse(currentBalance),
23
+ receivableBalance = BigInt.parse(receivableBalance),
24
+ super(0, 0);
25
+
26
@override
27
String get formattedAvailableBalance {
27
- return NanoUtil.getRawAsUsableString(currentBalance.toString(), NanoUtil.rawPerNano);
28
+ return NanoAmounts.getRawAsUsableString(currentBalance.toString(), NanoAmounts.rawPerNano);
29
}
30
31
@override
32
String get formattedAdditionalBalance {
32
- return NanoUtil.getRawAsUsableString(receivableBalance.toString(), NanoUtil.rawPerNano);
33
+ return NanoAmounts.getRawAsUsableString(receivableBalance.toString(), NanoAmounts.rawPerNano);
34
}
35
}
cw_nano/lib/nano_client.dart
+9
-2
@@ -4,10 +4,10 @@ import 'dart:convert';
4
import 'package:cw_core/nano_account_info_response.dart';
5
import 'package:cw_nano/nano_balance.dart';
6
import 'package:cw_nano/nano_transaction_model.dart';
7
-import 'package:cw_nano/nano_util.dart';
7
import 'package:http/http.dart' as http;
8
import 'package:nanodart/nanodart.dart';
9
import 'package:cw_core/node.dart';
10
+import 'package:nanoutil/nanoutil.dart';
11
import 'package:shared_preferences/shared_preferences.dart';
12
13
class NanoClient {
@@ -61,6 +61,13 @@ class NanoClient {
61
),
62
);
63
final data = await jsonDecode(response.body);
64
+ if (response.statusCode != 200 ||
65
+ data["error"] != null ||
66
+ data["balance"] == null ||
67
+ data["receivable"] == null) {
68
+ throw Exception(
69
+ "Error while trying to get balance! ${data["error"] != null ? data["error"] : ""}");
70
+ }
71
final String currentBalance = data["balance"] as String;
72
final String receivableBalance = data["receivable"] as String;
73
final BigInt cur = BigInt.parse(currentBalance);
@@ -203,7 +210,7 @@ class NanoClient {
210
String? previousHash,
211
}) async {
212
// our address:
206
- final String publicAddress = NanoUtil.privateKeyToAddress(privateKey);
213
+ final String publicAddress = NanoDerivations.privateKeyToAddress(privateKey);
214
215
// first get the current account balance:
216
if (balanceAfterTx == null) {
cw_nano/lib/nano_transaction_info.dart
+12
-3
@@ -1,7 +1,7 @@
1
import 'package:cw_core/format_amount.dart';
2
import 'package:cw_core/transaction_direction.dart';
3
import 'package:cw_core/transaction_info.dart';
4
-import 'package:cw_nano/nano_util.dart';
4
+import 'package:nanoutil/nanoutil.dart';
5
6
class NanoTransactionInfo extends TransactionInfo {
7
NanoTransactionInfo({
@@ -13,6 +13,8 @@ class NanoTransactionInfo extends TransactionInfo {
13
required this.confirmed,
14
required this.date,
15
required this.confirmations,
16
+ required this.to,
17
+ required this.from,
18
}) : this.amount = amountRaw.toInt();
19
20
final String id;
@@ -24,14 +26,17 @@ class NanoTransactionInfo extends TransactionInfo {
26
final bool confirmed;
27
final int confirmations;
28
final String tokenSymbol;
29
+ final String? to;
30
+ final String? from;
31
String? _fiatAmount;
32
33
bool get isPending => !this.confirmed;
34
35
@override
36
String amountFormatted() {
33
- final String amt = NanoUtil.getRawAsUsableString(amountRaw.toString(), NanoUtil.rawPerNano);
34
- final String acc = NanoUtil.getRawAccuracy(amountRaw.toString(), NanoUtil.rawPerNano);
37
+ final String amt =
38
+ NanoAmounts.getRawAsUsableString(amountRaw.toString(), NanoAmounts.rawPerNano);
39
+ final String acc = NanoAmounts.getRawAccuracy(amountRaw.toString(), NanoAmounts.rawPerNano);
40
return "$acc$amt $tokenSymbol";
41
}
42
@@ -54,6 +59,8 @@ class NanoTransactionInfo extends TransactionInfo {
59
confirmed: data['confirmed'] as bool,
60
confirmations: data['confirmations'] as int,
61
tokenSymbol: data['tokenSymbol'] as String,
62
+ to: data['to'] as String,
63
+ from: data['from'] as String,
64
);
65
}
66
@@ -66,5 +73,7 @@ class NanoTransactionInfo extends TransactionInfo {
73
'confirmed': confirmed,
74
'confirmations': confirmations,
75
'tokenSymbol': tokenSymbol,
76
+ 'to': to,
77
+ 'from': from,
78
};
79
}
cw_nano/lib/nano_util.dart
deleted
-193
@@ -1,193 +0,0 @@
1
-import 'dart:convert';
2
-import 'dart:typed_data';
3
-
4
-import 'package:convert/convert.dart';
5
-import "package:ed25519_hd_key/ed25519_hd_key.dart";
6
-import 'package:libcrypto/libcrypto.dart';
7
-import 'package:nanodart/nanodart.dart';
8
-import 'package:decimal/decimal.dart';
9
-
10
-class NanoUtil {
11
- // standard:
12
- static String seedToPrivate(String seed, int index) {
13
- return NanoKeys.seedToPrivate(seed, index);
14
- }
15
-
16
- static String seedToAddress(String seed, int index) {
17
- return NanoAccounts.createAccount(
18
- NanoAccountType.NANO, privateKeyToPublic(seedToPrivate(seed, index)));
19
- }
20
-
21
- static String seedToMnemonic(String seed) {
22
- return NanoMnemomics.seedToMnemonic(seed).join(" ");
23
- }
24
-
25
- static Future<String> mnemonicToSeed(String mnemonic) async {
26
- return NanoMnemomics.mnemonicListToSeed(mnemonic.split(' '));
27
- }
28
-
29
- static String privateKeyToPublic(String privateKey) {
30
- // return NanoHelpers.byteToHex(Ed25519Blake2b.getPubkey(NanoHelpers.hexToBytes(privateKey))!);
31
- return NanoKeys.createPublicKey(privateKey);
32
- }
33
-
34
- static String addressToPublicKey(String publicAddress) {
35
- return NanoAccounts.extractPublicKey(publicAddress);
36
- }
37
-
38
- // universal:
39
- static String privateKeyToAddress(String privateKey) {
40
- return NanoAccounts.createAccount(NanoAccountType.NANO, privateKeyToPublic(privateKey));
41
- }
42
-
43
- static String publicKeyToAddress(String publicKey) {
44
- return NanoAccounts.createAccount(NanoAccountType.NANO, publicKey);
45
- }
46
-
47
- // standard + hd:
48
- static bool isValidSeed(String seed) {
49
- // Ensure seed is 64 or 128 characters long
50
- if (seed == null || (seed.length != 64 && seed.length != 128)) {
51
- return false;
52
- }
53
- // Ensure seed only contains hex characters, 0-9;A-F
54
- return NanoHelpers.isHexString(seed);
55
- }
56
-
57
- // // hd:
58
- static Future<String> hdMnemonicListToSeed(List<String> words) async {
59
- // if (words.length != 24) {
60
- // throw Exception('Expected a 24-word list, got a ${words.length} list');
61
- // }
62
- final Uint8List salt = Uint8List.fromList(utf8.encode('mnemonic'));
63
- final Pbkdf2 hasher = Pbkdf2(iterations: 2048);
64
- final String seed = await hasher.sha512(words.join(' '), salt);
65
- return seed;
66
- }
67
-
68
- static Future<String> hdSeedToPrivate(String seed, int index) async {
69
- List<int> seedBytes = hex.decode(seed);
70
- KeyData data = await ED25519_HD_KEY.derivePath("m/44'/165'/$index'", seedBytes);
71
- return hex.encode(data.key);
72
- }
73
-
74
- static Future<String> hdSeedToAddress(String seed, int index) async {
75
- return NanoAccounts.createAccount(
76
- NanoAccountType.NANO, privateKeyToPublic(await hdSeedToPrivate(seed, index)));
77
- }
78
-
79
- static Future<String> uniSeedToAddress(String seed, int index, String type) {
80
- if (type == "standard") {
81
- return Future<String>.value(seedToAddress(seed, index));
82
- } else if (type == "hd") {
83
- return hdSeedToAddress(seed, index);
84
- } else {
85
- throw Exception('Unknown seed type');
86
- }
87
- }
88
-
89
- static Future<String> uniSeedToPrivate(String seed, int index, String type) {
90
- if (type == "standard") {
91
- return Future<String>.value(seedToPrivate(seed, index));
92
- } else if (type == "hd") {
93
- return hdSeedToPrivate(seed, index);
94
- } else {
95
- throw Exception('Unknown seed type');
96
- }
97
- }
98
-
99
- static bool isValidBip39Seed(String seed) {
100
- // Ensure seed is 128 characters long
101
- if (seed.length != 128) {
102
- return false;
103
- }
104
- // Ensure seed only contains hex characters, 0-9;A-F
105
- return NanoHelpers.isHexString(seed);
106
- }
107
-
108
- // number util:
109
-
110
- static const int maxDecimalDigits = 6; // Max digits after decimal
111
- static BigInt rawPerNano = BigInt.parse("1000000000000000000000000000000");
112
- static BigInt rawPerNyano = BigInt.parse("1000000000000000000000000");
113
- static BigInt rawPerBanano = BigInt.parse("100000000000000000000000000000");
114
- static BigInt rawPerXMR = BigInt.parse("1000000000000");
115
- static BigInt convertXMRtoNano = BigInt.parse("1000000000000000000");
116
- // static BigInt convertXMRtoNano = BigInt.parse("1000000000000000000000000000");
117
-
118
- /// Convert raw to ban and return as BigDecimal
119
- ///
120
- /// @param raw 100000000000000000000000000000
121
- /// @return Decimal value 1.000000000000000000000000000000
122
- ///
123
- static Decimal getRawAsDecimal(String? raw, BigInt? rawPerCur) {
124
- rawPerCur ??= rawPerNano;
125
- final Decimal amount = Decimal.parse(raw.toString());
126
- final Decimal result = (amount / Decimal.parse(rawPerCur.toString())).toDecimal();
127
- return result;
128
- }
129
-
130
- static String truncateDecimal(Decimal input, {int digits = maxDecimalDigits}) {
131
- Decimal bigger = input.shift(digits);
132
- bigger = bigger.floor(); // chop off the decimal: 1.059 -> 1.05
133
- bigger = bigger.shift(-digits);
134
- return bigger.toString();
135
- }
136
-
137
- /// Return raw as a NANO amount.
138
- ///
139
- /// @param raw 100000000000000000000000000000
140
- /// @returns 1
141
- ///
142
- static String getRawAsUsableString(String? raw, BigInt rawPerCur) {
143
- final String res =
144
- truncateDecimal(getRawAsDecimal(raw, rawPerCur), digits: maxDecimalDigits + 9);
145
-
146
- if (raw == null || raw == "0" || raw == "00000000000000000000000000000000") {
147
- return "0";
148
- }
149
-
150
- if (!res.contains(".")) {
151
- return res;
152
- }
153
-
154
- final String numAmount = res.split(".")[0];
155
- String decAmount = res.split(".")[1];
156
-
157
- // truncate:
158
- if (decAmount.length > maxDecimalDigits) {
159
- decAmount = decAmount.substring(0, maxDecimalDigits);
160
- // remove trailing zeros:
161
- decAmount = decAmount.replaceAllMapped(RegExp(r'0+$'), (Match match) => '');
162
- if (decAmount.isEmpty) {
163
- return numAmount;
164
- }
165
- }
166
-
167
- return "$numAmount.$decAmount";
168
- }
169
-
170
- static String getRawAccuracy(String? raw, BigInt rawPerCur) {
171
- final String rawString = getRawAsUsableString(raw, rawPerCur);
172
- final String rawDecimalString = getRawAsDecimal(raw, rawPerCur).toString();
173
-
174
- if (raw == null || raw.isEmpty || raw == "0") {
175
- return "";
176
- }
177
-
178
- if (rawString != rawDecimalString) {
179
- return "~";
180
- }
181
- return "";
182
- }
183
-
184
- /// Return readable string amount as raw string
185
- /// @param amount 1.01
186
- /// @returns 101000000000000000000000000000
187
- ///
188
- static String getAmountAsRaw(String amount, BigInt rawPerCur) {
189
- final Decimal asDecimal = Decimal.parse(amount);
190
- final Decimal rawDecimal = Decimal.parse(rawPerCur.toString());
191
- return (asDecimal * rawDecimal).toString();
192
- }
193
-}
cw_nano/lib/nano_wallet.dart
+72
-26
@@ -18,7 +18,6 @@ import 'package:cw_nano/nano_client.dart';
18
import 'package:cw_nano/nano_transaction_credentials.dart';
19
import 'package:cw_nano/nano_transaction_history.dart';
20
import 'package:cw_nano/nano_transaction_info.dart';
21
-import 'package:cw_nano/nano_util.dart';
21
import 'package:cw_nano/nano_wallet_keys.dart';
22
import 'package:cw_nano/pending_nano_transaction.dart';
23
import 'package:mobx/mobx.dart';
@@ -27,6 +26,7 @@ import 'package:cw_nano/nano_wallet_addresses.dart';
26
import 'package:cw_core/wallet_base.dart';
27
import 'package:nanodart/nanodart.dart';
28
import 'package:bip39/bip39.dart' as bip39;
29
+import 'package:nanoutil/nanoutil.dart';
30
31
part 'nano_wallet.g.dart';
32
@@ -83,6 +83,8 @@ abstract class NanoWalletBase
83
@observable
84
late ObservableMap<CryptoCurrency, NanoBalance> balance;
85
86
+ static const int POLL_INTERVAL_SECONDS = 10;
87
+
88
// initialize the different forms of private / public key we'll need:
89
Future<void> init() async {
90
if (_derivationType == DerivationType.unknown) {
@@ -100,11 +102,21 @@ abstract class NanoWalletBase
102
if (_derivationType == DerivationType.nano) {
103
_hexSeed = bip39.mnemonicToEntropy(_mnemonic).toUpperCase();
104
} else {
103
- _hexSeed = await NanoUtil.hdMnemonicListToSeed(_mnemonic.split(' '));
105
+ _hexSeed = await NanoDerivations.hdMnemonicListToSeed(_mnemonic.split(' '));
106
}
107
}
106
- _privateKey = await NanoUtil.uniSeedToPrivate(_hexSeed!, 0, type);
107
- _publicAddress = await NanoUtil.uniSeedToAddress(_hexSeed!, 0, type);
108
+ NanoDerivationType derivationType =
109
+ type == "standard" ? NanoDerivationType.STANDARD : NanoDerivationType.HD;
110
+ _privateKey = await NanoDerivations.universalSeedToPrivate(
111
+ _hexSeed!,
112
+ index: 0,
113
+ type: derivationType,
114
+ );
115
+ _publicAddress = await NanoDerivations.universalSeedToAddress(
116
+ _hexSeed!,
117
+ index: 0,
118
+ type: derivationType,
119
+ );
120
this.walletInfo.address = _publicAddress!;
121
122
await walletAddresses.init();
@@ -125,6 +137,7 @@ abstract class NanoWalletBase
137
@override
138
void close() {
139
_client.stop();
140
+ _receiveTimer?.cancel();
141
}
142
143
@action
@@ -139,6 +152,7 @@ abstract class NanoWalletBase
152
153
try {
154
await _updateBalance();
155
+ await updateTransactions();
156
await _updateRep();
157
await _receiveAll();
158
} catch (e) {
@@ -173,8 +187,8 @@ abstract class NanoWalletBase
187
if (txOut.sendAll) {
188
amt = balance[currency]?.currentBalance ?? BigInt.zero;
189
} else {
176
- amt = BigInt.tryParse(NanoUtil.getAmountAsRaw(
177
- txOut.cryptoAmount?.replaceAll(',', '.') ?? "0", NanoUtil.rawPerNano)) ??
190
+ amt = BigInt.tryParse(NanoAmounts.getAmountAsRaw(
191
+ txOut.cryptoAmount?.replaceAll(',', '.') ?? "0", NanoAmounts.rawPerNano)) ??
192
BigInt.zero;
193
}
194
@@ -186,9 +200,7 @@ abstract class NanoWalletBase
200
201
final block = await _client.constructSendBlock(
202
amountRaw: amt.toString(),
189
- destinationAddress: txOut.isParsedAddress
190
- ? txOut.extractedAddress!
191
- : txOut.address,
203
+ destinationAddress: txOut.isParsedAddress ? txOut.extractedAddress! : txOut.address,
204
privateKey: _privateKey!,
205
balanceAfterTx: runningBalance,
206
previousHash: previousHash,
@@ -236,10 +248,10 @@ abstract class NanoWalletBase
248
}
249
}
250
239
- Future<void> updateTransactions() async {
251
+ Future<bool> updateTransactions() async {
252
try {
253
if (_isTransactionUpdating) {
242
- return;
254
+ return false;
255
}
256
257
_isTransactionUpdating = true;
@@ -247,8 +259,10 @@ abstract class NanoWalletBase
259
transactionHistory.addMany(transactions);
260
await transactionHistory.save();
261
_isTransactionUpdating = false;
262
+ return true;
263
} catch (_) {
264
_isTransactionUpdating = false;
265
+ return false;
266
}
267
}
268
@@ -261,16 +275,17 @@ abstract class NanoWalletBase
275
final Map<String, NanoTransactionInfo> result = {};
276
277
for (var transactionModel in transactions) {
278
+ final bool isSend = transactionModel.type == "send";
279
result[transactionModel.hash] = NanoTransactionInfo(
280
id: transactionModel.hash,
281
amountRaw: transactionModel.amount,
282
height: transactionModel.height,
268
- direction: transactionModel.type == "send"
269
- ? TransactionDirection.outgoing
270
- : TransactionDirection.incoming,
283
+ direction: isSend ? TransactionDirection.outgoing : TransactionDirection.incoming,
284
confirmed: transactionModel.confirmed,
285
date: transactionModel.date ?? DateTime.now(),
286
confirmations: transactionModel.confirmed ? 1 : 0,
287
+ to: isSend ? transactionModel.account : address,
288
+ from: isSend ? address : transactionModel.account,
289
);
290
}
291
@@ -312,11 +327,10 @@ abstract class NanoWalletBase
327
Future<void> startSync() async {
328
try {
329
syncStatus = AttemptingSyncStatus();
315
- await _updateBalance();
316
- await updateTransactions();
330
331
+ // setup a timer to receive transactions periodically:
332
_receiveTimer?.cancel();
319
- _receiveTimer = Timer.periodic(const Duration(seconds: 15), (timer) async {
333
+ _receiveTimer = Timer.periodic(const Duration(seconds: POLL_INTERVAL_SECONDS), (timer) async {
334
// get our balance:
335
await _updateBalance();
336
// if we have anything to receive, process it:
@@ -325,6 +339,14 @@ abstract class NanoWalletBase
339
}
340
});
341
342
+ // also run once, immediately:
343
+ await _updateBalance();
344
+ bool updateSuccess = await updateTransactions();
345
+ if (!updateSuccess) {
346
+ syncStatus = FailedSyncStatus();
347
+ return;
348
+ }
349
+
350
syncStatus = SyncedSyncStatus();
351
} catch (e) {
352
print(e);
@@ -353,9 +375,11 @@ abstract class NanoWalletBase
375
376
final data = json.decode(jsonSource) as Map;
377
final mnemonic = data['mnemonic'] as String;
356
- final balance = NanoBalance.fromString(
357
- formattedCurrentBalance: data['currentBalance'] as String? ?? "0",
358
- formattedReceivableBalance: data['receivableBalance'] as String? ?? "0");
378
+
379
+ final balance = NanoBalance.fromRawString(
380
+ currentBalance: data['currentBalance'] as String? ?? "0",
381
+ receivableBalance: data['receivableBalance'] as String? ?? "0",
382
+ );
383
384
DerivationType derivationType = DerivationType.nano;
385
if (data['derivationType'] == "DerivationType.bip39") {
@@ -374,12 +398,26 @@ abstract class NanoWalletBase
398
}
399
400
Future<void> _updateBalance() async {
401
+ var oldBalance = balance[currency];
402
try {
403
balance[currency] = await _client.getBalance(_publicAddress!);
404
} catch (e) {
405
print("Failed to get balance $e");
406
+ // if we don't have a balance, we should at least create one, since it's a late binding
407
+ // otherwise, it's better to just leave it as whatever it was before:
408
+ if (balance[currency] == null) {
409
+ balance[currency] =
410
+ NanoBalance(currentBalance: BigInt.zero, receivableBalance: BigInt.zero);
411
+ }
412
+ }
413
+ // don't save unnecessarily:
414
+ // trying to save too frequently can cause problems with the file system
415
+ // since nano is updated frequently this can be a problem, so we only save if there is a change:
416
+ if (oldBalance == null ||
417
+ balance[currency]!.currentBalance != oldBalance.currentBalance ||
418
+ balance[currency]!.receivableBalance != oldBalance.receivableBalance) {
419
+ await save();
420
}
382
- await save();
421
}
422
423
Future<void> _updateRep() async {
@@ -394,11 +432,19 @@ abstract class NanoWalletBase
432
}
433
434
Future<void> regenerateAddress() async {
397
- final String type = (_derivationType == DerivationType.nano) ? "standard" : "hd";
398
- _privateKey =
399
- await NanoUtil.uniSeedToPrivate(_hexSeed!, this.walletAddresses.account!.id, type);
400
- _publicAddress =
401
- await NanoUtil.uniSeedToAddress(_hexSeed!, this.walletAddresses.account!.id, type);
435
+ final NanoDerivationType type = (_derivationType == DerivationType.nano)
436
+ ? NanoDerivationType.STANDARD
437
+ : NanoDerivationType.HD;
438
+ _privateKey = await NanoDerivations.universalSeedToPrivate(
439
+ _hexSeed!,
440
+ index: this.walletAddresses.account!.id,
441
+ type: type,
442
+ );
443
+ _publicAddress = await NanoDerivations.universalSeedToAddress(
444
+ _hexSeed!,
445
+ index: this.walletAddresses.account!.id,
446
+ type: type,
447
+ );
448
449
this.walletInfo.address = _publicAddress!;
450
this.walletAddresses.address = _publicAddress!;
cw_nano/lib/nano_wallet_service.dart
+3
-3
@@ -6,12 +6,12 @@ import 'package:cw_core/wallet_info.dart';
6
import 'package:cw_core/wallet_service.dart';
7
import 'package:cw_core/wallet_type.dart';
8
import 'package:cw_nano/nano_mnemonic.dart' as nm;
9
-import 'package:cw_nano/nano_util.dart';
9
import 'package:cw_nano/nano_wallet.dart';
10
import 'package:cw_nano/nano_wallet_creation_credentials.dart';
11
import 'package:hive/hive.dart';
12
import 'package:bip39/bip39.dart' as bip39;
13
import 'package:nanodart/nanodart.dart';
14
+import 'package:nanoutil/nanoutil.dart';
15
16
class NanoWalletService extends WalletService<NanoNewWalletCredentials,
17
NanoRestoreWalletFromSeedCredentials, NanoRestoreWalletFromKeysCredentials> {
@@ -30,7 +30,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
30
// nano standard:
31
DerivationType derivationType = DerivationType.nano;
32
String seedKey = NanoSeeds.generateSeed();
33
- String mnemonic = NanoUtil.seedToMnemonic(seedKey);
33
+ String mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey);
34
35
credentials.walletInfo!.derivationType = derivationType;
36
@@ -95,7 +95,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
95
// we can't derive the mnemonic from the key in all cases, only if it's a "nano" seed
96
if (credentials.seedKey.length == 64) {
97
try {
98
- mnemonic = NanoUtil.seedToMnemonic(credentials.seedKey);
98
+ mnemonic = NanoDerivations.standardSeedToMnemonic(credentials.seedKey);
99
} catch (e) {
100
throw Exception("Wasn't a valid nano style seed!");
101
}
cw_nano/lib/pending_nano_transaction.dart
+4
-4
@@ -1,6 +1,6 @@
1
import 'package:cw_core/pending_transaction.dart';
2
import 'package:cw_nano/nano_client.dart';
3
-import 'package:cw_nano/nano_util.dart';
3
+import 'package:nanoutil/nanoutil.dart';
4
5
class PendingNanoTransaction with PendingTransaction {
6
PendingNanoTransaction({
@@ -18,13 +18,13 @@ class PendingNanoTransaction with PendingTransaction {
18
19
@override
20
String get amountFormatted {
21
- final String amt = NanoUtil.getRawAsUsableString(amount.toString(), NanoUtil.rawPerNano);
21
+ final String amt = NanoAmounts.getRawAsUsableString(amount.toString(), NanoAmounts.rawPerNano);
22
return amt;
23
}
24
25
String get accurateAmountFormatted {
26
- final String amt = NanoUtil.getRawAsUsableString(amount.toString(), NanoUtil.rawPerNano);
27
- final String acc = NanoUtil.getRawAccuracy(amount.toString(), NanoUtil.rawPerNano);
26
+ final String amt = NanoAmounts.getRawAsUsableString(amount.toString(), NanoAmounts.rawPerNano);
27
+ final String acc = NanoAmounts.getRawAccuracy(amount.toString(), NanoAmounts.rawPerNano);
28
return "$acc$amt";
29
}
30
cw_nano/pubspec.yaml
+4
@@ -22,6 +22,10 @@ dependencies:
22
hex: ^0.2.0
23
http: ^1.1.0
24
shared_preferences: ^2.0.15
25
+ nanoutil:
26
+ git:
27
+ url: https://github.com/perishllc/nanoutil.git
28
+ ref: c37e72817cf0a28162f43124f79661d6c8e0098f
29
cw_core:
30
path: ../cw_core
31
lib/nano/cw_nano.dart
+19
-197
@@ -173,7 +173,7 @@ class CWNano extends Nano {
173
}
174
175
@override
176
- Future<void> updateTransactions(Object wallet) async {
176
+ Future<bool> updateTransactions(Object wallet) async {
177
return (wallet as NanoWallet).updateTransactions();
178
}
179
@@ -189,116 +189,10 @@ class CWNano extends Nano {
189
}
190
191
class CWNanoUtil extends NanoUtil {
192
- // standard:
193
- @override
194
- String seedToPrivate(String seed, int index) {
195
- return ND.NanoKeys.seedToPrivate(seed, index);
196
- }
197
-
198
- @override
199
- String seedToAddress(String seed, int index) {
200
- return ND.NanoAccounts.createAccount(
201
- ND.NanoAccountType.NANO, privateKeyToPublic(seedToPrivate(seed, index)));
202
- }
203
-
204
- @override
205
- String seedToMnemonic(String seed) {
206
- return NanoMnemomics.seedToMnemonic(seed).join(" ");
207
- }
208
-
209
- @override
210
- Future<String> mnemonicToSeed(String mnemonic) async {
211
- return NanoMnemomics.mnemonicListToSeed(mnemonic.split(' '));
212
- }
213
-
214
- @override
215
- String privateKeyToPublic(String privateKey) {
216
- // return NanoHelpers.byteToHex(Ed25519Blake2b.getPubkey(NanoHelpers.hexToBytes(privateKey))!);
217
- return ND.NanoKeys.createPublicKey(privateKey);
218
- }
219
-
220
- @override
221
- String addressToPublicKey(String publicAddress) {
222
- return ND.NanoAccounts.extractPublicKey(publicAddress);
223
- }
224
-
225
- // universal:
226
- @override
227
- String privateKeyToAddress(String privateKey) {
228
- return ND.NanoAccounts.createAccount(ND.NanoAccountType.NANO, privateKeyToPublic(privateKey));
229
- }
230
-
231
- @override
232
- String publicKeyToAddress(String publicKey) {
233
- return ND.NanoAccounts.createAccount(ND.NanoAccountType.NANO, publicKey);
234
- }
235
-
236
- // standard + hd:
237
- @override
238
- bool isValidSeed(String seed) {
239
- // Ensure seed is 64 or 128 characters long
240
- if (seed.length != 64 && seed.length != 128) {
241
- return false;
242
- }
243
- // Ensure seed only contains hex characters, 0-9;A-F
244
- return ND.NanoHelpers.isHexString(seed);
245
- }
246
-
247
- // hd:
248
- @override
249
- Future<String> hdMnemonicListToSeed(List<String> words) async {
250
- // if (words.length != 24) {
251
- // throw Exception('Expected a 24-word list, got a ${words.length} list');
252
- // }
253
- final Uint8List salt = Uint8List.fromList(utf8.encode('mnemonic'));
254
- final Pbkdf2 hasher = Pbkdf2(iterations: 2048);
255
- final String seed = await hasher.sha512(words.join(' '), salt);
256
- return seed;
257
- }
258
-
259
- @override
260
- Future<String> hdSeedToPrivate(String seed, int index) async {
261
- List<int> seedBytes = hex.decode(seed);
262
- KeyData data = await ED25519_HD_KEY.derivePath("m/44'/165'/$index'", seedBytes);
263
- return hex.encode(data.key);
264
- }
265
-
266
- @override
267
- Future<String> hdSeedToAddress(String seed, int index) async {
268
- return ND.NanoAccounts.createAccount(
269
- ND.NanoAccountType.NANO, privateKeyToPublic(await hdSeedToPrivate(seed, index)));
270
- }
271
-
272
- @override
273
- Future<String> uniSeedToAddress(String seed, int index, String type) {
274
- if (type == "standard") {
275
- return Future<String>.value(seedToAddress(seed, index));
276
- } else if (type == "hd") {
277
- return hdSeedToAddress(seed, index);
278
- } else {
279
- throw Exception('Unknown seed type');
280
- }
281
- }
282
-
283
- @override
284
- Future<String> uniSeedToPrivate(String seed, int index, String type) {
285
- if (type == "standard") {
286
- return Future<String>.value(seedToPrivate(seed, index));
287
- } else if (type == "hd") {
288
- return hdSeedToPrivate(seed, index);
289
- } else {
290
- throw Exception('Unknown seed type');
291
- }
292
- }
192
193
@override
194
bool isValidBip39Seed(String seed) {
296
- // Ensure seed is 128 characters long
297
- if (seed.length != 128) {
298
- return false;
299
- }
300
- // Ensure seed only contains hex characters, 0-9;A-F
301
- return ND.NanoHelpers.isHexString(seed);
195
+ return NanoDerivations.isValidBip39Seed(seed);
196
}
197
198
// number util:
@@ -309,92 +203,20 @@ class CWNanoUtil extends NanoUtil {
203
BigInt rawPerBanano = BigInt.parse("100000000000000000000000000000");
204
BigInt rawPerXMR = BigInt.parse("1000000000000");
205
BigInt convertXMRtoNano = BigInt.parse("1000000000000000000");
312
- // static BigInt convertXMRtoNano = BigInt.parse("1000000000000000000000000000");
313
-
314
- /// Convert raw to ban and return as BigDecimal
315
- ///
316
- /// @param raw 100000000000000000000000000000
317
- /// @return Decimal value 1.000000000000000000000000000000
318
- ///
319
- Decimal _getRawAsDecimal(String? raw, BigInt? rawPerCur) {
320
- rawPerCur ??= rawPerNano;
321
- final Decimal amount = Decimal.parse(raw.toString());
322
- final Decimal result = (amount / Decimal.parse(rawPerCur.toString())).toDecimal();
323
- return result;
324
- }
325
-
326
- @override
327
- String getRawAsDecimalString(String? raw, BigInt? rawPerCur) {
328
- final Decimal result = _getRawAsDecimal(raw, rawPerCur);
329
- return result.toString();
330
- }
206
332
- @override
333
- String truncateDecimal(Decimal input, {int digits = maxDecimalDigits}) {
334
- Decimal bigger = input.shift(digits);
335
- bigger = bigger.floor(); // chop off the decimal: 1.059 -> 1.05
336
- bigger = bigger.shift(-digits);
337
- return bigger.toString();
338
- }
339
-
340
- /// Return raw as a NANO amount.
341
- ///
342
- /// @param raw 100000000000000000000000000000
343
- /// @returns 1
344
- ///
207
@override
208
String getRawAsUsableString(String? raw, BigInt rawPerCur) {
347
- final String res =
348
- truncateDecimal(_getRawAsDecimal(raw, rawPerCur), digits: maxDecimalDigits + 9);
349
-
350
- if (raw == null || raw == "0" || raw == "00000000000000000000000000000000") {
351
- return "0";
352
- }
353
-
354
- if (!res.contains(".")) {
355
- return res;
356
- }
357
-
358
- final String numAmount = res.split(".")[0];
359
- String decAmount = res.split(".")[1];
360
-
361
- // truncate:
362
- if (decAmount.length > maxDecimalDigits) {
363
- decAmount = decAmount.substring(0, maxDecimalDigits);
364
- // remove trailing zeros:
365
- decAmount = decAmount.replaceAllMapped(RegExp(r'0+$'), (Match match) => '');
366
- if (decAmount.isEmpty) {
367
- return numAmount;
368
- }
369
- }
370
-
371
- return "$numAmount.$decAmount";
209
+ return NanoAmounts.getRawAsUsableString(raw, rawPerCur);
210
}
211
212
@override
213
String getRawAccuracy(String? raw, BigInt rawPerCur) {
376
- final String rawString = getRawAsUsableString(raw, rawPerCur);
377
- final String rawDecimalString = _getRawAsDecimal(raw, rawPerCur).toString();
378
-
379
- if (raw == null || raw.isEmpty || raw == "0") {
380
- return "";
381
- }
382
-
383
- if (rawString != rawDecimalString) {
384
- return "~";
385
- }
386
- return "";
214
+ return NanoAmounts.getRawAccuracy(raw, rawPerCur);
215
}
216
389
- /// Return readable string amount as raw string
390
- /// @param amount 1.01
391
- /// @returns 101000000000000000000000000000
392
- ///
217
@override
218
String getAmountAsRaw(String amount, BigInt rawPerCur) {
395
- final Decimal asDecimal = Decimal.parse(amount);
396
- final Decimal rawDecimal = Decimal.parse(rawPerCur.toString());
397
- return (asDecimal * rawDecimal).toString();
219
+ return NanoAmounts.getAmountAsRaw(amount, rawPerCur);
220
}
221
222
@override
@@ -411,29 +233,29 @@ class CWNanoUtil extends NanoUtil {
233
if (seedKey != null) {
234
if (seedKey.length == 64) {
235
try {
414
- mnemonic = nanoUtil!.seedToMnemonic(seedKey);
236
+ mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey);
237
} catch (e) {
238
print("not a valid 'nano' seed key");
239
}
240
}
241
if (derivationType == DerivationType.bip39) {
420
- publicAddress = await hdSeedToAddress(seedKey, 0);
242
+ publicAddress = await NanoDerivations.hdSeedToAddress(seedKey, index: 0);
243
} else if (derivationType == DerivationType.nano) {
422
- publicAddress = await seedToAddress(seedKey, 0);
244
+ publicAddress = await NanoDerivations.standardSeedToAddress(seedKey, index: 0);
245
}
246
}
247
248
if (derivationType == DerivationType.bip39) {
249
if (mnemonic != null) {
428
- seedKey = await hdMnemonicListToSeed(mnemonic.split(' '));
429
- publicAddress = await hdSeedToAddress(seedKey, 0);
250
+ seedKey = await NanoDerivations.hdMnemonicListToSeed(mnemonic.split(' '));
251
+ publicAddress = await NanoDerivations.hdSeedToAddress(seedKey, index: 0);
252
}
253
}
254
255
if (derivationType == DerivationType.nano) {
256
if (mnemonic != null) {
435
- seedKey = await mnemonicToSeed(mnemonic);
436
- publicAddress = await seedToAddress(seedKey, 0);
257
+ seedKey = await NanoDerivations.standardMnemonicToSeed(mnemonic);
258
+ publicAddress = await NanoDerivations.standardSeedToAddress(seedKey, index: 0);
259
}
260
}
261
@@ -461,7 +283,7 @@ class CWNanoUtil extends NanoUtil {
283
return [DerivationType.bip39];
284
} else if (seedKey?.length == 64) {
285
try {
464
- mnemonic = nanoUtil!.seedToMnemonic(seedKey!);
286
+ mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey!);
287
} catch (e) {
288
print("not a valid 'nano' seed key");
289
}
@@ -475,19 +297,19 @@ class CWNanoUtil extends NanoUtil {
297
nanoClient.connect(node);
298
299
if (mnemonic != null) {
478
- seedKey = await hdMnemonicListToSeed(mnemonic.split(' '));
479
- publicAddressBip39 = await hdSeedToAddress(seedKey, 0);
300
+ seedKey = await NanoDerivations.hdMnemonicListToSeed(mnemonic.split(' '));
301
+ publicAddressBip39 = await NanoDerivations.hdSeedToAddress(seedKey, index: 0);
302
481
- seedKey = await mnemonicToSeed(mnemonic);
482
- publicAddressStandard = await seedToAddress(seedKey, 0);
303
+ seedKey = await NanoDerivations.standardMnemonicToSeed(mnemonic);
304
+ publicAddressStandard = await NanoDerivations.standardSeedToAddress(seedKey, index: 0);
305
} else if (seedKey != null) {
306
try {
485
- publicAddressBip39 = await hdSeedToAddress(seedKey, 0);
307
+ publicAddressBip39 = await NanoDerivations.hdSeedToAddress(seedKey, index: 0);
308
} catch (e) {
309
return [DerivationType.nano];
310
}
311
try {
490
- publicAddressStandard = await seedToAddress(seedKey, 0);
312
+ publicAddressStandard = await NanoDerivations.standardSeedToAddress(seedKey, index: 0);
313
} catch (e) {
314
return [DerivationType.bip39];
315
}
lib/view_model/dashboard/balance_view_model.dart
+5
@@ -125,6 +125,8 @@ abstract class BalanceViewModelBase with Store {
125
case WalletType.haven:
126
case WalletType.ethereum:
127
case WalletType.polygon:
128
+ case WalletType.nano:
129
+ case WalletType.banano:
130
return S.current.xmr_available_balance;
131
default:
132
return S.current.confirmed;
@@ -139,6 +141,9 @@ abstract class BalanceViewModelBase with Store {
141
case WalletType.ethereum:
142
case WalletType.polygon:
143
return S.current.xmr_full_balance;
144
+ case WalletType.nano:
145
+ case WalletType.banano:
146
+ return S.current.receivable_balance;
147
default:
148
return S.current.unconfirmed;
149
}
lib/view_model/dashboard/transaction_list_item.dart
+1
-1
@@ -101,7 +101,7 @@ class TransactionListItem extends ActionListItem with Keyable {
101
break;
102
case WalletType.nano:
103
amount = calculateFiatAmountRaw(
104
- cryptoAmount: double.parse(nanoUtil!.getRawAsDecimalString(
104
+ cryptoAmount: double.parse(nanoUtil!.getRawAsUsableString(
105
nano!.getTransactionAmountRaw(transaction).toString(), nanoUtil!.rawPerNano)),
106
price: price);
107
break;
lib/view_model/transaction_details_view_model.dart
+6
-2
@@ -247,11 +247,15 @@ abstract class TransactionDetailsViewModelBase with Store {
247
void _addNanoListItems(TransactionInfo tx, DateFormat dateFormat) {
248
final _items = [
249
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id),
250
+ if (showRecipientAddress && tx.to != null)
251
+ StandartListItem(title: S.current.transaction_details_recipient_address, value: tx.to!),
252
+ if (showRecipientAddress && tx.from != null)
253
+ StandartListItem(title: S.current.transaction_details_source_address, value: tx.from!),
254
+ StandartListItem(title: S.current.transaction_details_amount, value: tx.amountFormatted()),
255
StandartListItem(
256
title: S.current.transaction_details_date, value: dateFormat.format(tx.date)),
252
- StandartListItem(title: S.current.confirmations, value: (tx.confirmations > 0).toString()),
257
+ StandartListItem(title: S.current.confirmed_tx, value: (tx.confirmations > 0).toString()),
258
StandartListItem(title: S.current.transaction_details_height, value: '${tx.height}'),
254
- StandartListItem(title: S.current.transaction_details_amount, value: tx.amountFormatted()),
259
];
260
261
items.addAll(_items);
res/values/strings_ar.arb
+5
-2
@@ -759,5 +759,8 @@
759
"default_sell_provider": " ﻲﺿﺍﺮﺘﻓﻻﺍ ﻊﻴﺒﻟﺍ ﺩﻭﺰﻣ",
760
"select_sell_provider_notice": ".ﻖﻴﺒﻄﺘﻟﺍ ﺕﺍﺩﺍﺪﻋﺇ ﻲﻓ ﻚﺑ ﺹﺎﺨﻟﺍ ﻲﺿﺍﺮﺘﻓﻻﺍ ﻊﻴﺒﻟﺍ ﺩﻭﺰﻣ ﻦﻴﻴﻌﺗ ﻖﻳﺮﻃ ﻦﻋ ﺔﺷﺎﺸﻟﺍ ﻩﺬﻫ ﻲﻄﺨﺗ",
761
"custom_drag": "مخصص (عقد وسحب)",
762
- "switchToEVMCompatibleWallet": " (Ethereum، Polygon) ﻯﺮﺧﺃ ﺓﺮﻣ ﺔﻟﻭﺎﺤﻤﻟﺍﻭ EVM ﻊﻣ ﺔﻘﻓﺍﻮﺘﻣ ﺔﻈﻔﺤﻣ ﻰﻟﺇ ﻞﻳﺪﺒﺘﻟﺍ ﻰﺟﺮﻳ"
763
-}
762
+ "switchToEVMCompatibleWallet": " (Ethereum، Polygon) ﻯﺮﺧﺃ ﺓﺮﻣ ﺔﻟﻭﺎﺤﻤﻟﺍﻭ EVM ﻊﻣ ﺔﻘﻓﺍﻮﺘﻣ ﺔﻈﻔﺤﻣ ﻰﻟﺇ ﻞﻳﺪﺒﺘﻟﺍ ﻰﺟﺮﻳ",
763
+ "receivable_balance": "التوازن القادم",
764
+ "confirmed_tx": "مؤكد",
765
+ "transaction_details_source_address": "عنوان المصدر"
766
+}
\ No newline at end of file
res/values/strings_bg.arb
+5
-2
@@ -755,5 +755,8 @@
755
"default_sell_provider": "Доставчик за продажба по подразбиране",
756
"select_sell_provider_notice": "Изберете доставчик на продажба по-горе. Можете да пропуснете този екран, като зададете своя доставчик на продажба по подразбиране в настройките на приложението.",
757
"custom_drag": "Персонализиране (задръжте и плъзнете)",
758
- "switchToEVMCompatibleWallet": "Моля, превключете към портфейл, съвместим с EVM, и опитайте отново (Ethereum, Polygon)"
759
-}
758
+ "switchToEVMCompatibleWallet": "Моля, превключете към портфейл, съвместим с EVM, и опитайте отново (Ethereum, Polygon)",
759
+ "receivable_balance": "Баланс за вземания",
760
+ "confirmed_tx": "Потвърдено",
761
+ "transaction_details_source_address": "Адрес на източника"
762
+}
\ No newline at end of file
res/values/strings_cs.arb
+5
-2
@@ -755,5 +755,8 @@
755
"default_sell_provider": "Výchozí poskytovatel prodeje",
756
"select_sell_provider_notice": "Výše vyberte poskytovatele prodeje. Tuto obrazovku můžete přeskočit nastavením výchozího poskytovatele prodeje v nastavení aplikace.",
757
"custom_drag": "Custom (Hold and Drag)",
758
- "switchToEVMCompatibleWallet": "Přepněte na peněženku kompatibilní s EVM a zkuste to znovu (Ethereum, Polygon)"
759
-}
758
+ "switchToEVMCompatibleWallet": "Přepněte na peněženku kompatibilní s EVM a zkuste to znovu (Ethereum, Polygon)",
759
+ "receivable_balance": "Zůstatek pohledávek",
760
+ "confirmed_tx": "Potvrzeno",
761
+ "transaction_details_source_address": "Zdrojová adresa"
762
+}
\ No newline at end of file
res/values/strings_de.arb
+5
-2
@@ -763,5 +763,8 @@
763
"default_sell_provider": "Standard-Verkaufsanbieter",
764
"select_sell_provider_notice": "Wählen Sie oben einen Verkaufsanbieter aus. Sie können diesen Bildschirm überspringen, indem Sie in den App-Einstellungen Ihren Standard-Verkaufsanbieter festlegen.",
765
"custom_drag": "Custom (Hold and Drag)",
766
- "switchToEVMCompatibleWallet": "Bitte wechseln Sie zu einem EVM-kompatiblen Wallet und versuchen Sie es erneut (Ethereum, Polygon)"
767
-}
766
+ "switchToEVMCompatibleWallet": "Bitte wechseln Sie zu einem EVM-kompatiblen Wallet und versuchen Sie es erneut (Ethereum, Polygon)",
767
+ "receivable_balance": "Forderungsbilanz",
768
+ "confirmed_tx": "Bestätigt",
769
+ "transaction_details_source_address": "Quelladresse"
770
+}
\ No newline at end of file
res/values/strings_en.arb
+5
-2
@@ -764,5 +764,8 @@
764
"default_sell_provider": "Default Sell Provider",
765
"select_sell_provider_notice": "Select a sell provider above. You can skip this screen by setting your default sell provider in app settings.",
766
"custom_drag": "Custom (Hold and Drag)",
767
- "switchToEVMCompatibleWallet": "Please switch to an EVM compatible wallet and try again (Ethereum, Polygon)"
768
-}
767
+ "switchToEVMCompatibleWallet": "Please switch to an EVM compatible wallet and try again (Ethereum, Polygon)",
768
+ "receivable_balance": "Receivable Balance",
769
+ "confirmed_tx": "Confirmed",
770
+ "transaction_details_source_address": "Source address"
771
+}
\ No newline at end of file
res/values/strings_es.arb
+5
-2
@@ -763,5 +763,8 @@
763
"default_sell_provider": "Proveedor de venta predeterminado",
764
"select_sell_provider_notice": "Seleccione un proveedor de venta arriba. Puede omitir esta pantalla configurando su proveedor de venta predeterminado en la configuración de la aplicación.",
765
"custom_drag": "Custom (mantenía y arrastre)",
766
- "switchToEVMCompatibleWallet": "Cambie a una billetera compatible con EVM e inténtelo nuevamente (Ethereum, Polygon)"
767
-}
766
+ "switchToEVMCompatibleWallet": "Cambie a una billetera compatible con EVM e inténtelo nuevamente (Ethereum, Polygon)",
767
+ "receivable_balance": "Saldo de cuentas por cobrar",
768
+ "confirmed_tx": "Confirmado",
769
+ "transaction_details_source_address": "Dirección de la fuente"
770
+}
\ No newline at end of file
res/values/strings_fr.arb
+5
-2
@@ -763,5 +763,8 @@
763
"default_sell_provider": "Fournisseur de vente par défaut",
764
"select_sell_provider_notice": "Sélectionnez un fournisseur de vente ci-dessus. Vous pouvez ignorer cet écran en définissant votre fournisseur de vente par défaut dans les paramètres de l'application.",
765
"custom_drag": "Custom (maintenir et traîner)",
766
- "switchToEVMCompatibleWallet": "Veuillez passer à un portefeuille compatible EVM et réessayer (Ethereum, Polygon)"
767
-}
766
+ "switchToEVMCompatibleWallet": "Veuillez passer à un portefeuille compatible EVM et réessayer (Ethereum, Polygon)",
767
+ "receivable_balance": "Solde de créances",
768
+ "confirmed_tx": "Confirmé",
769
+ "transaction_details_source_address": "Adresse source"
770
+}
\ No newline at end of file
res/values/strings_ha.arb
+5
-2
@@ -745,5 +745,8 @@
745
"default_sell_provider": "Tsohuwar Mai Bayar Siyarwa",
746
"select_sell_provider_notice": "Zaɓi mai bada siyarwa a sama. Kuna iya tsallake wannan allon ta saita mai bada siyar da ku a cikin saitunan app.",
747
"custom_drag": "Al'ada (riƙe da ja)",
748
- "switchToEVMCompatibleWallet": "Da fatan za a canza zuwa walat ɗin EVM mai jituwa kuma a sake gwadawa (Ethereum, Polygon)"
749
-}
748
+ "switchToEVMCompatibleWallet": "Da fatan za a canza zuwa walat ɗin EVM mai jituwa kuma a sake gwadawa (Ethereum, Polygon)",
749
+ "receivable_balance": "Daidaituwa da daidaituwa",
750
+ "confirmed_tx": "Tabbatar",
751
+ "transaction_details_source_address": "Adireshin Incord"
752
+}
\ No newline at end of file
res/values/strings_hi.arb
+5
-2
@@ -763,5 +763,8 @@
763
"default_sell_provider": "डिफ़ॉल्ट विक्रय प्रदाता",
764
"select_sell_provider_notice": "ऊपर एक विक्रय प्रदाता का चयन करें। आप ऐप सेटिंग में अपना डिफ़ॉल्ट विक्रय प्रदाता सेट करके इस स्क्रीन को छोड़ सकते हैं।",
765
"custom_drag": "कस्टम (पकड़ और खींचें)",
766
- "switchToEVMCompatibleWallet": "कृपया ईवीएम संगत वॉलेट पर स्विच करें और पुनः प्रयास करें (एथेरियम, पॉलीगॉन)"
767
-}
766
+ "switchToEVMCompatibleWallet": "कृपया ईवीएम संगत वॉलेट पर स्विच करें और पुनः प्रयास करें (एथेरियम, पॉलीगॉन)",
767
+ "receivable_balance": "प्राप्य शेष",
768
+ "confirmed_tx": "की पुष्टि",
769
+ "transaction_details_source_address": "स्रोत पता"
770
+}
\ No newline at end of file
res/values/strings_hr.arb
+5
-2
@@ -761,5 +761,8 @@
761
"default_sell_provider": "Zadani dobavljač prodaje",
762
"select_sell_provider_notice": "Gore odaberite pružatelja usluga prodaje. Ovaj zaslon možete preskočiti postavljanjem zadanog pružatelja usluga prodaje u postavkama aplikacije.",
763
"custom_drag": "Prilagođeni (držite i povucite)",
764
- "switchToEVMCompatibleWallet": "Prijeđite na novčanik kompatibilan s EVM-om i pokušajte ponovno (Ethereum, Polygon)"
765
-}
764
+ "switchToEVMCompatibleWallet": "Prijeđite na novčanik kompatibilan s EVM-om i pokušajte ponovno (Ethereum, Polygon)",
765
+ "receivable_balance": "Stanje potraživanja",
766
+ "confirmed_tx": "Potvrđen",
767
+ "transaction_details_source_address": "Adresa izvora"
768
+}
\ No newline at end of file
res/values/strings_id.arb
+5
-2
@@ -751,5 +751,8 @@
751
"default_sell_provider": "Penyedia Penjualan Default",
752
"select_sell_provider_notice": "Pilih penyedia jual di atas. Anda dapat melewati layar ini dengan mengatur penyedia penjualan default Anda di pengaturan aplikasi.",
753
"custom_drag": "Khusus (tahan dan seret)",
754
- "switchToEVMCompatibleWallet": "Silakan beralih ke dompet yang kompatibel dengan EVM dan coba lagi (Ethereum, Polygon)"
755
-}
754
+ "switchToEVMCompatibleWallet": "Silakan beralih ke dompet yang kompatibel dengan EVM dan coba lagi (Ethereum, Polygon)",
755
+ "receivable_balance": "Saldo piutang",
756
+ "confirmed_tx": "Dikonfirmasi",
757
+ "transaction_details_source_address": "Alamat sumber"
758
+}
\ No newline at end of file
res/values/strings_it.arb
+5
-2
@@ -763,5 +763,8 @@
763
"default_sell_provider": "Fornitore di vendita predefinito",
764
"select_sell_provider_notice": "Seleziona un fornitore di vendita sopra. Puoi saltare questa schermata impostando il tuo fornitore di vendita predefinito nelle impostazioni dell'app.",
765
"custom_drag": "Custom (Hold and Drag)",
766
- "switchToEVMCompatibleWallet": "Passa a un portafoglio compatibile con EVM e riprova (Ethereum, Polygon)"
767
-}
766
+ "switchToEVMCompatibleWallet": "Passa a un portafoglio compatibile con EVM e riprova (Ethereum, Polygon)",
767
+ "receivable_balance": "Bilanciamento creditizio",
768
+ "confirmed_tx": "Confermato",
769
+ "transaction_details_source_address": "Indirizzo di partenza"
770
+}
\ No newline at end of file
res/values/strings_ja.arb
+5
-2
@@ -763,5 +763,8 @@
763
"default_sell_provider": "デフォルトの販売プロバイダー",
764
"select_sell_provider_notice": "上記の販売プロバイダーを選択してください。アプリ設定でデフォルトの販売プロバイダーを設定することで、この画面をスキップできます。",
765
"custom_drag": "カスタム(ホールドとドラッグ)",
766
- "switchToEVMCompatibleWallet": "EVM 互換のウォレットに切り替えて再試行してください (イーサリアム、ポリゴン)"
767
-}
766
+ "switchToEVMCompatibleWallet": "EVM 互換のウォレットに切り替えて再試行してください (イーサリアム、ポリゴン)",
767
+ "receivable_balance": "売掛金残高",
768
+ "confirmed_tx": "確認済み",
769
+ "transaction_details_source_address": "ソースアドレス"
770
+}
\ No newline at end of file
res/values/strings_ko.arb
+5
-2
@@ -761,5 +761,8 @@
761
"default_sell_provider": "기본 판매 공급자",
762
"select_sell_provider_notice": "위에서 판매 공급자를 선택하세요. 앱 설정에서 기본 판매 공급자를 설정하면 이 화면을 건너뛸 수 있습니다.",
763
"custom_drag": "사용자 정의 (홀드 앤 드래그)",
764
- "switchToEVMCompatibleWallet": "EVM 호환 지갑으로 전환 후 다시 시도해 주세요. (이더리움, 폴리곤)"
765
-}
764
+ "switchToEVMCompatibleWallet": "EVM 호환 지갑으로 전환 후 다시 시도해 주세요. (이더리움, 폴리곤)",
765
+ "receivable_balance": "채권 잔액",
766
+ "confirmed_tx": "확인",
767
+ "transaction_details_source_address": "소스 주소"
768
+}
\ No newline at end of file
res/values/strings_my.arb
+5
-2
@@ -761,5 +761,8 @@
761
"default_sell_provider": "ပုံသေရောင်းချပေးသူ",
762
"select_sell_provider_notice": "အထက်ဖော်ပြပါ အရောင်းဝန်ဆောင်မှုပေးသူကို ရွေးပါ။ အက်ပ်ဆက်တင်များတွင် သင်၏မူလရောင်းချပေးသူကို သတ်မှတ်ခြင်းဖြင့် ဤစခရင်ကို ကျော်နိုင်သည်။",
763
"custom_drag": "စိတ်ကြိုက် (Drag)",
764
- "switchToEVMCompatibleWallet": "ကျေးဇူးပြု၍ EVM တွဲဖက်သုံးနိုင်သော ပိုက်ဆံအိတ်သို့ ပြောင်းပြီး ထပ်စမ်းကြည့်ပါ (Ethereum၊ Polygon)"
765
-}
764
+ "switchToEVMCompatibleWallet": "ကျေးဇူးပြု၍ EVM တွဲဖက်သုံးနိုင်သော ပိုက်ဆံအိတ်သို့ ပြောင်းပြီး ထပ်စမ်းကြည့်ပါ (Ethereum၊ Polygon)",
765
+ "receivable_balance": "လက်ကျန်ငွေ",
766
+ "confirmed_tx": "အတည်ပြုသည်",
767
+ "transaction_details_source_address": "အရင်းအမြစ်လိပ်စာ"
768
+}
\ No newline at end of file
res/values/strings_nl.arb
+5
-2
@@ -763,5 +763,8 @@
763
"default_sell_provider": "Standaard verkoopaanbieder",
764
"select_sell_provider_notice": "Selecteer hierboven een verkoopaanbieder. U kunt dit scherm overslaan door uw standaardverkoopprovider in te stellen in de app-instellingen.",
765
"custom_drag": "Custom (vasthouden en slepen)",
766
- "switchToEVMCompatibleWallet": "Schakel over naar een EVM-compatibele portemonnee en probeer het opnieuw (Ethereum, Polygon)"
767
-}
766
+ "switchToEVMCompatibleWallet": "Schakel over naar een EVM-compatibele portemonnee en probeer het opnieuw (Ethereum, Polygon)",
767
+ "receivable_balance": "Het saldo",
768
+ "confirmed_tx": "Bevestigd",
769
+ "transaction_details_source_address": "Bron adres"
770
+}
\ No newline at end of file
res/values/strings_pl.arb
+5
-2
@@ -763,5 +763,8 @@
763
"default_sell_provider": "Domyślny dostawca sprzedaży",
764
"select_sell_provider_notice": "Wybierz dostawcę sprzedaży powyżej. Możesz pominąć ten ekran, ustawiając domyślnego dostawcę sprzedaży w ustawieniach aplikacji.",
765
"custom_drag": "Niestandardowe (trzymaj i przeciągnij)",
766
- "switchToEVMCompatibleWallet": "Przejdź na portfel zgodny z EVM i spróbuj ponownie (Ethereum, Polygon)"
767
-}
766
+ "switchToEVMCompatibleWallet": "Przejdź na portfel zgodny z EVM i spróbuj ponownie (Ethereum, Polygon)",
767
+ "receivable_balance": "Saldo należności",
768
+ "confirmed_tx": "Potwierdzony",
769
+ "transaction_details_source_address": "Adres źródłowy"
770
+}
\ No newline at end of file
res/values/strings_pt.arb
+5
-2
@@ -762,5 +762,8 @@
762
"default_sell_provider": "Provedor de venda padrão",
763
"select_sell_provider_notice": "Selecione um fornecedor de venda acima. Você pode pular esta tela definindo seu provedor de venda padrão nas configurações do aplicativo.",
764
"custom_drag": "Personalizado (segure e arraste)",
765
- "switchToEVMCompatibleWallet": "Mude para uma carteira compatível com EVM e tente novamente (Ethereum, Polygon)"
766
-}
765
+ "switchToEVMCompatibleWallet": "Mude para uma carteira compatível com EVM e tente novamente (Ethereum, Polygon)",
766
+ "receivable_balance": "Saldo a receber",
767
+ "confirmed_tx": "Confirmado",
768
+ "transaction_details_source_address": "Endereço de Origem"
769
+}
\ No newline at end of file
res/values/strings_ru.arb
+5
-2
@@ -763,5 +763,8 @@
763
"default_sell_provider": "Поставщик продаж по умолчанию",
764
"select_sell_provider_notice": "Выберите поставщика услуг продажи выше. Вы можете пропустить этот экран, установив поставщика услуг продаж по умолчанию в настройках приложения.",
765
"custom_drag": "Пользователь (удерживайте и перетаскивайте)",
766
- "switchToEVMCompatibleWallet": "Пожалуйста, переключитесь на кошелек, совместимый с EVM, и повторите попытку (Ethereum, Polygon)."
767
-}
766
+ "switchToEVMCompatibleWallet": "Пожалуйста, переключитесь на кошелек, совместимый с EVM, и повторите попытку (Ethereum, Polygon).",
767
+ "receivable_balance": "Баланс дебиторской задолженности",
768
+ "confirmed_tx": "Подтвержденный",
769
+ "transaction_details_source_address": "Адрес источника"
770
+}
\ No newline at end of file
res/values/strings_th.arb
+5
-2
@@ -761,5 +761,8 @@
761
"default_sell_provider": "ผู้ให้บริการการขายเริ่มต้น",
762
"select_sell_provider_notice": "เลือกผู้ให้บริการการขายด้านบน คุณสามารถข้ามหน้าจอนี้ได้โดยการตั้งค่าผู้ให้บริการการขายเริ่มต้นในการตั้งค่าแอป",
763
"custom_drag": "กำหนดเอง (ค้างและลาก)",
764
- "switchToEVMCompatibleWallet": "โปรดเปลี่ยนไปใช้กระเป๋าเงินที่รองรับ EVM แล้วลองอีกครั้ง (Ethereum, Polygon)"
765
-}
764
+ "switchToEVMCompatibleWallet": "โปรดเปลี่ยนไปใช้กระเป๋าเงินที่รองรับ EVM แล้วลองอีกครั้ง (Ethereum, Polygon)",
765
+ "receivable_balance": "ยอดลูกหนี้",
766
+ "confirmed_tx": "ซึ่งยืนยันแล้ว",
767
+ "transaction_details_source_address": "ที่อยู่แหล่งกำเนิด"
768
+}
\ No newline at end of file
res/values/strings_tl.arb
+5
-2
@@ -757,5 +757,8 @@
757
"default_sell_provider": "Default na Sell Provider",
758
"select_sell_provider_notice": "Pumili ng provider ng nagbebenta sa itaas. Maaari mong laktawan ang screen na ito sa pamamagitan ng pagtatakda ng iyong default na sell provider sa mga setting ng app.",
759
"custom_drag": "Pasadyang (hawakan at i -drag)",
760
- "switchToEVMCompatibleWallet": "Mangyaring lumipat sa isang EVM compatible na wallet at subukang muli (Ethereum, Polygon)"
761
-}
760
+ "switchToEVMCompatibleWallet": "Mangyaring lumipat sa isang EVM compatible na wallet at subukang muli (Ethereum, Polygon)",
761
+ "receivable_balance": "Natatanggap na balanse",
762
+ "confirmed_tx": "Nakumpirma",
763
+ "transaction_details_source_address": "SOURCE ADDRESS"
764
+}
\ No newline at end of file
res/values/strings_tr.arb
+5
-2
@@ -761,5 +761,8 @@
761
"default_sell_provider": "Varsayılan Satış Sağlayıcısı",
762
"select_sell_provider_notice": "Yukarıdan bir satış sağlayıcısı seçin. Uygulama ayarlarında varsayılan satış sağlayıcınızı ayarlayarak bu ekranı atlayabilirsiniz.",
763
"custom_drag": "Özel (Bekle ve Sürükle)",
764
- "switchToEVMCompatibleWallet": "Lütfen EVM uyumlu bir cüzdana geçin ve tekrar deneyin (Ethereum, Polygon)"
765
-}
764
+ "switchToEVMCompatibleWallet": "Lütfen EVM uyumlu bir cüzdana geçin ve tekrar deneyin (Ethereum, Polygon)",
765
+ "receivable_balance": "Alacak bakiyesi",
766
+ "confirmed_tx": "Onaylanmış",
767
+ "transaction_details_source_address": "Kaynak adresi"
768
+}
\ No newline at end of file
res/values/strings_uk.arb
+5
-2
@@ -763,5 +763,8 @@
763
"default_sell_provider": "Постачальник продажу за замовчуванням",
764
"select_sell_provider_notice": "Виберіть вище постачальника послуг продажу. Ви можете пропустити цей екран, встановивши постачальника послуг продажу за умовчанням у налаштуваннях програми.",
765
"custom_drag": "На замовлення (утримуйте та перетягується)",
766
- "switchToEVMCompatibleWallet": "Перейдіть на гаманець, сумісний з EVM, і повторіть спробу (Ethereum, Polygon)"
767
-}
766
+ "switchToEVMCompatibleWallet": "Перейдіть на гаманець, сумісний з EVM, і повторіть спробу (Ethereum, Polygon)",
767
+ "receivable_balance": "Баланс дебіторської заборгованості",
768
+ "confirmed_tx": "Підтверджений",
769
+ "transaction_details_source_address": "Адреса джерела"
770
+}
\ No newline at end of file
res/values/strings_ur.arb
+5
-2
@@ -755,5 +755,8 @@
755
"default_sell_provider": " ﮦﺪﻨﻨﮐ ﻢﮨﺍﺮﻓ ﻞﯿﺳ ﭧﻟﺎﻔﯾﮈ",
756
"select_sell_provider_notice": "۔ﮟﯿﮨ ﮯﺘﮑﺳ ﮌﻮﮭﭼ ﻮﮐ ﻦﯾﺮﮑﺳﺍ ﺱﺍ ﺮﮐ ﮮﺩ ﺐﯿﺗﺮﺗ ﻮﮐ ﮦﺪﻨﻨﮐ ﻢﮨﺍﺮﻓ ﻞﯿﺳ ﭧﻟﺎﻔﯾﮈ ﮯﻨﭘﺍ ﮟﯿﻣ ﺕﺎﺒ",
757
"custom_drag": "کسٹم (ہولڈ اینڈ ڈریگ)",
758
- "switchToEVMCompatibleWallet": "(Ethereum, Polygon) ﮟﯾﺮﮐ ﺶﺷﻮﮐ ﮦﺭﺎﺑﻭﺩ ﺭﻭﺍ ﮟﯾﺮﮐ ﭻﺋﻮﺳ ﺮﭘ ﭧﯿﻟﺍﻭ ﮯﻟﺍﻭ ﮯﻨﮭﮐﺭ ﺖﻘﺑﺎﻄﻣ "
759
-}
758
+ "switchToEVMCompatibleWallet": "(Ethereum, Polygon) ﮟﯾﺮﮐ ﺶﺷﻮﮐ ﮦﺭﺎﺑﻭﺩ ﺭﻭﺍ ﮟﯾﺮﮐ ﭻﺋﻮﺳ ﺮﭘ ﭧﯿﻟﺍﻭ ﮯﻟﺍﻭ ﮯﻨﮭﮐﺭ ﺖﻘﺑﺎﻄﻣ ",
759
+ "receivable_balance": "قابل وصول توازن",
760
+ "confirmed_tx": "تصدیق",
761
+ "transaction_details_source_address": "ماخذ ایڈریس"
762
+}
\ No newline at end of file
res/values/strings_yo.arb
+5
-2
@@ -757,5 +757,8 @@
757
"default_sell_provider": "Aiyipada Olupese Tita",
758
"select_sell_provider_notice": "Yan olupese ti o ta loke. O le foju iboju yii nipa tito olupese iṣẹ tita aiyipada rẹ ni awọn eto app.",
759
"custom_drag": "Aṣa (mu ati fa)",
760
- "switchToEVMCompatibleWallet": "Jọwọ yipada si apamọwọ ibaramu EVM ki o tun gbiyanju lẹẹkansi (Ethereum, Polygon)"
761
-}
760
+ "switchToEVMCompatibleWallet": "Jọwọ yipada si apamọwọ ibaramu EVM ki o tun gbiyanju lẹẹkansi (Ethereum, Polygon)",
761
+ "receivable_balance": "Iwontunws.funfun ti o gba",
762
+ "confirmed_tx": "Jẹrisi",
763
+ "transaction_details_source_address": "Adirẹsi orisun"
764
+}
\ No newline at end of file
res/values/strings_zh.arb
+5
-2
@@ -762,5 +762,8 @@
762
"default_sell_provider": "默认销售提供商",
763
"select_sell_provider_notice": "选择上面的销售提供商。您可以通过在应用程序设置中设置默认销售提供商来跳过此屏幕。",
764
"custom_drag": "定制(保持和拖动)",
765
- "switchToEVMCompatibleWallet": "请切换到 EVM 兼容钱包并重试(以太坊、Polygon)"
766
-}
765
+ "switchToEVMCompatibleWallet": "请切换到 EVM 兼容钱包并重试(以太坊、Polygon)",
766
+ "receivable_balance": "应收余额",
767
+ "confirmed_tx": "确认的",
768
+ "transaction_details_source_address": "源地址"
769
+}
\ No newline at end of file
tool/configure.dart
+2
-17
@@ -759,7 +759,7 @@ import 'package:convert/convert.dart';
759
import "package:ed25519_hd_key/ed25519_hd_key.dart";
760
import 'package:libcrypto/libcrypto.dart';
761
import 'package:nanodart/nanodart.dart' as ND;
762
-import 'package:decimal/decimal.dart';
762
+import 'package:nanoutil/nanoutil.dart';
763
""";
764
const nanoCwPart = "part 'cw_nano.dart';";
765
const nanoContent = """
@@ -795,7 +795,7 @@ abstract class Nano {
795
Map<String, String> getKeys(Object wallet);
796
Object createNanoTransactionCredentials(List<Output> outputs);
797
Future<void> changeRep(Object wallet, String address);
798
- Future<void> updateTransactions(Object wallet);
798
+ Future<bool> updateTransactions(Object wallet);
799
BigInt getTransactionAmountRaw(TransactionInfo transactionInfo);
800
String getRepresentative(Object wallet);
801
}
@@ -810,20 +810,6 @@ abstract class NanoAccountList {
810
}
811
812
abstract class NanoUtil {
813
- String seedToPrivate(String seed, int index);
814
- String seedToAddress(String seed, int index);
815
- String seedToMnemonic(String seed);
816
- Future<String> mnemonicToSeed(String mnemonic);
817
- String privateKeyToPublic(String privateKey);
818
- String addressToPublicKey(String publicAddress);
819
- String privateKeyToAddress(String privateKey);
820
- String publicKeyToAddress(String publicKey);
821
- bool isValidSeed(String seed);
822
- Future<String> hdMnemonicListToSeed(List<String> words);
823
- Future<String> hdSeedToPrivate(String seed, int index);
824
- Future<String> hdSeedToAddress(String seed, int index);
825
- Future<String> uniSeedToAddress(String seed, int index, String type);
826
- Future<String> uniSeedToPrivate(String seed, int index, String type);
813
bool isValidBip39Seed(String seed);
814
static const int maxDecimalDigits = 6; // Max digits after decimal
815
BigInt rawPerNano = BigInt.parse("1000000000000000000000000000000");
@@ -831,7 +817,6 @@ abstract class NanoUtil {
817
BigInt rawPerBanano = BigInt.parse("100000000000000000000000000000");
818
BigInt rawPerXMR = BigInt.parse("1000000000000");
819
BigInt convertXMRtoNano = BigInt.parse("1000000000000000000");
834
- String getRawAsDecimalString(String? raw, BigInt? rawPerCur);
820
String getRawAsUsableString(String? raw, BigInt rawPerCur);
821
String getRawAccuracy(String? raw, BigInt rawPerCur);
822
String getAmountAsRaw(String amount, BigInt rawPerCur);