remove monero stuff, work on derivation ui

This commit is contained in:
fosse 2023-08-15 18:50:00 -04:00
parent c6d937ca4e
commit 2fb43e8b28
56 changed files with 368 additions and 1599 deletions

View file

@ -1,83 +0,0 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:cw_nano/api/signatures.dart';
import 'package:cw_nano/api/types.dart';
import 'package:cw_nano/api/monero_api.dart';
import 'package:cw_nano/api/structs/account_row.dart';
import 'package:flutter/foundation.dart';
import 'package:cw_nano/api/wallet.dart';
// final accountSizeNative = moneroApi
// .lookup<NativeFunction<account_size>>('account_size')
// .asFunction<SubaddressSize>();
// final accountRefreshNative = moneroApi
// .lookup<NativeFunction<account_refresh>>('account_refresh')
// .asFunction<AccountRefresh>();
// final accountGetAllNative = moneroApi
// .lookup<NativeFunction<account_get_all>>('account_get_all')
// .asFunction<AccountGetAll>();
// final accountAddNewNative = moneroApi
// .lookup<NativeFunction<account_add_new>>('account_add_row')
// .asFunction<AccountAddNew>();
// final accountSetLabelNative = moneroApi
// .lookup<NativeFunction<account_set_label>>('account_set_label_row')
// .asFunction<AccountSetLabel>();
bool isUpdating = false;
void refreshAccounts() {
// try {
// isUpdating = true;
// accountRefreshNative();
// isUpdating = false;
// } catch (e) {
// isUpdating = false;
// rethrow;
// }
}
List<AccountRow> getAllAccount() {
// final size = accountSizeNative();
// final accountAddressesPointer = accountGetAllNative();
// final accountAddresses = accountAddressesPointer.asTypedList(size);
// return accountAddresses
// .map((addr) => Pointer<AccountRow>.fromAddress(addr).ref)
// .toList();
return [];
}
void addAccountSync({required String label}) {
// final labelPointer = label.toNativeUtf8();
// accountAddNewNative(labelPointer);
// calloc.free(labelPointer);
}
void setLabelForAccountSync({required int accountIndex, required String label}) {
// final labelPointer = label.toNativeUtf8();
// accountSetLabelNative(accountIndex, labelPointer);
// calloc.free(labelPointer);
}
void _addAccount(String label) => addAccountSync(label: label);
void _setLabelForAccount(Map<String, dynamic> args) {
// final label = args['label'] as String;
// final accountIndex = args['accountIndex'] as int;
// setLabelForAccountSync(label: label, accountIndex: accountIndex);
}
Future<void> addAccount({required String label}) async {
// await compute(_addAccount, label);
// await store();
}
Future<void> setLabelForAccount({required int accountIndex, required String label}) async {
// await compute(_setLabelForAccount, {'accountIndex': accountIndex, 'label': label});
// await store();
}

View file

@ -1,8 +0,0 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
String convertUTF8ToString({required Pointer<Utf8> pointer}) {
final str = pointer.toDartString();
calloc.free(pointer);
return str;
}

View file

@ -1,5 +0,0 @@
class ConnectionToNodeException implements Exception {
ConnectionToNodeException({required this.message});
final String message;
}

View file

@ -1,8 +0,0 @@
class CreationTransactionException implements Exception {
CreationTransactionException({required this.message});
final String message;
@override
String toString() => message;
}

View file

@ -1,5 +0,0 @@
class SetupWalletException implements Exception {
SetupWalletException({required this.message});
final String message;
}

View file

@ -1,8 +0,0 @@
class WalletCreationException implements Exception {
WalletCreationException({required this.message});
final String message;
@override
String toString() => message;
}

View file

@ -1,8 +0,0 @@
class WalletOpeningException implements Exception {
WalletOpeningException({required this.message});
final String message;
@override
String toString() => message;
}

View file

@ -1,5 +0,0 @@
class WalletRestoreFromKeysException implements Exception {
WalletRestoreFromKeysException({required this.message});
final String message;
}

View file

@ -1,5 +0,0 @@
class WalletRestoreFromSeedException implements Exception {
WalletRestoreFromSeedException({required this.message});
final String message;
}

View file

@ -1,6 +0,0 @@
import 'dart:ffi';
import 'dart:io';
final DynamicLibrary moneroApi = Platform.isAndroid
? DynamicLibrary.open("libcw_monero.so")
: DynamicLibrary.open("cw_monero.framework/cw_monero");

View file

@ -1,6 +0,0 @@
class MoneroOutput {
MoneroOutput({required this.address, required this.amount});
final String address;
final String amount;
}

View file

@ -1,132 +0,0 @@
import 'dart:ffi';
import 'package:cw_monero/api/structs/pending_transaction.dart';
import 'package:cw_monero/api/structs/ut8_box.dart';
import 'package:ffi/ffi.dart';
typedef create_wallet = Int8 Function(
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Int32, Pointer<Utf8>);
typedef restore_wallet_from_seed = Int8 Function(
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Int32, Int64, Pointer<Utf8>);
typedef restore_wallet_from_keys = Int8 Function(Pointer<Utf8>, Pointer<Utf8>,
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Int32, Int64, Pointer<Utf8>);
typedef is_wallet_exist = Int8 Function(Pointer<Utf8>);
typedef load_wallet = Int8 Function(Pointer<Utf8>, Pointer<Utf8>, Int8);
typedef error_string = Pointer<Utf8> Function();
typedef get_filename = Pointer<Utf8> Function();
typedef get_seed = Pointer<Utf8> Function();
typedef get_address = Pointer<Utf8> Function(Int32, Int32);
typedef get_full_balanace = Int64 Function(Int32);
typedef get_unlocked_balanace = Int64 Function(Int32);
typedef get_current_height = Int64 Function();
typedef get_node_height = Int64 Function();
typedef is_connected = Int8 Function();
typedef setup_node = Int8 Function(
Pointer<Utf8>, Pointer<Utf8>?, Pointer<Utf8>?, Int8, Int8, Pointer<Utf8>);
typedef start_refresh = Void Function();
typedef connect_to_node = Int8 Function();
typedef set_refresh_from_block_height = Void Function(Int64);
typedef set_recovering_from_seed = Void Function(Int8);
typedef store_c = Void Function(Pointer<Utf8>);
typedef set_password = Int8 Function(Pointer<Utf8> password, Pointer<Utf8Box> error);
typedef set_listener = Void Function();
typedef get_syncing_height = Int64 Function();
typedef is_needed_to_refresh = Int8 Function();
typedef is_new_transaction_exist = Int8 Function();
typedef subaddrress_size = Int32 Function();
typedef subaddrress_refresh = Void Function(Int32);
typedef subaddress_get_all = Pointer<Int64> Function();
typedef subaddress_add_new = Void Function(
Int32 accountIndex, Pointer<Utf8> label);
typedef subaddress_set_label = Void Function(
Int32 accountIndex, Int32 addressIndex, Pointer<Utf8> label);
typedef account_size = Int32 Function();
typedef account_refresh = Void Function();
typedef account_get_all = Pointer<Int64> Function();
typedef account_add_new = Void Function(Pointer<Utf8> label);
typedef account_set_label = Void Function(
Int32 accountIndex, Pointer<Utf8> label);
typedef transactions_refresh = Void Function();
typedef get_tx_key = Pointer<Utf8>? Function(Pointer<Utf8> txId);
typedef transactions_count = Int64 Function();
typedef transactions_get_all = Pointer<Int64> Function();
typedef transaction_create = Int8 Function(
Pointer<Utf8> address,
Pointer<Utf8> paymentId,
Pointer<Utf8> amount,
Int8 priorityRaw,
Int32 subaddrAccount,
Pointer<Utf8Box> error,
Pointer<PendingTransactionRaw> pendingTransaction);
typedef transaction_create_mult_dest = Int8 Function(
Pointer<Pointer<Utf8>> addresses,
Pointer<Utf8> paymentId,
Pointer<Pointer<Utf8>> amounts,
Int32 size,
Int8 priorityRaw,
Int32 subaddrAccount,
Pointer<Utf8Box> error,
Pointer<PendingTransactionRaw> pendingTransaction);
typedef transaction_commit = Int8 Function(Pointer<PendingTransactionRaw>, Pointer<Utf8Box>);
typedef secret_view_key = Pointer<Utf8> Function();
typedef public_view_key = Pointer<Utf8> Function();
typedef secret_spend_key = Pointer<Utf8> Function();
typedef public_spend_key = Pointer<Utf8> Function();
typedef close_current_wallet = Void Function();
typedef on_startup = Void Function();
typedef rescan_blockchain = Void Function();
typedef get_subaddress_label = Pointer<Utf8> Function(
Int32 accountIndex,
Int32 addressIndex);
typedef set_trusted_daemon = Void Function(Int8 trusted);
typedef trusted_daemon = Int8 Function();

View file

@ -1,12 +0,0 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
class AccountRow extends Struct {
@Int64()
external int id;
external Pointer<Utf8> label;
String getLabel() => label.toDartString();
int getId() => id;
}

View file

@ -1,39 +0,0 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
class PendingTransactionRaw extends Struct {
@Int64()
external int amount;
@Int64()
external int fee;
external Pointer<Utf8> hash;
external Pointer<Utf8> hex;
external Pointer<Utf8> txKey;
String getHash() => hash.toDartString();
String getHex() => hex.toDartString();
String getKey() => txKey.toDartString();
}
class PendingTransactionDescription {
PendingTransactionDescription({
required this.amount,
required this.fee,
required this.hash,
required this.hex,
required this.txKey,
required this.pointerAddress});
final int amount;
final int fee;
final String hash;
final String hex;
final String txKey;
final int pointerAddress;
}

View file

@ -1,15 +0,0 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
class SubaddressRow extends Struct {
@Int64()
external int id;
external Pointer<Utf8> address;
external Pointer<Utf8> label;
String getLabel() => label.toDartString();
String getAddress() => address.toDartString();
int getId() => id;
}

View file

@ -1,41 +0,0 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
class TransactionInfoRow extends Struct {
@Uint64()
external int amount;
@Uint64()
external int fee;
@Uint64()
external int blockHeight;
@Uint64()
external int confirmations;
@Uint32()
external int subaddrAccount;
@Int8()
external int direction;
@Int8()
external int isPending;
@Uint32()
external int subaddrIndex;
external Pointer<Utf8> hash;
external Pointer<Utf8> paymentId;
@Int64()
external int datetime;
int getDatetime() => datetime;
int getAmount() => amount >= 0 ? amount : amount * -1;
bool getIsPending() => isPending != 0;
String getHash() => hash.toDartString();
String getPaymentId() => paymentId.toDartString();
}

View file

@ -1,8 +0,0 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
class Utf8Box extends Struct {
external Pointer<Utf8> value;
String getValue() => value.toDartString();
}

View file

@ -1,97 +0,0 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:flutter/foundation.dart';
import 'package:cw_monero/api/signatures.dart';
import 'package:cw_monero/api/types.dart';
import 'package:cw_monero/api/monero_api.dart';
import 'package:cw_monero/api/structs/subaddress_row.dart';
import 'package:cw_monero/api/wallet.dart';
final subaddressSizeNative = moneroApi
.lookup<NativeFunction<subaddrress_size>>('subaddrress_size')
.asFunction<SubaddressSize>();
final subaddressRefreshNative = moneroApi
.lookup<NativeFunction<subaddrress_refresh>>('subaddress_refresh')
.asFunction<SubaddressRefresh>();
final subaddrressGetAllNative = moneroApi
.lookup<NativeFunction<subaddress_get_all>>('subaddrress_get_all')
.asFunction<SubaddressGetAll>();
final subaddrressAddNewNative = moneroApi
.lookup<NativeFunction<subaddress_add_new>>('subaddress_add_row')
.asFunction<SubaddressAddNew>();
final subaddrressSetLabelNative = moneroApi
.lookup<NativeFunction<subaddress_set_label>>('subaddress_set_label')
.asFunction<SubaddressSetLabel>();
bool isUpdating = false;
void refreshSubaddresses({required int accountIndex}) {
try {
isUpdating = true;
subaddressRefreshNative(accountIndex);
isUpdating = false;
} catch (e) {
isUpdating = false;
rethrow;
}
}
List<SubaddressRow> getAllSubaddresses() {
final size = subaddressSizeNative();
final subaddressAddressesPointer = subaddrressGetAllNative();
final subaddressAddresses = subaddressAddressesPointer.asTypedList(size);
return subaddressAddresses
.map((addr) => Pointer<SubaddressRow>.fromAddress(addr).ref)
.toList();
}
void addSubaddressSync({required int accountIndex, required String label}) {
final labelPointer = label.toNativeUtf8();
subaddrressAddNewNative(accountIndex, labelPointer);
calloc.free(labelPointer);
}
void setLabelForSubaddressSync(
{required int accountIndex, required int addressIndex, required String label}) {
final labelPointer = label.toNativeUtf8();
subaddrressSetLabelNative(accountIndex, addressIndex, labelPointer);
calloc.free(labelPointer);
}
void _addSubaddress(Map<String, dynamic> args) {
final label = args['label'] as String;
final accountIndex = args['accountIndex'] as int;
addSubaddressSync(accountIndex: accountIndex, label: label);
}
void _setLabelForSubaddress(Map<String, dynamic> args) {
final label = args['label'] as String;
final accountIndex = args['accountIndex'] as int;
final addressIndex = args['addressIndex'] as int;
setLabelForSubaddressSync(
accountIndex: accountIndex, addressIndex: addressIndex, label: label);
}
Future<void> addSubaddress({required int accountIndex, required String label}) async {
await compute<Map<String, Object>, void>(
_addSubaddress, {'accountIndex': accountIndex, 'label': label});
await store();
}
Future<void> setLabelForSubaddress(
{required int accountIndex, required int addressIndex, required String label}) async {
await compute<Map<String, Object>, void>(_setLabelForSubaddress, {
'accountIndex': accountIndex,
'addressIndex': addressIndex,
'label': label
});
await store();
}

View file

@ -1,234 +0,0 @@
import 'dart:ffi';
import 'package:cw_monero/api/convert_utf8_to_string.dart';
import 'package:cw_monero/api/monero_output.dart';
import 'package:cw_monero/api/structs/ut8_box.dart';
import 'package:ffi/ffi.dart';
import 'package:flutter/foundation.dart';
import 'package:cw_monero/api/signatures.dart';
import 'package:cw_monero/api/types.dart';
import 'package:cw_monero/api/monero_api.dart';
import 'package:cw_monero/api/structs/transaction_info_row.dart';
import 'package:cw_monero/api/structs/pending_transaction.dart';
import 'package:cw_monero/api/exceptions/creation_transaction_exception.dart';
final transactionsRefreshNative = moneroApi
.lookup<NativeFunction<transactions_refresh>>('transactions_refresh')
.asFunction<TransactionsRefresh>();
final transactionsCountNative = moneroApi
.lookup<NativeFunction<transactions_count>>('transactions_count')
.asFunction<TransactionsCount>();
final transactionsGetAllNative = moneroApi
.lookup<NativeFunction<transactions_get_all>>('transactions_get_all')
.asFunction<TransactionsGetAll>();
final transactionCreateNative = moneroApi
.lookup<NativeFunction<transaction_create>>('transaction_create')
.asFunction<TransactionCreate>();
final transactionCreateMultDestNative = moneroApi
.lookup<NativeFunction<transaction_create_mult_dest>>('transaction_create_mult_dest')
.asFunction<TransactionCreateMultDest>();
final transactionCommitNative = moneroApi
.lookup<NativeFunction<transaction_commit>>('transaction_commit')
.asFunction<TransactionCommit>();
final getTxKeyNative = moneroApi
.lookup<NativeFunction<get_tx_key>>('get_tx_key')
.asFunction<GetTxKey>();
String getTxKey(String txId) {
final txIdPointer = txId.toNativeUtf8();
final keyPointer = getTxKeyNative(txIdPointer);
calloc.free(txIdPointer);
if (keyPointer != null) {
return convertUTF8ToString(pointer: keyPointer);
}
return '';
}
void refreshTransactions() => transactionsRefreshNative();
int countOfTransactions() => transactionsCountNative();
List<TransactionInfoRow> getAllTransations() {
final size = transactionsCountNative();
final transactionsPointer = transactionsGetAllNative();
final transactionsAddresses = transactionsPointer.asTypedList(size);
return transactionsAddresses
.map((addr) => Pointer<TransactionInfoRow>.fromAddress(addr).ref)
.toList();
}
PendingTransactionDescription createTransactionSync(
{required String address,
required String paymentId,
required int priorityRaw,
String? amount,
int accountIndex = 0}) {
final addressPointer = address.toNativeUtf8();
final paymentIdPointer = paymentId.toNativeUtf8();
final amountPointer = amount != null ? amount.toNativeUtf8() : nullptr;
final errorMessagePointer = calloc<Utf8Box>();
final pendingTransactionRawPointer = calloc<PendingTransactionRaw>();
final created = transactionCreateNative(
addressPointer,
paymentIdPointer,
amountPointer,
priorityRaw,
accountIndex,
errorMessagePointer,
pendingTransactionRawPointer) !=
0;
calloc.free(addressPointer);
calloc.free(paymentIdPointer);
if (amountPointer != nullptr) {
calloc.free(amountPointer);
}
if (!created) {
final message = errorMessagePointer.ref.getValue();
calloc.free(errorMessagePointer);
throw CreationTransactionException(message: message);
}
return PendingTransactionDescription(
amount: pendingTransactionRawPointer.ref.amount,
fee: pendingTransactionRawPointer.ref.fee,
hash: pendingTransactionRawPointer.ref.getHash(),
hex: pendingTransactionRawPointer.ref.getHex(),
txKey: pendingTransactionRawPointer.ref.getKey(),
pointerAddress: pendingTransactionRawPointer.address);
}
PendingTransactionDescription createTransactionMultDestSync(
{required List<MoneroOutput> outputs,
required String paymentId,
required int priorityRaw,
int accountIndex = 0}) {
final int size = outputs.length;
final List<Pointer<Utf8>> addressesPointers = outputs.map((output) =>
output.address.toNativeUtf8()).toList();
final Pointer<Pointer<Utf8>> addressesPointerPointer = calloc(size);
final List<Pointer<Utf8>> amountsPointers = outputs.map((output) =>
output.amount.toNativeUtf8()).toList();
final Pointer<Pointer<Utf8>> amountsPointerPointer = calloc(size);
for (int i = 0; i < size; i++) {
addressesPointerPointer[i] = addressesPointers[i];
amountsPointerPointer[i] = amountsPointers[i];
}
final paymentIdPointer = paymentId.toNativeUtf8();
final errorMessagePointer = calloc<Utf8Box>();
final pendingTransactionRawPointer = calloc<PendingTransactionRaw>();
final created = transactionCreateMultDestNative(
addressesPointerPointer,
paymentIdPointer,
amountsPointerPointer,
size,
priorityRaw,
accountIndex,
errorMessagePointer,
pendingTransactionRawPointer) !=
0;
calloc.free(addressesPointerPointer);
calloc.free(amountsPointerPointer);
addressesPointers.forEach((element) => calloc.free(element));
amountsPointers.forEach((element) => calloc.free(element));
calloc.free(paymentIdPointer);
if (!created) {
final message = errorMessagePointer.ref.getValue();
calloc.free(errorMessagePointer);
throw CreationTransactionException(message: message);
}
return PendingTransactionDescription(
amount: pendingTransactionRawPointer.ref.amount,
fee: pendingTransactionRawPointer.ref.fee,
hash: pendingTransactionRawPointer.ref.getHash(),
hex: pendingTransactionRawPointer.ref.getHex(),
txKey: pendingTransactionRawPointer.ref.getKey(),
pointerAddress: pendingTransactionRawPointer.address);
}
void commitTransactionFromPointerAddress({required int address}) => commitTransaction(
transactionPointer: Pointer<PendingTransactionRaw>.fromAddress(address));
void commitTransaction({required Pointer<PendingTransactionRaw> transactionPointer}) {
final errorMessagePointer = calloc<Utf8Box>();
final isCommited =
transactionCommitNative(transactionPointer, errorMessagePointer) != 0;
if (!isCommited) {
final message = errorMessagePointer.ref.getValue();
calloc.free(errorMessagePointer);
throw CreationTransactionException(message: message);
}
}
PendingTransactionDescription _createTransactionSync(Map args) {
final address = args['address'] as String;
final paymentId = args['paymentId'] as String;
final amount = args['amount'] as String?;
final priorityRaw = args['priorityRaw'] as int;
final accountIndex = args['accountIndex'] as int;
return createTransactionSync(
address: address,
paymentId: paymentId,
amount: amount,
priorityRaw: priorityRaw,
accountIndex: accountIndex);
}
PendingTransactionDescription _createTransactionMultDestSync(Map args) {
final outputs = args['outputs'] as List<MoneroOutput>;
final paymentId = args['paymentId'] as String;
final priorityRaw = args['priorityRaw'] as int;
final accountIndex = args['accountIndex'] as int;
return createTransactionMultDestSync(
outputs: outputs,
paymentId: paymentId,
priorityRaw: priorityRaw,
accountIndex: accountIndex);
}
Future<PendingTransactionDescription> createTransaction(
{required String address,
required int priorityRaw,
String? amount,
String paymentId = '',
int accountIndex = 0}) =>
compute(_createTransactionSync, {
'address': address,
'paymentId': paymentId,
'amount': amount,
'priorityRaw': priorityRaw,
'accountIndex': accountIndex
});
Future<PendingTransactionDescription> createTransactionMultDest(
{required List<MoneroOutput> outputs,
required int priorityRaw,
String paymentId = '',
int accountIndex = 0}) =>
compute(_createTransactionMultDestSync, {
'outputs': outputs,
'paymentId': paymentId,
'priorityRaw': priorityRaw,
'accountIndex': accountIndex
});

View file

@ -1,130 +0,0 @@
import 'dart:ffi';
import 'package:cw_monero/api/structs/pending_transaction.dart';
import 'package:cw_monero/api/structs/ut8_box.dart';
import 'package:ffi/ffi.dart';
typedef CreateWallet = int Function(
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, int, Pointer<Utf8>);
typedef RestoreWalletFromSeed = int Function(
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, int, int, Pointer<Utf8>);
typedef RestoreWalletFromKeys = int Function(Pointer<Utf8>, Pointer<Utf8>,
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, int, int, Pointer<Utf8>);
typedef IsWalletExist = int Function(Pointer<Utf8>);
typedef LoadWallet = int Function(Pointer<Utf8>, Pointer<Utf8>, int);
typedef ErrorString = Pointer<Utf8> Function();
typedef GetFilename = Pointer<Utf8> Function();
typedef GetSeed = Pointer<Utf8> Function();
typedef GetAddress = Pointer<Utf8> Function(int, int);
typedef GetFullBalance = int Function(int);
typedef GetUnlockedBalance = int Function(int);
typedef GetCurrentHeight = int Function();
typedef GetNodeHeight = int Function();
typedef IsConnected = int Function();
typedef SetupNode = int Function(
Pointer<Utf8>, Pointer<Utf8>?, Pointer<Utf8>?, int, int, Pointer<Utf8>);
typedef StartRefresh = void Function();
typedef ConnectToNode = int Function();
typedef SetRefreshFromBlockHeight = void Function(int);
typedef SetRecoveringFromSeed = void Function(int);
typedef Store = void Function(Pointer<Utf8>);
typedef SetPassword = int Function(Pointer<Utf8> password, Pointer<Utf8Box> error);
typedef SetListener = void Function();
typedef GetSyncingHeight = int Function();
typedef IsNeededToRefresh = int Function();
typedef IsNewTransactionExist = int Function();
typedef SubaddressSize = int Function();
typedef SubaddressRefresh = void Function(int);
typedef SubaddressGetAll = Pointer<Int64> Function();
typedef SubaddressAddNew = void Function(int accountIndex, Pointer<Utf8> label);
typedef SubaddressSetLabel = void Function(
int accountIndex, int addressIndex, Pointer<Utf8> label);
typedef AccountSize = int Function();
typedef AccountRefresh = void Function();
typedef AccountGetAll = Pointer<Int64> Function();
typedef AccountAddNew = void Function(Pointer<Utf8> label);
typedef AccountSetLabel = void Function(int accountIndex, Pointer<Utf8> label);
typedef TransactionsRefresh = void Function();
typedef GetTxKey = Pointer<Utf8>? Function(Pointer<Utf8> txId);
typedef TransactionsCount = int Function();
typedef TransactionsGetAll = Pointer<Int64> Function();
typedef TransactionCreate = int Function(
Pointer<Utf8> address,
Pointer<Utf8> paymentId,
Pointer<Utf8> amount,
int priorityRaw,
int subaddrAccount,
Pointer<Utf8Box> error,
Pointer<PendingTransactionRaw> pendingTransaction);
typedef TransactionCreateMultDest = int Function(
Pointer<Pointer<Utf8>> addresses,
Pointer<Utf8> paymentId,
Pointer<Pointer<Utf8>> amounts,
int size,
int priorityRaw,
int subaddrAccount,
Pointer<Utf8Box> error,
Pointer<PendingTransactionRaw> pendingTransaction);
typedef TransactionCommit = int Function(Pointer<PendingTransactionRaw>, Pointer<Utf8Box>);
typedef SecretViewKey = Pointer<Utf8> Function();
typedef PublicViewKey = Pointer<Utf8> Function();
typedef SecretSpendKey = Pointer<Utf8> Function();
typedef PublicSpendKey = Pointer<Utf8> Function();
typedef CloseCurrentWallet = void Function();
typedef OnStartup = void Function();
typedef RescanBlockchainAsync = void Function();
typedef GetSubaddressLabel = Pointer<Utf8> Function(
int accountIndex,
int addressIndex);
typedef SetTrustedDaemon = void Function(int);
typedef TrustedDaemon = int Function();

View file

@ -1,353 +0,0 @@
import 'dart:async';
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:cw_monero/api/structs/ut8_box.dart';
import 'package:cw_monero/api/convert_utf8_to_string.dart';
import 'package:cw_monero/api/signatures.dart';
import 'package:cw_monero/api/types.dart';
import 'package:cw_monero/api/monero_api.dart';
import 'package:cw_monero/api/exceptions/setup_wallet_exception.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
int _boolToInt(bool value) => value ? 1 : 0;
final getFileNameNative =
moneroApi.lookup<NativeFunction<get_filename>>('get_filename').asFunction<GetFilename>();
final getSeedNative = moneroApi.lookup<NativeFunction<get_seed>>('seed').asFunction<GetSeed>();
final getAddressNative =
moneroApi.lookup<NativeFunction<get_address>>('get_address').asFunction<GetAddress>();
final getFullBalanceNative = moneroApi
.lookup<NativeFunction<get_full_balanace>>('get_full_balance')
.asFunction<GetFullBalance>();
final getUnlockedBalanceNative = moneroApi
.lookup<NativeFunction<get_unlocked_balanace>>('get_unlocked_balance')
.asFunction<GetUnlockedBalance>();
final getCurrentHeightNative = moneroApi
.lookup<NativeFunction<get_current_height>>('get_current_height')
.asFunction<GetCurrentHeight>();
final getNodeHeightNative = moneroApi
.lookup<NativeFunction<get_node_height>>('get_node_height')
.asFunction<GetNodeHeight>();
final isConnectedNative =
moneroApi.lookup<NativeFunction<is_connected>>('is_connected').asFunction<IsConnected>();
final setupNodeNative =
moneroApi.lookup<NativeFunction<setup_node>>('setup_node').asFunction<SetupNode>();
final startRefreshNative =
moneroApi.lookup<NativeFunction<start_refresh>>('start_refresh').asFunction<StartRefresh>();
final connecToNodeNative = moneroApi
.lookup<NativeFunction<connect_to_node>>('connect_to_node')
.asFunction<ConnectToNode>();
final setRefreshFromBlockHeightNative = moneroApi
.lookup<NativeFunction<set_refresh_from_block_height>>('set_refresh_from_block_height')
.asFunction<SetRefreshFromBlockHeight>();
final setRecoveringFromSeedNative = moneroApi
.lookup<NativeFunction<set_recovering_from_seed>>('set_recovering_from_seed')
.asFunction<SetRecoveringFromSeed>();
final storeNative = moneroApi.lookup<NativeFunction<store_c>>('store').asFunction<Store>();
final setPasswordNative =
moneroApi.lookup<NativeFunction<set_password>>('set_password').asFunction<SetPassword>();
final setListenerNative =
moneroApi.lookup<NativeFunction<set_listener>>('set_listener').asFunction<SetListener>();
final getSyncingHeightNative = moneroApi
.lookup<NativeFunction<get_syncing_height>>('get_syncing_height')
.asFunction<GetSyncingHeight>();
final isNeededToRefreshNative = moneroApi
.lookup<NativeFunction<is_needed_to_refresh>>('is_needed_to_refresh')
.asFunction<IsNeededToRefresh>();
final isNewTransactionExistNative = moneroApi
.lookup<NativeFunction<is_new_transaction_exist>>('is_new_transaction_exist')
.asFunction<IsNewTransactionExist>();
final getSecretViewKeyNative = moneroApi
.lookup<NativeFunction<secret_view_key>>('secret_view_key')
.asFunction<SecretViewKey>();
final getPublicViewKeyNative = moneroApi
.lookup<NativeFunction<public_view_key>>('public_view_key')
.asFunction<PublicViewKey>();
final getSecretSpendKeyNative = moneroApi
.lookup<NativeFunction<secret_spend_key>>('secret_spend_key')
.asFunction<SecretSpendKey>();
final getPublicSpendKeyNative = moneroApi
.lookup<NativeFunction<secret_view_key>>('public_spend_key')
.asFunction<PublicSpendKey>();
final closeCurrentWalletNative = moneroApi
.lookup<NativeFunction<close_current_wallet>>('close_current_wallet')
.asFunction<CloseCurrentWallet>();
final onStartupNative =
moneroApi.lookup<NativeFunction<on_startup>>('on_startup').asFunction<OnStartup>();
final rescanBlockchainAsyncNative = moneroApi
.lookup<NativeFunction<rescan_blockchain>>('rescan_blockchain')
.asFunction<RescanBlockchainAsync>();
final getSubaddressLabelNative = moneroApi
.lookup<NativeFunction<get_subaddress_label>>('get_subaddress_label')
.asFunction<GetSubaddressLabel>();
final setTrustedDaemonNative = moneroApi
.lookup<NativeFunction<set_trusted_daemon>>('set_trusted_daemon')
.asFunction<SetTrustedDaemon>();
final trustedDaemonNative =
moneroApi.lookup<NativeFunction<trusted_daemon>>('trusted_daemon').asFunction<TrustedDaemon>();
int getSyncingHeight() => getSyncingHeightNative();
bool isNeededToRefresh() => isNeededToRefreshNative() != 0;
bool isNewTransactionExist() => isNewTransactionExistNative() != 0;
String getFilename() => convertUTF8ToString(pointer: getFileNameNative());
String getSeed() => convertUTF8ToString(pointer: getSeedNative());
String getAddress({int accountIndex = 0, int addressIndex = 0}) =>
convertUTF8ToString(pointer: getAddressNative(accountIndex, addressIndex));
int getFullBalance({int accountIndex = 0}) => getFullBalanceNative(accountIndex);
int getUnlockedBalance({int accountIndex = 0}) => getUnlockedBalanceNative(accountIndex);
int getCurrentHeight() => getCurrentHeightNative();
int getNodeHeightSync() => getNodeHeightNative();
bool isConnectedSync() => isConnectedNative() != 0;
bool setupNodeSync(
{required String address,
String? login,
String? password,
bool useSSL = false,
bool isLightWallet = false}) {
final addressPointer = address.toNativeUtf8();
Pointer<Utf8>? loginPointer;
Pointer<Utf8>? passwordPointer;
if (login != null) {
loginPointer = login.toNativeUtf8();
}
if (password != null) {
passwordPointer = password.toNativeUtf8();
}
final errorMessagePointer = ''.toNativeUtf8();
// final isSetupNode = setupNodeNative(
// addressPointer,
// loginPointer,
// passwordPointer,
// _boolToInt(useSSL),
// _boolToInt(isLightWallet),
// errorMessagePointer) !=
// 0;
calloc.free(addressPointer);
if (loginPointer != null) {
calloc.free(loginPointer);
}
if (passwordPointer != null) {
calloc.free(passwordPointer);
}
// if (!isSetupNode) {
// throw SetupWalletException(
// message: convertUTF8ToString(pointer: errorMessagePointer));
// }
// return isSetupNode;
return true;
}
void startRefreshSync() => startRefreshNative();
Future<bool> connectToNode() async => connecToNodeNative() != 0;
void setRefreshFromBlockHeight({required int height}) => setRefreshFromBlockHeightNative(height);
void setRecoveringFromSeed({required bool isRecovery}) =>
setRecoveringFromSeedNative(_boolToInt(isRecovery));
void storeSync() {
final pathPointer = ''.toNativeUtf8();
storeNative(pathPointer);
calloc.free(pathPointer);
}
void setPasswordSync(String password) {
final passwordPointer = password.toNativeUtf8();
final errorMessagePointer = calloc<Utf8Box>();
final changed = setPasswordNative(passwordPointer, errorMessagePointer) != 0;
calloc.free(passwordPointer);
if (!changed) {
final message = errorMessagePointer.ref.getValue();
calloc.free(errorMessagePointer);
throw Exception(message);
}
calloc.free(errorMessagePointer);
}
void closeCurrentWallet() => closeCurrentWalletNative();
String getSecretViewKey() => convertUTF8ToString(pointer: getSecretViewKeyNative());
String getPublicViewKey() => convertUTF8ToString(pointer: getPublicViewKeyNative());
String getSecretSpendKey() => convertUTF8ToString(pointer: getSecretSpendKeyNative());
String getPublicSpendKey() => convertUTF8ToString(pointer: getPublicSpendKeyNative());
class SyncListener {
SyncListener(this.onNewBlock, this.onNewTransaction)
: _cachedBlockchainHeight = 0,
_lastKnownBlockHeight = 0,
_initialSyncHeight = 0;
void Function(int, int, double) onNewBlock;
void Function() onNewTransaction;
Timer? _updateSyncInfoTimer;
int _cachedBlockchainHeight;
int _lastKnownBlockHeight;
int _initialSyncHeight;
Future<int> getNodeHeightOrUpdate(int baseHeight) async {
if (_cachedBlockchainHeight < baseHeight || _cachedBlockchainHeight == 0) {
_cachedBlockchainHeight = await getNodeHeight();
}
return _cachedBlockchainHeight;
}
void start() {
_cachedBlockchainHeight = 0;
_lastKnownBlockHeight = 0;
_initialSyncHeight = 0;
_updateSyncInfoTimer ??= Timer.periodic(Duration(milliseconds: 1200), (_) async {
if (isNewTransactionExist()) {
onNewTransaction();
}
var syncHeight = getSyncingHeight();
if (syncHeight <= 0) {
syncHeight = getCurrentHeight();
}
if (_initialSyncHeight <= 0) {
_initialSyncHeight = syncHeight;
}
final bchHeight = await getNodeHeightOrUpdate(syncHeight);
if (_lastKnownBlockHeight == syncHeight || syncHeight == null) {
return;
}
_lastKnownBlockHeight = syncHeight;
final track = bchHeight - _initialSyncHeight;
final diff = track - (bchHeight - syncHeight);
final ptc = diff <= 0 ? 0.0 : diff / track;
final left = bchHeight - syncHeight;
if (syncHeight < 0 || left < 0) {
return;
}
// 1. Actual new height; 2. Blocks left to finish; 3. Progress in percents;
onNewBlock?.call(syncHeight, left, ptc);
});
}
void stop() => _updateSyncInfoTimer?.cancel();
}
SyncListener setListeners(
void Function(int, int, double) onNewBlock, void Function() onNewTransaction) {
final listener = SyncListener(onNewBlock, onNewTransaction);
setListenerNative();
return listener;
}
void onStartup() => onStartupNative();
void _storeSync(Object _) => storeSync();
bool _setupNodeSync(Map<String, Object?> args) {
final address = args['address'] as String;
final login = (args['login'] ?? '') as String;
final password = (args['password'] ?? '') as String;
final useSSL = args['useSSL'] as bool;
final isLightWallet = args['isLightWallet'] as bool;
return setupNodeSync(
address: address,
login: login,
password: password,
useSSL: useSSL,
isLightWallet: isLightWallet);
}
bool _isConnected(Object _) => isConnectedSync();
int _getNodeHeight(Object _) => getNodeHeightSync();
void startRefresh() => startRefreshSync();
Future<void> setupNode(
{required String address,
String? login,
String? password,
bool useSSL = false,
bool isLightWallet = false}) =>
compute<Map<String, Object?>, void>(_setupNodeSync, {
'address': address,
'login': login,
'password': password,
'useSSL': useSSL,
'isLightWallet': isLightWallet
});
Future<void> store() => compute<int, void>(_storeSync, 0);
Future<bool> isConnected() => compute(_isConnected, 0);
Future<int> getNodeHeight() => compute(_getNodeHeight, 0);
void rescanBlockchainAsync() => rescanBlockchainAsyncNative();
String getSubaddressLabel(int accountIndex, int addressIndex) {
return convertUTF8ToString(pointer: getSubaddressLabelNative(accountIndex, addressIndex));
}
Future setTrustedDaemon(bool trusted) async => setTrustedDaemonNative(_boolToInt(trusted));
Future<bool> trustedDaemon() async => trustedDaemonNative() != 0;

View file

@ -1,254 +0,0 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:flutter/foundation.dart';
import 'package:cw_monero/api/convert_utf8_to_string.dart';
import 'package:cw_monero/api/signatures.dart';
import 'package:cw_monero/api/types.dart';
import 'package:cw_monero/api/monero_api.dart';
import 'package:cw_monero/api/wallet.dart';
import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart';
import 'package:cw_monero/api/exceptions/wallet_creation_exception.dart';
import 'package:cw_monero/api/exceptions/wallet_restore_from_keys_exception.dart';
import 'package:cw_monero/api/exceptions/wallet_restore_from_seed_exception.dart';
final createWalletNative = moneroApi
.lookup<NativeFunction<create_wallet>>('create_wallet')
.asFunction<CreateWallet>();
final restoreWalletFromSeedNative = moneroApi
.lookup<NativeFunction<restore_wallet_from_seed>>(
'restore_wallet_from_seed')
.asFunction<RestoreWalletFromSeed>();
final restoreWalletFromKeysNative = moneroApi
.lookup<NativeFunction<restore_wallet_from_keys>>(
'restore_wallet_from_keys')
.asFunction<RestoreWalletFromKeys>();
final isWalletExistNative = moneroApi
.lookup<NativeFunction<is_wallet_exist>>('is_wallet_exist')
.asFunction<IsWalletExist>();
final loadWalletNative = moneroApi
.lookup<NativeFunction<load_wallet>>('load_wallet')
.asFunction<LoadWallet>();
final errorStringNative = moneroApi
.lookup<NativeFunction<error_string>>('error_string')
.asFunction<ErrorString>();
void createWalletSync(
{required String path,
required String password,
required String language,
int nettype = 0}) {
final pathPointer = path.toNativeUtf8();
final passwordPointer = password.toNativeUtf8();
final languagePointer = language.toNativeUtf8();
final errorMessagePointer = ''.toNativeUtf8();
final isWalletCreated = createWalletNative(pathPointer, passwordPointer,
languagePointer, nettype, errorMessagePointer) !=
0;
calloc.free(pathPointer);
calloc.free(passwordPointer);
calloc.free(languagePointer);
if (!isWalletCreated) {
throw WalletCreationException(
message: convertUTF8ToString(pointer: errorMessagePointer));
}
// setupNodeSync(address: "node.moneroworld.com:18089");
}
bool isWalletExistSync({required String path}) {
final pathPointer = path.toNativeUtf8();
final isExist = isWalletExistNative(pathPointer) != 0;
calloc.free(pathPointer);
return isExist;
}
void restoreWalletFromSeedSync(
{required String path,
required String password,
required String seed,
int nettype = 0,
int restoreHeight = 0}) {
final pathPointer = path.toNativeUtf8();
final passwordPointer = password.toNativeUtf8();
final seedPointer = seed.toNativeUtf8();
final errorMessagePointer = ''.toNativeUtf8();
final isWalletRestored = restoreWalletFromSeedNative(
pathPointer,
passwordPointer,
seedPointer,
nettype,
restoreHeight,
errorMessagePointer) !=
0;
calloc.free(pathPointer);
calloc.free(passwordPointer);
calloc.free(seedPointer);
if (!isWalletRestored) {
throw WalletRestoreFromSeedException(
message: convertUTF8ToString(pointer: errorMessagePointer));
}
}
void restoreWalletFromKeysSync(
{required String path,
required String password,
required String language,
required String address,
required String viewKey,
required String spendKey,
int nettype = 0,
int restoreHeight = 0}) {
final pathPointer = path.toNativeUtf8();
final passwordPointer = password.toNativeUtf8();
final languagePointer = language.toNativeUtf8();
final addressPointer = address.toNativeUtf8();
final viewKeyPointer = viewKey.toNativeUtf8();
final spendKeyPointer = spendKey.toNativeUtf8();
final errorMessagePointer = ''.toNativeUtf8();
final isWalletRestored = restoreWalletFromKeysNative(
pathPointer,
passwordPointer,
languagePointer,
addressPointer,
viewKeyPointer,
spendKeyPointer,
nettype,
restoreHeight,
errorMessagePointer) !=
0;
calloc.free(pathPointer);
calloc.free(passwordPointer);
calloc.free(languagePointer);
calloc.free(addressPointer);
calloc.free(viewKeyPointer);
calloc.free(spendKeyPointer);
if (!isWalletRestored) {
throw WalletRestoreFromKeysException(
message: convertUTF8ToString(pointer: errorMessagePointer));
}
}
void loadWallet({
required String path,
required String password,
int nettype = 0}) {
final pathPointer = path.toNativeUtf8();
final passwordPointer = password.toNativeUtf8();
final loaded = loadWalletNative(pathPointer, passwordPointer, nettype) != 0;
calloc.free(pathPointer);
calloc.free(passwordPointer);
if (!loaded) {
throw WalletOpeningException(
message: convertUTF8ToString(pointer: errorStringNative()));
}
}
void _createWallet(Map<String, dynamic> args) {
final path = args['path'] as String;
final password = args['password'] as String;
final language = args['language'] as String;
createWalletSync(path: path, password: password, language: language);
}
void _restoreFromSeed(Map<String, dynamic> args) {
final path = args['path'] as String;
final password = args['password'] as String;
final seed = args['seed'] as String;
final restoreHeight = args['restoreHeight'] as int;
restoreWalletFromSeedSync(
path: path, password: password, seed: seed, restoreHeight: restoreHeight);
}
void _restoreFromKeys(Map<String, dynamic> args) {
final path = args['path'] as String;
final password = args['password'] as String;
final language = args['language'] as String;
final restoreHeight = args['restoreHeight'] as int;
final address = args['address'] as String;
final viewKey = args['viewKey'] as String;
final spendKey = args['spendKey'] as String;
restoreWalletFromKeysSync(
path: path,
password: password,
language: language,
restoreHeight: restoreHeight,
address: address,
viewKey: viewKey,
spendKey: spendKey);
}
Future<void> _openWallet(Map<String, String> args) async =>
loadWallet(path: args['path'] as String, password: args['password'] as String);
bool _isWalletExist(String path) => isWalletExistSync(path: path);
void openWallet({required String path, required String password, int nettype = 0}) async =>
loadWallet(path: path, password: password, nettype: nettype);
Future<void> openWalletAsync(Map<String, String> args) async =>
compute(_openWallet, args);
Future<void> createWallet(
{required String path,
required String password,
required String language,
int nettype = 0}) async =>
compute(_createWallet, {
'path': path,
'password': password,
'language': language,
'nettype': nettype
});
Future<void> restoreFromSeed(
{required String path,
required String password,
required String seed,
int nettype = 0,
int restoreHeight = 0}) async =>
compute<Map<String, Object>, void>(_restoreFromSeed, {
'path': path,
'password': password,
'seed': seed,
'nettype': nettype,
'restoreHeight': restoreHeight
});
Future<void> restoreFromKeys(
{required String path,
required String password,
required String language,
required String address,
required String viewKey,
required String spendKey,
int nettype = 0,
int restoreHeight = 0}) async =>
compute<Map<String, Object>, void>(_restoreFromKeys, {
'path': path,
'password': password,
'language': language,
'address': address,
'viewKey': viewKey,
'spendKey': spendKey,
'nettype': nettype,
'restoreHeight': restoreHeight
});
Future<bool> isWalletExist({required String path}) => compute(_isWalletExist, path);

View file

@ -89,6 +89,11 @@ abstract class NanoWalletBase
Future<void> init() async {
final String type = (_derivationType == DerivationType.nano) ? "standard" : "hd";
// our "mnemonic" is actually a seedkey:
if (!_mnemonic.contains(' ')) {
_seedKey = _mnemonic;
}
if (_seedKey == null) {
if (_derivationType == DerivationType.nano) {
_seedKey = bip39.mnemonicToEntropy(_mnemonic).toUpperCase();

View file

@ -255,12 +255,18 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
@override
Future<NanoWallet> restoreFromSeed(NanoRestoreWalletFromSeedCredentials credentials) async {
if (!bip39.validateMnemonic(credentials.mnemonic)) {
throw nm.NanoMnemonicIsIncorrectException();
}
if (credentials.mnemonic.contains(' ')) {
if (!bip39.validateMnemonic(credentials.mnemonic)) {
throw nm.NanoMnemonicIsIncorrectException();
}
if (!NanoMnemomics.validateMnemonic(credentials.mnemonic.split(' '))) {
throw nm.NanoMnemonicIsIncorrectException();
if (!NanoMnemomics.validateMnemonic(credentials.mnemonic.split(' '))) {
throw nm.NanoMnemonicIsIncorrectException();
}
} else {
if (credentials.mnemonic.length != 64 && credentials.mnemonic.length != 128) {
throw Exception("Invalid seed length");
}
}
DerivationType derivationType = credentials.derivationType ?? DerivationType.nano;

View file

@ -850,11 +850,11 @@ Future setup({
getIt.registerFactoryParam<WalletRestorePage, WalletType, void>(
(type, _) => WalletRestorePage(getIt.get<WalletRestoreViewModel>(param1: type)));
getIt.registerFactoryParam<WalletRestoreChooseDerivationViewModel, WalletType, dynamic>(
getIt.registerFactoryParam<WalletRestoreChooseDerivationViewModel, dynamic, void>(
(credentials, _) =>
WalletRestoreChooseDerivationViewModel(credentials: credentials));
getIt.registerFactoryParam<WalletRestoreChooseDerivationPage, WalletType, dynamic>(
getIt.registerFactoryParam<WalletRestoreChooseDerivationPage, dynamic, void>(
(credentials, _) =>
WalletRestoreChooseDerivationPage(getIt.get<WalletRestoreChooseDerivationViewModel>(
param1: credentials,

View file

@ -14,7 +14,7 @@ class WalletRestoreChooseDerivationPage extends BasePage {
@override
Widget middle(BuildContext context) => Observer(
builder: (_) => Text(
"change me",
S.current.choose_derivation,
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
@ -32,61 +32,63 @@ class WalletRestoreChooseDerivationPage extends BasePage {
future: walletRestoreChooseDerivationViewModel.derivations,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator(); // Show loading spinner while waiting
return Center(
child: CircularProgressIndicator(),
);
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}'); // Show error if any
return Text('Error: ${snapshot.error}');
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
return Text('No derivations available'); // Show message if no derivations are available
return Text('No derivations available');
} else {
return ListView.separated(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
separatorBuilder: (_, __) => Container(padding: EdgeInsets.only(bottom: 8)),
separatorBuilder: (_, __) => SizedBox(),
itemCount: snapshot.data!.length,
itemBuilder: (__, index) {
final derivation = snapshot.data![index];
return InkWell(
onTap: () async {
Navigator.pop(context, derivation.derivationType);
},
child: Container(
margin: const EdgeInsets.only(left: 16, right: 16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30.0),
border: Border.all(
color: getIt.get<SettingsStore>().currentTheme.type == ThemeType.bright
? Color.fromRGBO(255, 255, 255, 0.2)
: Colors.transparent,
width: 1,
return Card(
margin: const EdgeInsets.all(8),
elevation: 3,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
child: InkWell(
borderRadius: BorderRadius.circular(15),
onTap: () async {
Navigator.pop(context, derivation.derivationType);
},
child: ListTile(
contentPadding: EdgeInsets.all(16),
title: Center(
child: Text(
"${derivation.derivationType.toString().split('.').last}",
style:
Theme.of(context).primaryTextTheme.labelMedium!.copyWith(fontSize: 18),
),
),
color: Theme.of(context).textTheme.titleLarge!.backgroundColor!,
),
child: Container(
margin: const EdgeInsets.only(top: 16, left: 24, right: 24, bottom: 24),
child: Column(
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
derivation.address,
style: TextStyle(
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color:
Theme.of(context).accentTextTheme.displayMedium!.backgroundColor!,
height: 1,
),
style: Theme.of(context)
.primaryTextTheme
.labelMedium!
.copyWith(fontSize: 16),
),
Text(
"${S.current.confirmed}: ${derivation.balance}",
style: TextStyle(
fontSize: 18,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color:
Theme.of(context).accentTextTheme.displayMedium!.backgroundColor!,
height: 2,
),
style: Theme.of(context)
.primaryTextTheme
.labelMedium!
.copyWith(fontSize: 16),
),
Text(
"${S.current.transactions}: ${derivation.height}",
style: Theme.of(context)
.primaryTextTheme
.labelMedium!
.copyWith(fontSize: 16),
),
],
),

View file

@ -0,0 +1,149 @@
import 'package:cake_wallet/entities/generate_name.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/src/screens/seed_language/widgets/seed_language_picker.dart';
import 'package:cake_wallet/src/widgets/seed_widget.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/core/wallet_name_validator.dart';
class WalletRestoreFromSeedKeyForm extends StatefulWidget {
WalletRestoreFromSeedKeyForm(
{Key? key,
required this.displayLanguageSelector,
required this.displayBlockHeightSelector,
required this.type,
this.blockHeightFocusNode,
this.onHeightOrDateEntered,
this.onSeedChange,
this.onLanguageChange})
: super(key: key);
final WalletType type;
final bool displayLanguageSelector;
final bool displayBlockHeightSelector;
final FocusNode? blockHeightFocusNode;
final Function(bool)? onHeightOrDateEntered;
final void Function(String)? onSeedChange;
final void Function(String)? onLanguageChange;
@override
WalletRestoreFromSeedKeyFormState createState() =>
WalletRestoreFromSeedKeyFormState('English');
}
class WalletRestoreFromSeedKeyFormState extends State<WalletRestoreFromSeedKeyForm> {
WalletRestoreFromSeedKeyFormState(this.language)
: seedWidgetStateKey = GlobalKey<SeedWidgetState>(),
blockchainHeightKey = GlobalKey<BlockchainHeightState>(),
formKey = GlobalKey<FormState>(),
languageController = TextEditingController(),
nameTextEditingController = TextEditingController();
final GlobalKey<SeedWidgetState> seedWidgetStateKey;
final GlobalKey<BlockchainHeightState> blockchainHeightKey;
final TextEditingController languageController;
final TextEditingController nameTextEditingController;
final GlobalKey<FormState> formKey;
String language;
@override
void initState() {
_setLanguageLabel(language);
super.initState();
}
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(left: 24, right: 24),
child: Column(children: [
Form(
key: formKey,
child: Stack(
alignment: Alignment.centerRight,
children: [
BaseTextFormField(
controller: nameTextEditingController,
hintText: S.of(context).wallet_name,
suffixIcon: IconButton(
onPressed: () async {
final rName = await generateName();
FocusManager.instance.primaryFocus?.unfocus();
setState(() {
nameTextEditingController.text = rName;
nameTextEditingController.selection =
TextSelection.fromPosition(TextPosition(
offset: nameTextEditingController.text.length));
});
},
icon: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0),
color: Theme.of(context).hintColor,
),
width: 34,
height: 34,
child: Image.asset(
'assets/images/refresh_icon.png',
color: Theme.of(context)
.primaryTextTheme!
.headlineMedium!
.decorationColor!,
),
),
),
validator: WalletNameValidator(),
),
],
)),
Container(height: 20),
SeedWidget(
key: seedWidgetStateKey,
language: language,
type: widget.type,
onSeedChange: widget.onSeedChange),
if (widget.displayLanguageSelector)
GestureDetector(
onTap: () async {
await showPopUp<void>(
context: context,
builder: (_) => SeedLanguagePicker(
selected: language, onItemSelected: _changeLanguage));
},
child: Container(
color: Colors.transparent,
padding: EdgeInsets.only(top: 20.0),
child: IgnorePointer(
child: BaseTextFormField(
controller: languageController,
enableInteractiveSelection: false,
readOnly: true)))),
if (widget.displayBlockHeightSelector)
BlockchainHeightWidget(
focusNode: widget.blockHeightFocusNode,
key: blockchainHeightKey,
onHeightOrDateEntered: widget.onHeightOrDateEntered,
hasDatePicker: widget.type == WalletType.monero)
]));
}
void _changeLanguage(String language) {
setState(() {
this.language = language;
seedWidgetStateKey.currentState!.changeSeedLanguage(language);
_setLanguageLabel(language);
widget.onLanguageChange?.call(language);
});
}
void _setLanguageLabel(String language) =>
languageController.text = '$language (Seed language)';
}

View file

@ -1,4 +1,5 @@
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_from_seed_key_form.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
@ -29,6 +30,7 @@ class WalletRestorePage extends BasePage {
WalletRestorePage(this.walletRestoreViewModel)
: walletRestoreFromSeedFormKey = GlobalKey<WalletRestoreFromSeedFormState>(),
walletRestoreFromKeysFormKey = GlobalKey<WalletRestoreFromKeysFromState>(),
walletRestoreFromSeedKeyFormKey = GlobalKey<WalletRestoreFromSeedKeyFormState>(),
_pages = [],
_blockHeightFocusNode = FocusNode(),
_controller = PageController(initialPage: 0) {
@ -71,6 +73,17 @@ class WalletRestorePage extends BasePage {
}
}));
break;
case WalletRestoreMode.seedKey:
_pages.add(WalletRestoreFromSeedKeyForm(
displayBlockHeightSelector: walletRestoreViewModel.hasBlockchainHeightLanguageSelector,
displayLanguageSelector: walletRestoreViewModel.hasSeedLanguageSelector,
type: walletRestoreViewModel.type,
key: walletRestoreFromSeedKeyFormKey,
onSeedChange: (String seed) {
walletRestoreViewModel.isButtonEnabled = _isValidSeedKey();
},
));
break;
case WalletRestoreMode.keys:
_pages.add(WalletRestoreFromKeysFrom(
key: walletRestoreFromKeysFormKey,
@ -101,6 +114,7 @@ class WalletRestorePage extends BasePage {
final List<Widget> _pages;
final GlobalKey<WalletRestoreFromSeedFormState> walletRestoreFromSeedFormKey;
final GlobalKey<WalletRestoreFromKeysFromState> walletRestoreFromKeysFormKey;
final GlobalKey<WalletRestoreFromSeedKeyFormState> walletRestoreFromSeedKeyFormKey;
final FocusNode _blockHeightFocusNode;
DerivationType derivationType = DerivationType.unknown;
@ -162,8 +176,14 @@ class WalletRestorePage extends BasePage {
Expanded(
child: PageView.builder(
onPageChanged: (page) {
walletRestoreViewModel.mode =
page == 0 ? WalletRestoreMode.seed : WalletRestoreMode.keys;
if (walletRestoreViewModel.type == WalletType.nano ||
walletRestoreViewModel.type == WalletType.banano) {
walletRestoreViewModel.mode =
page == 0 ? WalletRestoreMode.seed : WalletRestoreMode.seedKey;
} else {
walletRestoreViewModel.mode =
page == 0 ? WalletRestoreMode.seed : WalletRestoreMode.keys;
}
},
controller: _controller,
itemCount: _pages.length,
@ -192,7 +212,7 @@ class WalletRestorePage extends BasePage {
builder: (context) {
return LoadingPrimaryButton(
onPressed: () async {
_confirmForm(context);
await _confirmForm(context);
},
text: S.of(context).restore_recover,
color: Theme.of(context).accentTextTheme!.titleSmall!.decorationColor!,
@ -228,12 +248,22 @@ class WalletRestorePage extends BasePage {
seedWords.length != WalletRestoreViewModelBase.electrumShortSeedMnemonicLength)) {
return false;
}
final words =
walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.words.toSet();
return seedWords.toSet().difference(words).toSet().isEmpty;
}
bool _isValidSeedKey() {
final seedKey =
walletRestoreFromSeedKeyFormKey.currentState!.seedWidgetStateKey.currentState!.text;
if (seedKey.length != 64 && seedKey.length != 128) {
return false;
}
return true;
}
Map<String, dynamic> _credentials() {
final credentials = <String, dynamic>{};
@ -248,7 +278,7 @@ class WalletRestorePage extends BasePage {
credentials['name'] =
walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.text;
} else {
} else if (walletRestoreViewModel.mode == WalletRestoreMode.keys) {
credentials['address'] = walletRestoreFromKeysFormKey.currentState!.addressController.text;
credentials['viewKey'] = walletRestoreFromKeysFormKey.currentState!.viewKeyController.text;
credentials['spendKey'] = walletRestoreFromKeysFormKey.currentState!.spendKeyController.text;
@ -256,6 +286,9 @@ class WalletRestorePage extends BasePage {
walletRestoreFromKeysFormKey.currentState!.blockchainHeightKey.currentState!.height;
credentials['name'] =
walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.text;
} else if (walletRestoreViewModel.mode == WalletRestoreMode.seedKey) {
credentials['seedKey'] =
walletRestoreFromSeedKeyFormKey.currentState!.seedWidgetStateKey.currentState!.text;
}
credentials['derivationType'] = this.derivationType;
@ -263,22 +296,28 @@ class WalletRestorePage extends BasePage {
return credentials;
}
void _confirmForm(BuildContext context) async {
Future<void> _confirmForm(BuildContext context) async {
// Dismissing all visible keyboard to provide context for navigation
FocusManager.instance.primaryFocus?.unfocus();
final formContext = walletRestoreViewModel.mode == WalletRestoreMode.seed
? walletRestoreFromSeedFormKey.currentContext
: walletRestoreFromKeysFormKey.currentContext;
late BuildContext? formContext;
late GlobalKey<FormState>? formKey;
late String name;
if (walletRestoreViewModel.mode == WalletRestoreMode.seed) {
formContext = walletRestoreFromSeedFormKey.currentContext;
formKey = walletRestoreFromSeedFormKey.currentState!.formKey;
name = walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.value.text;
} else if (walletRestoreViewModel.mode == WalletRestoreMode.keys) {
formContext = walletRestoreFromKeysFormKey.currentContext;
formKey = walletRestoreFromKeysFormKey.currentState!.formKey;
name = walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.value.text;
} else if (walletRestoreViewModel.mode == WalletRestoreMode.seedKey) {
formContext = walletRestoreFromSeedKeyFormKey.currentContext;
formKey = walletRestoreFromSeedKeyFormKey.currentState!.formKey;
name = walletRestoreFromSeedKeyFormKey.currentState!.nameTextEditingController.value.text;
}
final formKey = walletRestoreViewModel.mode == WalletRestoreMode.seed
? walletRestoreFromSeedFormKey.currentState!.formKey
: walletRestoreFromKeysFormKey.currentState!.formKey;
final name = walletRestoreViewModel.mode == WalletRestoreMode.seed
? walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.value.text
: walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.value.text;
if (!formKey.currentState!.validate()) {
if (!formKey!.currentState!.validate()) {
return;
}
@ -287,25 +326,26 @@ class WalletRestorePage extends BasePage {
return;
}
walletRestoreViewModel.state = IsExecutingState();
List<DerivationType> derivationTypes =
await walletRestoreViewModel.getDerivationType(_credentials());
if (derivationTypes[0] == DerivationType.unknown || derivationTypes.length > 0) {
// push screen to choose the derivation type:
var derivationType =
await Navigator.of(context).pushNamed(Routes.restoreWalletChooseDerivation, arguments: {
"walletType": walletRestoreViewModel.type,
"credentials": _credentials(),
}) as DerivationType?;
var derivationType = await Navigator.of(context)
.pushNamed(Routes.restoreWalletChooseDerivation, arguments: _credentials())
as DerivationType?;
if (derivationType == null) {
walletRestoreViewModel.state = InitialExecutionState();
return;
}
this.derivationType = derivationType;
}
print(this.derivationType);
walletRestoreViewModel.state = InitialExecutionState();
// todo: re-enable
// walletRestoreViewModel.create(options: _credentials());
walletRestoreViewModel.create(options: _credentials());
}
Future<void> showNameExistsAlert(BuildContext context) {

View file

@ -1 +1 @@
enum WalletRestoreMode { seed, keys, txids }
enum WalletRestoreMode { seed, keys, txids, seedKey }

View file

@ -26,7 +26,6 @@ abstract class WalletRestoreChooseDerivationViewModelBase with Store {
WalletRestoreChooseDerivationViewModelBase({required this.credentials})
: mode = WalletRestoreMode.seed {}
dynamic credentials;
@observable
@ -35,7 +34,7 @@ abstract class WalletRestoreChooseDerivationViewModelBase with Store {
Future<List<Derivation>> get derivations async {
var list = <Derivation>[];
switch (getIt.get<AppStore>().wallet!.type) {
switch ((await getIt.get<AppStore>().wallet!.type)) {
case WalletType.nano:
var seed = credentials['seed'] as String;
var bip39Info =
@ -43,14 +42,6 @@ abstract class WalletRestoreChooseDerivationViewModelBase with Store {
var standardInfo =
await NanoWalletService.getInfoFromSeedOrMnemonic(DerivationType.nano, mnemonic: seed);
list.add(Derivation(
NanoUtil.getRawAsUsableString(bip39Info["balance"] as String, NanoUtil.rawPerNano),
bip39Info["address"] as String,
DerivationType.bip39,
int.parse(
bip39Info["confirmation_height"] as String,
),
));
list.add(Derivation(
NanoUtil.getRawAsUsableString(standardInfo["balance"] as String, NanoUtil.rawPerNano),
standardInfo["address"] as String,
@ -59,6 +50,16 @@ abstract class WalletRestoreChooseDerivationViewModelBase with Store {
standardInfo["confirmation_height"] as String,
),
));
list.add(Derivation(
NanoUtil.getRawAsUsableString(bip39Info["balance"] as String, NanoUtil.rawPerNano),
bip39Info["address"] as String,
DerivationType.bip39,
int.parse(
bip39Info["confirmation_height"] as String,
),
));
break;
default:

View file

@ -26,8 +26,10 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
Box<WalletInfo> walletInfoSource,
{required WalletType type})
: availableModes = (type == WalletType.monero || type == WalletType.haven)
? WalletRestoreMode.values
: [WalletRestoreMode.seed],
? [WalletRestoreMode.seed, WalletRestoreMode.keys, WalletRestoreMode.txids]
: (type == WalletType.nano || type == WalletType.banano)
? [WalletRestoreMode.seed, WalletRestoreMode.seedKey]
: [WalletRestoreMode.seed],
hasSeedLanguageSelector = type == WalletType.monero || type == WalletType.haven,
hasBlockchainHeightLanguageSelector = type == WalletType.monero || type == WalletType.haven,
isButtonEnabled = false,
@ -121,7 +123,8 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
@override
Future<List<DerivationType>> getDerivationType(dynamic options) async {
final seed = options['seed'] as String?;
final seedKey = options['seedKey'] as String?;
final mnemonic = options['seed'] as String?;
switch (type) {
// case WalletType.bitcoin:
@ -131,7 +134,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
// return bitcoin!.createBitcoinRestoreWalletFromSeedCredentials(
// name: name, mnemonic: seed, password: password);
case WalletType.nano:
return await NanoWalletService.compareDerivationMethods(mnemonic: seed, seedKey: null);
return await NanoWalletService.compareDerivationMethods(mnemonic: mnemonic, seedKey: seedKey);
default:
break;
}

View file

@ -678,5 +678,6 @@
"support_title_guides": "أدلة محفظة كعكة",
"support_description_guides": "توثيق ودعم القضايا المشتركة",
"support_title_other_links": "روابط دعم أخرى",
"support_description_other_links": "انضم إلى مجتمعاتنا أو تصل إلينا شركائنا من خلال أساليب أخرى"
}
"support_description_other_links": "انضم إلى مجتمعاتنا أو تصل إلينا شركائنا من خلال أساليب أخرى",
"choose_derivation": "اختر اشتقاق المحفظة"
}

View file

@ -674,5 +674,6 @@
"support_title_guides": "Ръководства за портфейл за торта",
"support_description_guides": "Документация и подкрепа за общи проблеми",
"support_title_other_links": "Други връзки за поддръжка",
"support_description_other_links": "Присъединете се към нашите общности или се свържете с нас нашите партньори чрез други методи"
}
"support_description_other_links": "Присъединете се към нашите общности или се свържете с нас нашите партньори чрез други методи",
"choose_derivation": "Изберете производно на портфейла"
}

View file

@ -674,5 +674,6 @@
"support_title_guides": "Průvodce peněženkami dortu",
"support_description_guides": "Dokumentace a podpora běžných otázek",
"support_title_other_links": "Další odkazy na podporu",
"support_description_other_links": "Připojte se k našim komunitám nebo se k nám oslovte další metody"
}
"support_description_other_links": "Připojte se k našim komunitám nebo se k nám oslovte další metody",
"choose_derivation": "Vyberte derivaci peněženky"
}

View file

@ -682,5 +682,6 @@
"support_title_guides": "Cake Wallet Guides",
"support_description_guides": "Dokumentation und Hilfe für bekannte Probleme",
"support_title_other_links": "Andere Support-Links",
"support_description_other_links": "Treten Sie unseren Communities bei oder erreichen Sie uns oder unsere Partner über andere Methoden"
}
"support_description_other_links": "Treten Sie unseren Communities bei oder erreichen Sie uns oder unsere Partner über andere Methoden",
"choose_derivation": "Wählen Sie Brieftaschenableitung"
}

View file

@ -682,5 +682,6 @@
"support_title_guides": "Cake Wallet guides",
"support_description_guides": "Documentation and support for common issues",
"support_title_other_links": "Other support links",
"support_description_other_links": "Join our communities or reach us our our partners through other methods"
}
"support_description_other_links": "Join our communities or reach us our our partners through other methods",
"choose_derivation": "Choose Wallet Derivation"
}

View file

@ -682,5 +682,6 @@
"support_title_guides": "Guías de billetera para pastel",
"support_description_guides": "Documentación y apoyo para problemas comunes",
"support_title_other_links": "Otros enlaces de soporte",
"support_description_other_links": "Únase a nuestras comunidades o comuníquese con nosotros nuestros socios a través de otros métodos"
}
"support_description_other_links": "Únase a nuestras comunidades o comuníquese con nosotros nuestros socios a través de otros métodos",
"choose_derivation": "Elija la derivación de la billetera"
}

View file

@ -682,5 +682,6 @@
"support_title_guides": "Guides de portefeuille à gâteau",
"support_description_guides": "Documentation et soutien aux problèmes communs",
"support_title_other_links": "Autres liens d'assistance",
"support_description_other_links": "Rejoignez nos communautés ou contactez-nous nos partenaires à travers d'autres méthodes"
}
"support_description_other_links": "Rejoignez nos communautés ou contactez-nous nos partenaires à travers d'autres méthodes",
"choose_derivation": "Choisissez la dérivation du portefeuille"
}

View file

@ -660,5 +660,6 @@
"support_title_guides": "Jagorar Cake",
"support_description_guides": "Tallafi da tallafi don batutuwa na yau da kullun",
"support_title_other_links": "Sauran hanyoyin tallafi",
"support_description_other_links": "Kasance tare da al'ummominmu ko kuma ka kai mu abokanmu ta hanyar wasu hanyoyi"
}
"support_description_other_links": "Kasance tare da al'ummominmu ko kuma ka kai mu abokanmu ta hanyar wasu hanyoyi",
"choose_derivation": "Zaɓi walatawa"
}

View file

@ -682,5 +682,6 @@
"support_title_guides": "केक वॉलेट गाइड",
"support_description_guides": "सामान्य मुद्दों के लिए प्रलेखन और समर्थन",
"support_title_other_links": "अन्य समर्थन लिंक",
"support_description_other_links": "हमारे समुदायों में शामिल हों या अन्य तरीकों के माध्यम से हमारे साथी तक पहुंचें"
}
"support_description_other_links": "हमारे समुदायों में शामिल हों या अन्य तरीकों के माध्यम से हमारे साथी तक पहुंचें",
"choose_derivation": "वॉलेट व्युत्पत्ति चुनें"
}

View file

@ -680,5 +680,6 @@
"support_title_guides": "Vodiči za torte",
"support_description_guides": "Dokumentacija i podrška za uobičajena pitanja",
"support_title_other_links": "Ostale veze za podršku",
"support_description_other_links": "Pridružite se našim zajednicama ili nam dosegnu naše partnere drugim metodama"
}
"support_description_other_links": "Pridružite se našim zajednicama ili nam dosegnu naše partnere drugim metodama",
"choose_derivation": "Odaberite izvedbu novčanika"
}

View file

@ -670,5 +670,6 @@
"support_title_guides": "Panduan Dompet Kue",
"support_description_guides": "Dokumentasi dan dukungan untuk masalah umum",
"support_title_other_links": "Tautan dukungan lainnya",
"support_description_other_links": "Bergabunglah dengan komunitas kami atau hubungi kami mitra kami melalui metode lain"
}
"support_description_other_links": "Bergabunglah dengan komunitas kami atau hubungi kami mitra kami melalui metode lain",
"choose_derivation": "Pilih dompet dompet"
}

View file

@ -682,5 +682,6 @@
"support_title_guides": "Guide del portafoglio per torte",
"support_description_guides": "Documentazione e supporto per problemi comuni",
"support_title_other_links": "Altri collegamenti di supporto",
"support_description_other_links": "Unisciti alle nostre comunità o raggiungici i nostri partner attraverso altri metodi"
}
"support_description_other_links": "Unisciti alle nostre comunità o raggiungici i nostri partner attraverso altri metodi",
"choose_derivation": "Scegli la derivazione del portafoglio"
}

View file

@ -682,5 +682,6 @@
"support_title_guides": "ケーキウォレットガイド",
"support_description_guides": "一般的な問題のドキュメントとサポート",
"support_title_other_links": "その他のサポートリンク",
"support_description_other_links": "私たちのコミュニティに参加するか、他の方法を通して私たちのパートナーに連絡してください"
}
"support_description_other_links": "私たちのコミュニティに参加するか、他の方法を通して私たちのパートナーに連絡してください",
"choose_derivation": "ウォレット派生を選択します"
}

View file

@ -680,5 +680,6 @@
"support_title_guides": "케이크 지갑 가이드",
"support_description_guides": "일반적인 문제에 대한 문서화 및 지원",
"support_title_other_links": "다른 지원 링크",
"support_description_other_links": "다른 방법을 통해 커뮤니티에 가입하거나 파트너에게 연락하십시오."
}
"support_description_other_links": "다른 방법을 통해 커뮤니티에 가입하거나 파트너에게 연락하십시오.",
"choose_derivation": "지갑 파생을 선택하십시오"
}

View file

@ -680,5 +680,6 @@
"support_title_guides": "ကိတ်မုန့်ပိုက်ဆံအိတ်လမ်းညွှန်များ",
"support_description_guides": "ဘုံပြ issues နာများအတွက်စာရွက်စာတမ်းများနှင့်ထောက်ခံမှု",
"support_title_other_links": "အခြားအထောက်အပံ့လင့်များ",
"support_description_other_links": "ကျွန်ုပ်တို့၏လူမှုအသိုင်းအဝိုင်းများသို့ 0 င်ရောက်ပါ"
}
"support_description_other_links": "ကျွန်ုပ်တို့၏လူမှုအသိုင်းအဝိုင်းများသို့ 0 င်ရောက်ပါ",
"choose_derivation": "ပိုက်ဆံအိတ်ကိုရွေးချယ်ပါ"
}

View file

@ -682,5 +682,6 @@
"support_title_guides": "Cake -portemonnee gidsen",
"support_description_guides": "Documentatie en ondersteuning voor gemeenschappelijke problemen",
"support_title_other_links": "Andere ondersteuningslinks",
"support_description_other_links": "Word lid van onze gemeenschappen of bereik ons onze partners via andere methoden"
}
"support_description_other_links": "Word lid van onze gemeenschappen of bereik ons onze partners via andere methoden",
"choose_derivation": "Kies portemonnee -afleiding"
}

View file

@ -682,5 +682,6 @@
"support_title_guides": "Przewodniki portfela ciasta",
"support_description_guides": "Dokumentacja i wsparcie dla typowych problemów",
"support_title_other_links": "Inne linki wsparcia",
"support_description_other_links": "Dołącz do naszych społeczności lub skontaktuj się z nami naszymi partnerami za pomocą innych metod"
}
"support_description_other_links": "Dołącz do naszych społeczności lub skontaktuj się z nami naszymi partnerami za pomocą innych metod",
"choose_derivation": "Wybierz wyprowadzenie portfela"
}

View file

@ -681,5 +681,6 @@
"support_title_guides": "Guias da carteira de bolo",
"support_description_guides": "Documentação e suporte para problemas comuns",
"support_title_other_links": "Outros links de suporte",
"support_description_other_links": "Junte -se às nossas comunidades ou chegue a nós nossos parceiros por meio de outros métodos"
}
"support_description_other_links": "Junte -se às nossas comunidades ou chegue a nós nossos parceiros por meio de outros métodos",
"choose_derivation": "Escolha a derivação da carteira"
}

View file

@ -682,5 +682,6 @@
"support_title_guides": "Корт -гиды",
"support_description_guides": "Документация и поддержка общих вопросов",
"support_title_other_links": "Другие ссылки на поддержку",
"support_description_other_links": "Присоединяйтесь к нашим сообществам или охватите нас наших партнеров с помощью других методов"
}
"support_description_other_links": "Присоединяйтесь к нашим сообществам или охватите нас наших партнеров с помощью других методов",
"choose_derivation": "Выберите вывод кошелька"
}

View file

@ -680,5 +680,6 @@
"support_title_guides": "คู่มือกระเป๋าเงินเค้ก",
"support_description_guides": "เอกสารและการสนับสนุนสำหรับปัญหาทั่วไป",
"support_title_other_links": "ลิงค์สนับสนุนอื่น ๆ",
"support_description_other_links": "เข้าร่วมชุมชนของเราหรือเข้าถึงเราพันธมิตรของเราผ่านวิธีการอื่น ๆ"
}
"support_description_other_links": "เข้าร่วมชุมชนของเราหรือเข้าถึงเราพันธมิตรของเราผ่านวิธีการอื่น ๆ",
"choose_derivation": "เลือก Wallet Derivation"
}

View file

@ -680,5 +680,6 @@
"support_title_guides": "Kek Cüzdan Kılavuzları",
"support_description_guides": "Ortak sorunlara belge ve destek",
"support_title_other_links": "Diğer destek bağlantıları",
"support_description_other_links": "Topluluklarımıza katılın veya ortaklarımıza diğer yöntemlerle bize ulaşın"
}
"support_description_other_links": "Topluluklarımıza katılın veya ortaklarımıza diğer yöntemlerle bize ulaşın",
"choose_derivation": "Cüzdan türevini seçin"
}

View file

@ -682,5 +682,6 @@
"support_title_guides": "Поклики для гаманців тортів",
"support_description_guides": "Документація та підтримка загальних питань",
"support_title_other_links": "Інші посилання на підтримку",
"support_description_other_links": "Приєднуйтесь до наших спільнот або досягайте нас нашими партнерами іншими методами"
}
"support_description_other_links": "Приєднуйтесь до наших спільнот або досягайте нас нашими партнерами іншими методами",
"choose_derivation": "Виберіть деривацію гаманця"
}

View file

@ -674,5 +674,6 @@
"support_title_guides": "کیک پرس گائڈز",
"support_description_guides": "عام مسائل کے لئے دستاویزات اور مدد",
"support_title_other_links": "دوسرے سپورٹ لنکس",
"support_description_other_links": "ہماری برادریوں میں شامل ہوں یا دوسرے طریقوں سے ہمارے شراکت داروں تک پہنچیں"
}
"support_description_other_links": "ہماری برادریوں میں شامل ہوں یا دوسرے طریقوں سے ہمارے شراکت داروں تک پہنچیں",
"choose_derivation": "پرس سے ماخوذ منتخب کریں"
}

View file

@ -676,5 +676,6 @@
"support_title_guides": "Akara oyinbo Awọn Itọsọna Awọki oyinbo",
"support_description_guides": "Iwe ati atilẹyin fun awọn ọran ti o wọpọ",
"support_title_other_links": "Awọn ọna asopọ atilẹyin miiran",
"support_description_other_links": "Darapọ mọ awọn agbegbe wa tabi de wa awọn alabaṣepọ wa nipasẹ awọn ọna miiran"
}
"support_description_other_links": "Darapọ mọ awọn agbegbe wa tabi de wa awọn alabaṣepọ wa nipasẹ awọn ọna miiran",
"choose_derivation": "Yan awọn apamọwọ apamọwọ"
}

View file

@ -681,5 +681,6 @@
"support_title_guides": "蛋糕钱包指南",
"support_description_guides": "对常见问题的文档和支持",
"support_title_other_links": "其他支持链接",
"support_description_other_links": "加入我们的社区或通过其他方法与我们联系我们的合作伙伴"
}
"support_description_other_links": "加入我们的社区或通过其他方法与我们联系我们的合作伙伴",
"choose_derivation": "选择钱包推导"
}