mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-01-03 17:40:43 +00:00
Cw 467 mark change outputs in unspent outputs list (#1137)
* CW-490 Use native Coin Freeze * CW-467 Code cleanup * CW-467 Fix native Code * CW-467 Extend Unspend * CW-467 Add isChange * CW-467 Minor Fixes * CW-467 Add isChange to Electrum Unspents * CW-467 Localize Change Tag * CW-467 Fix frozen balance showing on other monero wallets * CW-467 Fix frozen balance showing on other monero wallets
This commit is contained in:
parent
4cffc8d4c5
commit
8084f490b5
42 changed files with 296 additions and 261 deletions
|
@ -2,41 +2,41 @@ import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'package:cw_core/pending_transaction.dart';
|
|
||||||
import 'package:cw_core/unspent_coins_info.dart';
|
|
||||||
import 'package:cw_core/wallet_type.dart';
|
|
||||||
import 'package:hive/hive.dart';
|
|
||||||
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
|
|
||||||
import 'package:mobx/mobx.dart';
|
|
||||||
import 'package:rxdart/subjects.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||||
import 'package:cw_bitcoin/electrum_transaction_info.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:cw_core/pathForWallet.dart';
|
|
||||||
import 'package:cw_bitcoin/address_to_output_script.dart';
|
import 'package:cw_bitcoin/address_to_output_script.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||||
import 'package:cw_bitcoin/electrum_balance.dart';
|
|
||||||
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
|
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
|
||||||
import 'package:cw_bitcoin/electrum_transaction_history.dart';
|
|
||||||
import 'package:cw_bitcoin/bitcoin_transaction_no_inputs_exception.dart';
|
import 'package:cw_bitcoin/bitcoin_transaction_no_inputs_exception.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
|
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_transaction_wrong_balance_exception.dart';
|
import 'package:cw_bitcoin/bitcoin_transaction_wrong_balance_exception.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_unspent.dart';
|
import 'package:cw_bitcoin/bitcoin_unspent.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_wallet_keys.dart';
|
import 'package:cw_bitcoin/bitcoin_wallet_keys.dart';
|
||||||
|
import 'package:cw_bitcoin/electrum.dart';
|
||||||
|
import 'package:cw_bitcoin/electrum_balance.dart';
|
||||||
|
import 'package:cw_bitcoin/electrum_transaction_history.dart';
|
||||||
|
import 'package:cw_bitcoin/electrum_transaction_info.dart';
|
||||||
|
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
|
||||||
import 'package:cw_bitcoin/file.dart';
|
import 'package:cw_bitcoin/file.dart';
|
||||||
import 'package:cw_bitcoin/pending_bitcoin_transaction.dart';
|
import 'package:cw_bitcoin/pending_bitcoin_transaction.dart';
|
||||||
import 'package:cw_bitcoin/script_hash.dart';
|
import 'package:cw_bitcoin/script_hash.dart';
|
||||||
import 'package:cw_bitcoin/utils.dart';
|
import 'package:cw_bitcoin/utils.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
|
||||||
import 'package:cw_core/node.dart';
|
|
||||||
import 'package:cw_core/sync_status.dart';
|
|
||||||
import 'package:cw_core/transaction_priority.dart';
|
|
||||||
import 'package:cw_core/wallet_info.dart';
|
|
||||||
import 'package:cw_bitcoin/electrum.dart';
|
|
||||||
import 'package:hex/hex.dart';
|
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:cw_core/node.dart';
|
||||||
import 'package:bip32/bip32.dart';
|
import 'package:cw_core/pathForWallet.dart';
|
||||||
|
import 'package:cw_core/pending_transaction.dart';
|
||||||
|
import 'package:cw_core/sync_status.dart';
|
||||||
|
import 'package:cw_core/transaction_direction.dart';
|
||||||
|
import 'package:cw_core/transaction_priority.dart';
|
||||||
|
import 'package:cw_core/unspent_coins_info.dart';
|
||||||
|
import 'package:cw_core/wallet_base.dart';
|
||||||
|
import 'package:cw_core/wallet_info.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:hex/hex.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
|
import 'package:rxdart/subjects.dart';
|
||||||
|
|
||||||
part 'electrum_wallet.g.dart';
|
part 'electrum_wallet.g.dart';
|
||||||
|
|
||||||
|
@ -79,8 +79,7 @@ abstract class ElectrumWalletBase
|
||||||
}
|
}
|
||||||
|
|
||||||
static bitcoin.HDWallet bitcoinCashHDWallet(Uint8List seedBytes) =>
|
static bitcoin.HDWallet bitcoinCashHDWallet(Uint8List seedBytes) =>
|
||||||
bitcoin.HDWallet.fromSeed(seedBytes)
|
bitcoin.HDWallet.fromSeed(seedBytes).derivePath("m/44'/145'/0'/0");
|
||||||
.derivePath("m/44'/145'/0'/0");
|
|
||||||
|
|
||||||
static int estimatedTransactionSize(int inputsCount, int outputsCounts) =>
|
static int estimatedTransactionSize(int inputsCount, int outputsCounts) =>
|
||||||
inputsCount * 146 + outputsCounts * 33 + 8;
|
inputsCount * 146 + outputsCounts * 33 + 8;
|
||||||
|
@ -295,7 +294,9 @@ abstract class ElectrumWalletBase
|
||||||
final p2wpkh = bitcoin
|
final p2wpkh = bitcoin
|
||||||
.P2WPKH(
|
.P2WPKH(
|
||||||
data: generatePaymentData(
|
data: generatePaymentData(
|
||||||
hd: input.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd,
|
hd: input.bitcoinAddressRecord.isHidden
|
||||||
|
? walletAddresses.sideHd
|
||||||
|
: walletAddresses.mainHd,
|
||||||
index: input.bitcoinAddressRecord.index),
|
index: input.bitcoinAddressRecord.index),
|
||||||
network: networkType)
|
network: networkType)
|
||||||
.data;
|
.data;
|
||||||
|
@ -467,18 +468,20 @@ abstract class ElectrumWalletBase
|
||||||
Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type);
|
Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type);
|
||||||
|
|
||||||
Future<void> updateUnspent() async {
|
Future<void> updateUnspent() async {
|
||||||
final unspent = await Future.wait(walletAddresses
|
final unspent = await Future.wait(walletAddresses.addresses.map((address) => electrumClient
|
||||||
.addresses.map((address) => electrumClient
|
|
||||||
.getListUnspentWithAddress(address.address, networkType)
|
.getListUnspentWithAddress(address.address, networkType)
|
||||||
.then((unspent) => unspent
|
.then((unspent) => unspent.map((unspent) {
|
||||||
.map((unspent) {
|
|
||||||
try {
|
try {
|
||||||
return BitcoinUnspent.fromJSON(address, unspent);
|
return BitcoinUnspent.fromJSON(address, unspent);
|
||||||
} catch(_) {
|
} catch (_) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}).whereNotNull())));
|
}).whereNotNull())));
|
||||||
unspentCoins = unspent.expand((e) => e).toList();
|
unspentCoins = unspent.expand((e) => e).toList();
|
||||||
|
unspentCoins.forEach((coin) async {
|
||||||
|
final tx = await fetchTransactionInfo(hash: coin.hash, height: 0);
|
||||||
|
coin.isChange = tx!.direction == TransactionDirection.outgoing;
|
||||||
|
});
|
||||||
|
|
||||||
if (unspentCoinsInfo.isEmpty) {
|
if (unspentCoinsInfo.isEmpty) {
|
||||||
unspentCoins.forEach((coin) => _addCoinInfo(coin));
|
unspentCoins.forEach((coin) => _addCoinInfo(coin));
|
||||||
|
@ -515,6 +518,7 @@ abstract class ElectrumWalletBase
|
||||||
address: coin.bitcoinAddressRecord.address,
|
address: coin.bitcoinAddressRecord.address,
|
||||||
value: coin.value,
|
value: coin.value,
|
||||||
vout: coin.vout,
|
vout: coin.vout,
|
||||||
|
isChange: coin.isChange,
|
||||||
);
|
);
|
||||||
|
|
||||||
await unspentCoinsInfo.add(newInfo);
|
await unspentCoinsInfo.add(newInfo);
|
||||||
|
@ -657,7 +661,7 @@ abstract class ElectrumWalletBase
|
||||||
final addresses = walletAddresses.addresses.toList();
|
final addresses = walletAddresses.addresses.toList();
|
||||||
final balanceFutures = <Future<Map<String, dynamic>>>[];
|
final balanceFutures = <Future<Map<String, dynamic>>>[];
|
||||||
for (var i = 0; i < addresses.length; i++) {
|
for (var i = 0; i < addresses.length; i++) {
|
||||||
final addressRecord = addresses[i] ;
|
final addressRecord = addresses[i];
|
||||||
final sh = scriptHash(addressRecord.address, networkType: networkType);
|
final sh = scriptHash(addressRecord.address, networkType: networkType);
|
||||||
final balanceFuture = electrumClient.getBalance(sh);
|
final balanceFuture = electrumClient.getBalance(sh);
|
||||||
balanceFutures.add(balanceFuture);
|
balanceFutures.add(balanceFuture);
|
||||||
|
|
|
@ -14,7 +14,9 @@ class UnspentCoinsInfo extends HiveObject {
|
||||||
required this.address,
|
required this.address,
|
||||||
required this.vout,
|
required this.vout,
|
||||||
required this.value,
|
required this.value,
|
||||||
this.keyImage = null
|
this.keyImage = null,
|
||||||
|
this.isChange = false,
|
||||||
|
this.accountIndex = 0
|
||||||
});
|
});
|
||||||
|
|
||||||
static const typeId = UNSPENT_COINS_INFO_TYPE_ID;
|
static const typeId = UNSPENT_COINS_INFO_TYPE_ID;
|
||||||
|
@ -48,6 +50,12 @@ class UnspentCoinsInfo extends HiveObject {
|
||||||
@HiveField(8, defaultValue: null)
|
@HiveField(8, defaultValue: null)
|
||||||
String? keyImage;
|
String? keyImage;
|
||||||
|
|
||||||
|
@HiveField(9, defaultValue: false)
|
||||||
|
bool isChange;
|
||||||
|
|
||||||
|
@HiveField(10, defaultValue: 0)
|
||||||
|
int accountIndex;
|
||||||
|
|
||||||
String get note => noteRaw ?? '';
|
String get note => noteRaw ?? '';
|
||||||
|
|
||||||
set note(String value) => noteRaw = value;
|
set note(String value) => noteRaw = value;
|
||||||
|
|
|
@ -2,6 +2,7 @@ class Unspent {
|
||||||
Unspent(this.address, this.hash, this.value, this.vout, this.keyImage)
|
Unspent(this.address, this.hash, this.value, this.vout, this.keyImage)
|
||||||
: isSending = true,
|
: isSending = true,
|
||||||
isFrozen = false,
|
isFrozen = false,
|
||||||
|
isChange = false,
|
||||||
note = '';
|
note = '';
|
||||||
|
|
||||||
final String address;
|
final String address;
|
||||||
|
@ -10,6 +11,7 @@ class Unspent {
|
||||||
final int vout;
|
final int vout;
|
||||||
final String? keyImage;
|
final String? keyImage;
|
||||||
|
|
||||||
|
bool isChange;
|
||||||
bool isSending;
|
bool isSending;
|
||||||
bool isFrozen;
|
bool isFrozen;
|
||||||
String note;
|
String note;
|
||||||
|
|
|
@ -841,6 +841,12 @@ extern "C"
|
||||||
return m_transaction_history->count();
|
return m_transaction_history->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TransactionInfoRow* get_transaction(char * txId)
|
||||||
|
{
|
||||||
|
Monero::TransactionInfo *row = m_transaction_history->transaction(std::string(txId));
|
||||||
|
return new TransactionInfoRow(row);
|
||||||
|
}
|
||||||
|
|
||||||
int LedgerExchange(
|
int LedgerExchange(
|
||||||
unsigned char *command,
|
unsigned char *command,
|
||||||
unsigned int cmd_len,
|
unsigned int cmd_len,
|
||||||
|
@ -970,6 +976,15 @@ extern "C"
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void freeze_coin(int index)
|
||||||
|
{
|
||||||
|
m_coins->setFrozen(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void thaw_coin(int index)
|
||||||
|
{
|
||||||
|
m_coins->thaw(index);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,20 @@ final coinNative = moneroApi
|
||||||
.lookup<NativeFunction<coin>>('coin')
|
.lookup<NativeFunction<coin>>('coin')
|
||||||
.asFunction<GetCoin>();
|
.asFunction<GetCoin>();
|
||||||
|
|
||||||
|
final freezeCoinNative = moneroApi
|
||||||
|
.lookup<NativeFunction<freeze_coin>>('freeze_coin')
|
||||||
|
.asFunction<FreezeCoin>();
|
||||||
|
|
||||||
|
final thawCoinNative = moneroApi
|
||||||
|
.lookup<NativeFunction<thaw_coin>>('thaw_coin')
|
||||||
|
.asFunction<ThawCoin>();
|
||||||
|
|
||||||
void refreshCoins(int accountIndex) => refreshCoinsNative(accountIndex);
|
void refreshCoins(int accountIndex) => refreshCoinsNative(accountIndex);
|
||||||
|
|
||||||
int countOfCoins() => coinsCountNative();
|
int countOfCoins() => coinsCountNative();
|
||||||
|
|
||||||
CoinsInfoRow getCoin(int index) => coinNative(index).ref;
|
CoinsInfoRow getCoin(int index) => coinNative(index).ref;
|
||||||
|
|
||||||
|
void freezeCoin(int index) => freezeCoinNative(index);
|
||||||
|
|
||||||
|
void thawCoin(int index) => thawCoinNative(index);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:ffi';
|
import 'dart:ffi';
|
||||||
import 'package:cw_monero/api/structs/coins_info_row.dart';
|
import 'package:cw_monero/api/structs/coins_info_row.dart';
|
||||||
import 'package:cw_monero/api/structs/pending_transaction.dart';
|
import 'package:cw_monero/api/structs/pending_transaction.dart';
|
||||||
|
import 'package:cw_monero/api/structs/transaction_info_row.dart';
|
||||||
import 'package:cw_monero/api/structs/ut8_box.dart';
|
import 'package:cw_monero/api/structs/ut8_box.dart';
|
||||||
import 'package:ffi/ffi.dart';
|
import 'package:ffi/ffi.dart';
|
||||||
|
|
||||||
|
@ -81,6 +82,8 @@ typedef account_set_label = Void Function(Int32 accountIndex, Pointer<Utf8> labe
|
||||||
|
|
||||||
typedef transactions_refresh = Void Function();
|
typedef transactions_refresh = Void Function();
|
||||||
|
|
||||||
|
typedef get_transaction = Pointer<TransactionInfoRow> Function(Pointer<Utf8> txId);
|
||||||
|
|
||||||
typedef get_tx_key = Pointer<Utf8>? Function(Pointer<Utf8> txId);
|
typedef get_tx_key = Pointer<Utf8>? Function(Pointer<Utf8> txId);
|
||||||
|
|
||||||
typedef transactions_count = Int64 Function();
|
typedef transactions_count = Int64 Function();
|
||||||
|
@ -139,3 +142,7 @@ typedef coins_count = Int64 Function();
|
||||||
// typedef coins_from_txid = Pointer<CoinsInfoRow> Function(Pointer<Utf8> txid);
|
// typedef coins_from_txid = Pointer<CoinsInfoRow> Function(Pointer<Utf8> txid);
|
||||||
|
|
||||||
typedef coin = Pointer<CoinsInfoRow> Function(Int32 index);
|
typedef coin = Pointer<CoinsInfoRow> Function(Int32 index);
|
||||||
|
|
||||||
|
typedef freeze_coin = Void Function(Int32 index);
|
||||||
|
|
||||||
|
typedef thaw_coin = Void Function(Int32 index);
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
import 'dart:ffi';
|
import 'dart:ffi';
|
||||||
|
|
||||||
import 'package:cw_monero/api/convert_utf8_to_string.dart';
|
import 'package:cw_monero/api/convert_utf8_to_string.dart';
|
||||||
|
import 'package:cw_monero/api/exceptions/creation_transaction_exception.dart';
|
||||||
|
import 'package:cw_monero/api/monero_api.dart';
|
||||||
import 'package:cw_monero/api/monero_output.dart';
|
import 'package:cw_monero/api/monero_output.dart';
|
||||||
|
import 'package:cw_monero/api/signatures.dart';
|
||||||
|
import 'package:cw_monero/api/structs/pending_transaction.dart';
|
||||||
|
import 'package:cw_monero/api/structs/transaction_info_row.dart';
|
||||||
import 'package:cw_monero/api/structs/ut8_box.dart';
|
import 'package:cw_monero/api/structs/ut8_box.dart';
|
||||||
|
import 'package:cw_monero/api/types.dart';
|
||||||
import 'package:ffi/ffi.dart';
|
import 'package:ffi/ffi.dart';
|
||||||
import 'package:flutter/foundation.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
|
final transactionsRefreshNative = moneroApi
|
||||||
.lookup<NativeFunction<transactions_refresh>>('transactions_refresh')
|
.lookup<NativeFunction<transactions_refresh>>('transactions_refresh')
|
||||||
|
@ -38,6 +39,10 @@ final transactionCommitNative = moneroApi
|
||||||
final getTxKeyNative =
|
final getTxKeyNative =
|
||||||
moneroApi.lookup<NativeFunction<get_tx_key>>('get_tx_key').asFunction<GetTxKey>();
|
moneroApi.lookup<NativeFunction<get_tx_key>>('get_tx_key').asFunction<GetTxKey>();
|
||||||
|
|
||||||
|
final getTransactionNative = moneroApi
|
||||||
|
.lookup<NativeFunction<get_transaction>>('get_transaction')
|
||||||
|
.asFunction<GetTransaction>();
|
||||||
|
|
||||||
String getTxKey(String txId) {
|
String getTxKey(String txId) {
|
||||||
final txIdPointer = txId.toNativeUtf8();
|
final txIdPointer = txId.toNativeUtf8();
|
||||||
final keyPointer = getTxKeyNative(txIdPointer);
|
final keyPointer = getTxKeyNative(txIdPointer);
|
||||||
|
@ -65,6 +70,11 @@ List<TransactionInfoRow> getAllTransactions() {
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TransactionInfoRow getTransaction(String txId) {
|
||||||
|
final txIdPointer = txId.toNativeUtf8();
|
||||||
|
return getTransactionNative(txIdPointer).ref;
|
||||||
|
}
|
||||||
|
|
||||||
PendingTransactionDescription createTransactionSync(
|
PendingTransactionDescription createTransactionSync(
|
||||||
{required String address,
|
{required String address,
|
||||||
required String paymentId,
|
required String paymentId,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:ffi';
|
import 'dart:ffi';
|
||||||
import 'package:cw_monero/api/structs/coins_info_row.dart';
|
import 'package:cw_monero/api/structs/coins_info_row.dart';
|
||||||
import 'package:cw_monero/api/structs/pending_transaction.dart';
|
import 'package:cw_monero/api/structs/pending_transaction.dart';
|
||||||
|
import 'package:cw_monero/api/structs/transaction_info_row.dart';
|
||||||
import 'package:cw_monero/api/structs/ut8_box.dart';
|
import 'package:cw_monero/api/structs/ut8_box.dart';
|
||||||
import 'package:ffi/ffi.dart';
|
import 'package:ffi/ffi.dart';
|
||||||
|
|
||||||
|
@ -81,6 +82,8 @@ typedef AccountSetLabel = void Function(int accountIndex, Pointer<Utf8> label);
|
||||||
|
|
||||||
typedef TransactionsRefresh = void Function();
|
typedef TransactionsRefresh = void Function();
|
||||||
|
|
||||||
|
typedef GetTransaction = Pointer<TransactionInfoRow> Function(Pointer<Utf8> txId);
|
||||||
|
|
||||||
typedef GetTxKey = Pointer<Utf8>? Function(Pointer<Utf8> txId);
|
typedef GetTxKey = Pointer<Utf8>? Function(Pointer<Utf8> txId);
|
||||||
|
|
||||||
typedef TransactionsCount = int Function();
|
typedef TransactionsCount = int Function();
|
||||||
|
@ -139,3 +142,7 @@ typedef RefreshCoins = void Function(int);
|
||||||
typedef CoinsCount = int Function();
|
typedef CoinsCount = int Function();
|
||||||
|
|
||||||
typedef GetCoin = Pointer<CoinsInfoRow> Function(int);
|
typedef GetCoin = Pointer<CoinsInfoRow> Function(int);
|
||||||
|
|
||||||
|
typedef FreezeCoin = void Function(int);
|
||||||
|
|
||||||
|
typedef ThawCoin = void Function(int);
|
||||||
|
|
|
@ -1,28 +1,20 @@
|
||||||
|
import 'package:cw_core/unspent_transaction_output.dart';
|
||||||
import 'package:cw_monero/api/structs/coins_info_row.dart';
|
import 'package:cw_monero/api/structs/coins_info_row.dart';
|
||||||
|
|
||||||
class MoneroUnspent {
|
class MoneroUnspent extends Unspent {
|
||||||
MoneroUnspent(this.address, this.hash, this.keyImage, this.value, this.isFrozen, this.isUnlocked)
|
MoneroUnspent(
|
||||||
: isSending = true,
|
String address, String hash, String keyImage, int value, bool isFrozen, this.isUnlocked)
|
||||||
note = '';
|
: super(address, hash, value, 0, keyImage) {
|
||||||
|
this.isFrozen = isFrozen;
|
||||||
|
}
|
||||||
|
|
||||||
MoneroUnspent.fromCoinsInfoRow(CoinsInfoRow coinsInfoRow)
|
factory MoneroUnspent.fromCoinsInfoRow(CoinsInfoRow coinsInfoRow) => MoneroUnspent(
|
||||||
: address = coinsInfoRow.getAddress(),
|
coinsInfoRow.getAddress(),
|
||||||
hash = coinsInfoRow.getHash(),
|
coinsInfoRow.getHash(),
|
||||||
keyImage = coinsInfoRow.getKeyImage(),
|
coinsInfoRow.getKeyImage(),
|
||||||
value = coinsInfoRow.amount,
|
coinsInfoRow.amount,
|
||||||
isFrozen = coinsInfoRow.frozen == 1,
|
coinsInfoRow.frozen == 1,
|
||||||
isUnlocked = coinsInfoRow.unlocked == 1,
|
coinsInfoRow.unlocked == 1);
|
||||||
isSending = true,
|
|
||||||
note = '';
|
|
||||||
|
|
||||||
final String address;
|
|
||||||
final String hash;
|
|
||||||
final String keyImage;
|
|
||||||
final int value;
|
|
||||||
|
|
||||||
final bool isUnlocked;
|
final bool isUnlocked;
|
||||||
|
|
||||||
bool isFrozen;
|
|
||||||
bool isSending;
|
|
||||||
String note;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:cw_core/account.dart';
|
import 'package:cw_core/account.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/monero_amount_format.dart';
|
import 'package:cw_core/monero_amount_format.dart';
|
||||||
|
@ -22,14 +23,14 @@ import 'package:cw_monero/api/transaction_history.dart' as transaction_history;
|
||||||
import 'package:cw_monero/api/wallet.dart' as monero_wallet;
|
import 'package:cw_monero/api/wallet.dart' as monero_wallet;
|
||||||
import 'package:cw_monero/exceptions/monero_transaction_creation_exception.dart';
|
import 'package:cw_monero/exceptions/monero_transaction_creation_exception.dart';
|
||||||
import 'package:cw_monero/exceptions/monero_transaction_no_inputs_exception.dart';
|
import 'package:cw_monero/exceptions/monero_transaction_no_inputs_exception.dart';
|
||||||
import 'package:cw_monero/pending_monero_transaction.dart';
|
|
||||||
import 'package:cw_monero/monero_transaction_creation_credentials.dart';
|
import 'package:cw_monero/monero_transaction_creation_credentials.dart';
|
||||||
import 'package:cw_monero/monero_transaction_history.dart';
|
import 'package:cw_monero/monero_transaction_history.dart';
|
||||||
import 'package:cw_monero/monero_transaction_info.dart';
|
import 'package:cw_monero/monero_transaction_info.dart';
|
||||||
import 'package:cw_monero/monero_unspent.dart';
|
import 'package:cw_monero/monero_unspent.dart';
|
||||||
import 'package:cw_monero/monero_wallet_addresses.dart';
|
import 'package:cw_monero/monero_wallet_addresses.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:cw_monero/pending_monero_transaction.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
|
|
||||||
part 'monero_wallet.g.dart';
|
part 'monero_wallet.g.dart';
|
||||||
|
|
||||||
|
@ -204,7 +205,7 @@ abstract class MoneroWalletBase
|
||||||
for (final utx in unspentCoins) {
|
for (final utx in unspentCoins) {
|
||||||
if (utx.isSending) {
|
if (utx.isSending) {
|
||||||
allInputsAmount += utx.value;
|
allInputsAmount += utx.value;
|
||||||
inputs.add(utx.keyImage);
|
inputs.add(utx.keyImage!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final spendAllCoins = inputs.length == unspentCoins.length;
|
final spendAllCoins = inputs.length == unspentCoins.length;
|
||||||
|
@ -395,7 +396,9 @@ abstract class MoneroWalletBase
|
||||||
for (var i = 0; i < coinCount; i++) {
|
for (var i = 0; i < coinCount; i++) {
|
||||||
final coin = getCoin(i);
|
final coin = getCoin(i);
|
||||||
if (coin.spent == 0) {
|
if (coin.spent == 0) {
|
||||||
unspentCoins.add(MoneroUnspent.fromCoinsInfoRow(coin));
|
final unspent = MoneroUnspent.fromCoinsInfoRow(coin);
|
||||||
|
unspent.isChange = transaction_history.getTransaction(unspent.hash).direction == 1;
|
||||||
|
unspentCoins.add(unspent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,8 +409,10 @@ abstract class MoneroWalletBase
|
||||||
|
|
||||||
if (unspentCoins.isNotEmpty) {
|
if (unspentCoins.isNotEmpty) {
|
||||||
unspentCoins.forEach((coin) {
|
unspentCoins.forEach((coin) {
|
||||||
final coinInfoList = unspentCoinsInfo.values
|
final coinInfoList = unspentCoinsInfo.values.where((element) =>
|
||||||
.where((element) => element.walletId.contains(id) && element.hash.contains(coin.hash));
|
element.walletId.contains(id) &&
|
||||||
|
element.accountIndex == walletAddresses.account!.id &&
|
||||||
|
element.keyImage!.contains(coin.keyImage!));
|
||||||
|
|
||||||
if (coinInfoList.isNotEmpty) {
|
if (coinInfoList.isNotEmpty) {
|
||||||
final coinInfo = coinInfoList.first;
|
final coinInfo = coinInfoList.first;
|
||||||
|
@ -435,7 +440,9 @@ abstract class MoneroWalletBase
|
||||||
address: coin.address,
|
address: coin.address,
|
||||||
value: coin.value,
|
value: coin.value,
|
||||||
vout: 0,
|
vout: 0,
|
||||||
keyImage: coin.keyImage);
|
keyImage: coin.keyImage,
|
||||||
|
isChange: coin.isChange,
|
||||||
|
accountIndex: walletAddresses.account!.id);
|
||||||
|
|
||||||
await unspentCoinsInfo.add(newInfo);
|
await unspentCoinsInfo.add(newInfo);
|
||||||
}
|
}
|
||||||
|
@ -443,12 +450,13 @@ abstract class MoneroWalletBase
|
||||||
Future<void> _refreshUnspentCoinsInfo() async {
|
Future<void> _refreshUnspentCoinsInfo() async {
|
||||||
try {
|
try {
|
||||||
final List<dynamic> keys = <dynamic>[];
|
final List<dynamic> keys = <dynamic>[];
|
||||||
final currentWalletUnspentCoins =
|
final currentWalletUnspentCoins = unspentCoinsInfo.values.where((element) =>
|
||||||
unspentCoinsInfo.values.where((element) => element.walletId.contains(id));
|
element.walletId.contains(id) && element.accountIndex == walletAddresses.account!.id);
|
||||||
|
|
||||||
if (currentWalletUnspentCoins.isNotEmpty) {
|
if (currentWalletUnspentCoins.isNotEmpty) {
|
||||||
currentWalletUnspentCoins.forEach((element) {
|
currentWalletUnspentCoins.forEach((element) {
|
||||||
final existUnspentCoins = unspentCoins.where((coin) => element.hash.contains(coin.hash));
|
final existUnspentCoins =
|
||||||
|
unspentCoins.where((coin) => element.keyImage!.contains(coin.keyImage!));
|
||||||
|
|
||||||
if (existUnspentCoins.isEmpty) {
|
if (existUnspentCoins.isEmpty) {
|
||||||
keys.add(element.key);
|
keys.add(element.key);
|
||||||
|
@ -566,7 +574,8 @@ abstract class MoneroWalletBase
|
||||||
int _getFrozenBalance() {
|
int _getFrozenBalance() {
|
||||||
var frozenBalance = 0;
|
var frozenBalance = 0;
|
||||||
|
|
||||||
for (var coin in unspentCoinsInfo.values) {
|
for (var coin in unspentCoinsInfo.values.where((element) =>
|
||||||
|
element.walletId == id && element.accountIndex == walletAddresses.account!.id)) {
|
||||||
if (coin.isFrozen) frozenBalance += coin.value;
|
if (coin.isFrozen) frozenBalance += coin.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -321,10 +321,7 @@ class CWMonero extends Monero {
|
||||||
@override
|
@override
|
||||||
List<Unspent> getUnspents(Object wallet) {
|
List<Unspent> getUnspents(Object wallet) {
|
||||||
final moneroWallet = wallet as MoneroWallet;
|
final moneroWallet = wallet as MoneroWallet;
|
||||||
return moneroWallet.unspentCoins
|
return moneroWallet.unspentCoins;
|
||||||
.map((MoneroUnspent moneroUnspent) => Unspent(moneroUnspent.address, moneroUnspent.hash,
|
|
||||||
moneroUnspent.value, 0, moneroUnspent.keyImage))
|
|
||||||
.toList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart';
|
import 'package:cake_wallet/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart';
|
||||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
|
||||||
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart';
|
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
|
||||||
|
|
||||||
class UnspentCoinsListPage extends BasePage {
|
class UnspentCoinsListPage extends BasePage {
|
||||||
UnspentCoinsListPage({required this.unspentCoinsListViewModel});
|
UnspentCoinsListPage({required this.unspentCoinsListViewModel});
|
||||||
|
@ -17,31 +15,10 @@ class UnspentCoinsListPage extends BasePage {
|
||||||
@override
|
@override
|
||||||
String get title => S.current.unspent_coins_title;
|
String get title => S.current.unspent_coins_title;
|
||||||
|
|
||||||
//@override
|
|
||||||
//Widget trailing(BuildContext context) {
|
|
||||||
// final questionImage = Image.asset('assets/images/question_mark.png',
|
|
||||||
// color: Theme.of(context).extension<CakeTextTheme>()!.titleColor);
|
|
||||||
|
|
||||||
// return SizedBox(
|
|
||||||
// height: 20.0,
|
|
||||||
// width: 20.0,
|
|
||||||
// child: ButtonTheme(
|
|
||||||
// minWidth: double.minPositive,
|
|
||||||
// child: FlatButton(
|
|
||||||
// highlightColor: Colors.transparent,
|
|
||||||
// splashColor: Colors.transparent,
|
|
||||||
// padding: EdgeInsets.all(0),
|
|
||||||
// onPressed: () => showUnspentCoinsAlert(context),
|
|
||||||
// child: questionImage),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
//}
|
|
||||||
|
|
||||||
final UnspentCoinsListViewModel unspentCoinsListViewModel;
|
final UnspentCoinsListViewModel unspentCoinsListViewModel;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget body(BuildContext context) =>
|
Widget body(BuildContext context) => UnspentCoinsListForm(unspentCoinsListViewModel);
|
||||||
UnspentCoinsListForm(unspentCoinsListViewModel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class UnspentCoinsListForm extends StatefulWidget {
|
class UnspentCoinsListForm extends StatefulWidget {
|
||||||
|
@ -50,8 +27,7 @@ class UnspentCoinsListForm extends StatefulWidget {
|
||||||
final UnspentCoinsListViewModel unspentCoinsListViewModel;
|
final UnspentCoinsListViewModel unspentCoinsListViewModel;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
UnspentCoinsListFormState createState() =>
|
UnspentCoinsListFormState createState() => UnspentCoinsListFormState(unspentCoinsListViewModel);
|
||||||
UnspentCoinsListFormState(unspentCoinsListViewModel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class UnspentCoinsListFormState extends State<UnspentCoinsListForm> {
|
class UnspentCoinsListFormState extends State<UnspentCoinsListForm> {
|
||||||
|
@ -59,16 +35,6 @@ class UnspentCoinsListFormState extends State<UnspentCoinsListForm> {
|
||||||
|
|
||||||
final UnspentCoinsListViewModel unspentCoinsListViewModel;
|
final UnspentCoinsListViewModel unspentCoinsListViewModel;
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback(afterLayout);
|
|
||||||
}
|
|
||||||
|
|
||||||
void afterLayout(dynamic _) {
|
|
||||||
//showUnspentCoinsAlert(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
|
@ -76,8 +42,7 @@ class UnspentCoinsListFormState extends State<UnspentCoinsListForm> {
|
||||||
child: Observer(
|
child: Observer(
|
||||||
builder: (_) => ListView.separated(
|
builder: (_) => ListView.separated(
|
||||||
itemCount: unspentCoinsListViewModel.items.length,
|
itemCount: unspentCoinsListViewModel.items.length,
|
||||||
separatorBuilder: (_, __) =>
|
separatorBuilder: (_, __) => SizedBox(height: 15),
|
||||||
SizedBox(height: 15),
|
|
||||||
itemBuilder: (_, int index) {
|
itemBuilder: (_, int index) {
|
||||||
return Observer(builder: (_) {
|
return Observer(builder: (_) {
|
||||||
final item = unspentCoinsListViewModel.items[index];
|
final item = unspentCoinsListViewModel.items[index];
|
||||||
|
@ -86,9 +51,7 @@ class UnspentCoinsListFormState extends State<UnspentCoinsListForm> {
|
||||||
: item.address;
|
: item.address;
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () =>
|
onTap: () => Navigator.of(context).pushNamed(Routes.unspentCoinsDetails,
|
||||||
Navigator.of(context)
|
|
||||||
.pushNamed(Routes.unspentCoinsDetails,
|
|
||||||
arguments: [item, unspentCoinsListViewModel]),
|
arguments: [item, unspentCoinsListViewModel]),
|
||||||
child: UnspentCoinsListItem(
|
child: UnspentCoinsListItem(
|
||||||
note: item.note,
|
note: item.note,
|
||||||
|
@ -96,28 +59,14 @@ class UnspentCoinsListFormState extends State<UnspentCoinsListForm> {
|
||||||
address: address,
|
address: address,
|
||||||
isSending: item.isSending,
|
isSending: item.isSending,
|
||||||
isFrozen: item.isFrozen,
|
isFrozen: item.isFrozen,
|
||||||
|
isChange: item.isChange,
|
||||||
onCheckBoxTap: item.isFrozen
|
onCheckBoxTap: item.isFrozen
|
||||||
? null
|
? null
|
||||||
: () async {
|
: () async {
|
||||||
item.isSending = !item.isSending;
|
item.isSending = !item.isSending;
|
||||||
await unspentCoinsListViewModel
|
await unspentCoinsListViewModel.saveUnspentCoinInfo(item);
|
||||||
.saveUnspentCoinInfo(item);}));
|
}));
|
||||||
});
|
});
|
||||||
}
|
})));
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void showUnspentCoinsAlert(BuildContext context) {
|
|
||||||
showPopUp<void>(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertWithOneAction(
|
|
||||||
alertTitle: '',
|
|
||||||
alertContent: 'Information about unspent coins',
|
|
||||||
buttonText: S.of(context).ok,
|
|
||||||
buttonAction: () => Navigator.of(context).pop());
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,8 +1,8 @@
|
||||||
import 'package:auto_size_text/auto_size_text.dart';
|
import 'package:auto_size_text/auto_size_text.dart';
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
|
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
|
||||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
|
||||||
|
|
||||||
class UnspentCoinsListItem extends StatelessWidget {
|
class UnspentCoinsListItem extends StatelessWidget {
|
||||||
UnspentCoinsListItem({
|
UnspentCoinsListItem({
|
||||||
|
@ -11,6 +11,7 @@ class UnspentCoinsListItem extends StatelessWidget {
|
||||||
required this.address,
|
required this.address,
|
||||||
required this.isSending,
|
required this.isSending,
|
||||||
required this.isFrozen,
|
required this.isFrozen,
|
||||||
|
required this.isChange,
|
||||||
this.onCheckBoxTap,
|
this.onCheckBoxTap,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -19,6 +20,7 @@ class UnspentCoinsListItem extends StatelessWidget {
|
||||||
final String address;
|
final String address;
|
||||||
final bool isSending;
|
final bool isSending;
|
||||||
final bool isFrozen;
|
final bool isFrozen;
|
||||||
|
final bool isChange;
|
||||||
final Function()? onCheckBoxTap;
|
final Function()? onCheckBoxTap;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -27,9 +29,8 @@ class UnspentCoinsListItem extends StatelessWidget {
|
||||||
final selectedItemColor = Theme.of(context).primaryColor;
|
final selectedItemColor = Theme.of(context).primaryColor;
|
||||||
final itemColor = isSending ? selectedItemColor : unselectedItemColor;
|
final itemColor = isSending ? selectedItemColor : unselectedItemColor;
|
||||||
|
|
||||||
final amountColor = isSending
|
final amountColor =
|
||||||
? Colors.white
|
isSending ? Colors.white : Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor;
|
||||||
: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor;
|
|
||||||
final addressColor = isSending
|
final addressColor = isSending
|
||||||
? Colors.white.withOpacity(0.5)
|
? Colors.white.withOpacity(0.5)
|
||||||
: Theme.of(context).extension<CakeTextTheme>()!.buttonSecondaryTextColor;
|
: Theme.of(context).extension<CakeTextTheme>()!.buttonSecondaryTextColor;
|
||||||
|
@ -47,7 +48,8 @@ class UnspentCoinsListItem extends StatelessWidget {
|
||||||
child: StandardCheckbox(
|
child: StandardCheckbox(
|
||||||
iconColor: amountColor,
|
iconColor: amountColor,
|
||||||
borderColor: addressColor,
|
borderColor: addressColor,
|
||||||
value: isSending, onChanged: (value) => onCheckBoxTap?.call())),
|
value: isSending,
|
||||||
|
onChanged: (value) => onCheckBoxTap?.call())),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
@ -57,9 +59,7 @@ class UnspentCoinsListItem extends StatelessWidget {
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Column(
|
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
if (note.isNotEmpty)
|
if (note.isNotEmpty)
|
||||||
AutoSizeText(
|
AutoSizeText(
|
||||||
note,
|
note,
|
||||||
|
@ -69,8 +69,8 @@ class UnspentCoinsListItem extends StatelessWidget {
|
||||||
),
|
),
|
||||||
AutoSizeText(
|
AutoSizeText(
|
||||||
amount,
|
amount,
|
||||||
style:
|
style: TextStyle(
|
||||||
TextStyle(color: amountColor, fontSize: 15, fontWeight: FontWeight.w600),
|
color: amountColor, fontSize: 15, fontWeight: FontWeight.w600),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
|
@ -84,23 +84,41 @@ class UnspentCoinsListItem extends StatelessWidget {
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Text(
|
child: Text(
|
||||||
S.of(context).frozen,
|
S.of(context).frozen,
|
||||||
style:
|
style: TextStyle(
|
||||||
TextStyle(color: amountColor, fontSize: 7, fontWeight: FontWeight.w600),
|
color: amountColor, fontSize: 7, fontWeight: FontWeight.w600),
|
||||||
))
|
)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
AutoSizeText(
|
AutoSizeText(
|
||||||
'${address.substring(0, 5)}...${address.substring(address.length-5)}', // ToDo: Maybe use address label
|
'${address.substring(0, 5)}...${address.substring(address.length - 5)}', // ToDo: Maybe use address label
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: addressColor,
|
color: addressColor,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
),
|
),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
),
|
),
|
||||||
|
if (isChange)
|
||||||
|
Container(
|
||||||
|
height: 17,
|
||||||
|
padding: EdgeInsets.only(left: 6, right: 6),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(8.5)),
|
||||||
|
color: Colors.white),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Text(
|
||||||
|
S.of(context).unspent_change,
|
||||||
|
style: TextStyle(
|
||||||
|
color: itemColor,
|
||||||
|
fontSize: 7,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -175,10 +175,8 @@ abstract class BalanceViewModelBase with Store {
|
||||||
return '---';
|
return '---';
|
||||||
}
|
}
|
||||||
|
|
||||||
return _getFiatBalance(
|
return _getFiatBalance(price: price, cryptoAmount: getFormattedFrozenBalance(walletBalance)) +
|
||||||
price: price,
|
' ${fiatCurrency.toString()}';
|
||||||
cryptoAmount: getFormattedFrozenBalance(walletBalance)) + ' ' + fiatCurrency.toString();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
|
@ -201,10 +199,8 @@ abstract class BalanceViewModelBase with Store {
|
||||||
return '---';
|
return '---';
|
||||||
}
|
}
|
||||||
|
|
||||||
return _getFiatBalance(
|
return _getFiatBalance(price: price, cryptoAmount: walletBalance.formattedAvailableBalance) +
|
||||||
price: price,
|
' ${fiatCurrency.toString()}';
|
||||||
cryptoAmount: walletBalance.formattedAvailableBalance) + ' ' + fiatCurrency.toString();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
|
@ -216,10 +212,8 @@ abstract class BalanceViewModelBase with Store {
|
||||||
return '---';
|
return '---';
|
||||||
}
|
}
|
||||||
|
|
||||||
return _getFiatBalance(
|
return _getFiatBalance(price: price, cryptoAmount: walletBalance.formattedAdditionalBalance) +
|
||||||
price: price,
|
' ${fiatCurrency.toString()}';
|
||||||
cryptoAmount: walletBalance.formattedAdditionalBalance) + ' ' + fiatCurrency.toString();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
|
|
|
@ -12,6 +12,7 @@ abstract class UnspentCoinsItemBase with Store {
|
||||||
required this.isFrozen,
|
required this.isFrozen,
|
||||||
required this.note,
|
required this.note,
|
||||||
required this.isSending,
|
required this.isSending,
|
||||||
|
required this.isChange,
|
||||||
required this.amountRaw,
|
required this.amountRaw,
|
||||||
required this.vout,
|
required this.vout,
|
||||||
required this.keyImage
|
required this.keyImage
|
||||||
|
@ -35,6 +36,9 @@ abstract class UnspentCoinsItemBase with Store {
|
||||||
@observable
|
@observable
|
||||||
bool isSending;
|
bool isSending;
|
||||||
|
|
||||||
|
@observable
|
||||||
|
bool isChange;
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
int amountRaw;
|
int amountRaw;
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
import 'package:collection/collection.dart';
|
|
||||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||||
import 'package:cw_core/unspent_transaction_output.dart';
|
|
||||||
import 'package:cake_wallet/monero/monero.dart';
|
import 'package:cake_wallet/monero/monero.dart';
|
||||||
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart';
|
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart';
|
||||||
import 'package:cw_core/unspent_coins_info.dart';
|
import 'package:cw_core/unspent_coins_info.dart';
|
||||||
import 'package:cw_core/wallet_addresses.dart';
|
import 'package:cw_core/unspent_transaction_output.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
@ -26,66 +24,49 @@ abstract class UnspentCoinsListViewModelBase with Store {
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
ObservableList<UnspentCoinsItem> get items => ObservableList.of(_getUnspents().map((elem) {
|
ObservableList<UnspentCoinsItem> get items => ObservableList.of(_getUnspents().map((elem) {
|
||||||
final amount = formatAmountToString(elem.value) + ' ${wallet.currency.title}';
|
|
||||||
|
|
||||||
final info =
|
final info =
|
||||||
getUnspentCoinInfo(elem.hash, elem.address, elem.value, elem.vout, elem.keyImage);
|
getUnspentCoinInfo(elem.hash, elem.address, elem.value, elem.vout, elem.keyImage);
|
||||||
|
|
||||||
return UnspentCoinsItem(
|
return UnspentCoinsItem(
|
||||||
address: elem.address,
|
address: elem.address,
|
||||||
amount: amount,
|
amount: '${formatAmountToString(elem.value)} ${wallet.currency.title}',
|
||||||
hash: elem.hash,
|
hash: elem.hash,
|
||||||
isFrozen: info?.isFrozen ?? false,
|
isFrozen: info.isFrozen,
|
||||||
note: info?.note ?? '',
|
note: info.note,
|
||||||
isSending: info?.isSending ?? true,
|
isSending: info.isSending,
|
||||||
amountRaw: elem.value,
|
amountRaw: elem.value,
|
||||||
vout: elem.vout,
|
vout: elem.vout,
|
||||||
keyImage: elem.keyImage);
|
keyImage: elem.keyImage,
|
||||||
|
isChange: elem.isChange,
|
||||||
|
);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Future<void> saveUnspentCoinInfo(UnspentCoinsItem item) async {
|
Future<void> saveUnspentCoinInfo(UnspentCoinsItem item) async {
|
||||||
try {
|
try {
|
||||||
final info =
|
final info =
|
||||||
getUnspentCoinInfo(item.hash, item.address, item.amountRaw, item.vout, item.keyImage);
|
getUnspentCoinInfo(item.hash, item.address, item.amountRaw, item.vout, item.keyImage);
|
||||||
if (info == null) {
|
|
||||||
final newInfo = UnspentCoinsInfo(
|
|
||||||
walletId: wallet.id,
|
|
||||||
hash: item.hash,
|
|
||||||
address: item.address,
|
|
||||||
value: item.amountRaw,
|
|
||||||
vout: item.vout,
|
|
||||||
isFrozen: item.isFrozen,
|
|
||||||
isSending: item.isSending,
|
|
||||||
noteRaw: item.note,
|
|
||||||
keyImage: item.keyImage);
|
|
||||||
|
|
||||||
await _unspentCoinsInfo.add(newInfo);
|
|
||||||
_updateUnspents();
|
|
||||||
wallet.updateBalance();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
info.isFrozen = item.isFrozen;
|
info.isFrozen = item.isFrozen;
|
||||||
info.isSending = item.isSending;
|
info.isSending = item.isSending;
|
||||||
info.note = item.note;
|
info.note = item.note;
|
||||||
|
|
||||||
await info.save();
|
await info.save();
|
||||||
_updateUnspents();
|
await _updateUnspents();
|
||||||
wallet.updateBalance();
|
await wallet.updateBalance();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e.toString());
|
print(e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UnspentCoinsInfo? getUnspentCoinInfo(
|
UnspentCoinsInfo getUnspentCoinInfo(
|
||||||
String hash, String address, int value, int vout, String? keyImage) {
|
String hash, String address, int value, int vout, String? keyImage) =>
|
||||||
return _unspentCoinsInfo.values.firstWhereOrNull((element) =>
|
_unspentCoinsInfo.values.firstWhere((element) =>
|
||||||
element.walletId == wallet.id &&
|
element.walletId == wallet.id &&
|
||||||
element.hash == hash &&
|
element.hash == hash &&
|
||||||
element.address == address &&
|
element.address == address &&
|
||||||
element.value == value &&
|
element.value == value &&
|
||||||
element.vout == vout &&
|
element.vout == vout &&
|
||||||
element.keyImage == keyImage);
|
element.keyImage == keyImage);
|
||||||
}
|
|
||||||
|
|
||||||
String formatAmountToString(int fullBalance) {
|
String formatAmountToString(int fullBalance) {
|
||||||
if (wallet.type == WalletType.monero)
|
if (wallet.type == WalletType.monero)
|
||||||
|
@ -95,7 +76,7 @@ abstract class UnspentCoinsListViewModelBase with Store {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateUnspents() {
|
Future<void> _updateUnspents() async {
|
||||||
if (wallet.type == WalletType.monero) return monero!.updateUnspents(wallet);
|
if (wallet.type == WalletType.monero) return monero!.updateUnspents(wallet);
|
||||||
if ([WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(wallet.type))
|
if ([WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(wallet.type))
|
||||||
return bitcoin!.updateUnspents(wallet);
|
return bitcoin!.updateUnspents(wallet);
|
||||||
|
|
|
@ -726,5 +726,6 @@
|
||||||
"domain_looks_up": "ﻝﺎﺠﻤﻟﺍ ﺚﺤﺑ ﺕﺎﻴﻠﻤﻋ",
|
"domain_looks_up": "ﻝﺎﺠﻤﻟﺍ ﺚﺤﺑ ﺕﺎﻴﻠﻤﻋ",
|
||||||
"require_for_exchanges_to_external_wallets": "ﺔﻴﺟﺭﺎﺧ ﻆﻓﺎﺤﻣ ﻰﻟﺇ ﺕﻻﺩﺎﺒﺘﻟﺍ ﺐﻠﻄﺘﺗ",
|
"require_for_exchanges_to_external_wallets": "ﺔﻴﺟﺭﺎﺧ ﻆﻓﺎﺤﻣ ﻰﻟﺇ ﺕﻻﺩﺎﺒﺘﻟﺍ ﺐﻠﻄﺘﺗ",
|
||||||
"camera_permission_is_required": ".ﺍﺮﻴﻣﺎﻜﻟﺍ ﻥﺫﺇ ﺏﻮﻠﻄﻣ",
|
"camera_permission_is_required": ".ﺍﺮﻴﻣﺎﻜﻟﺍ ﻥﺫﺇ ﺏﻮﻠﻄﻣ",
|
||||||
"switchToETHWallet": "ﻯﺮﺧﺃ ﺓﺮﻣ ﺔﻟﻭﺎﺤﻤﻟﺍﻭ Ethereum ﺔﻈﻔﺤﻣ ﻰﻟﺇ ﻞﻳﺪﺒﺘﻟﺍ ﻰﺟﺮﻳ"
|
"switchToETHWallet": "ﻯﺮﺧﺃ ﺓﺮﻣ ﺔﻟﻭﺎﺤﻤﻟﺍﻭ Ethereum ﺔﻈﻔﺤﻣ ﻰﻟﺇ ﻞﻳﺪﺒﺘﻟﺍ ﻰﺟﺮﻳ",
|
||||||
|
"unspent_change": "يتغير"
|
||||||
}
|
}
|
||||||
|
|
|
@ -722,5 +722,6 @@
|
||||||
"domain_looks_up": "Търсене на домейни",
|
"domain_looks_up": "Търсене на домейни",
|
||||||
"require_for_exchanges_to_external_wallets": "Изискване за обмен към външни портфейли",
|
"require_for_exchanges_to_external_wallets": "Изискване за обмен към външни портфейли",
|
||||||
"camera_permission_is_required": "Изисква се разрешение за камерата.\nМоля, активирайте го от настройките на приложението.",
|
"camera_permission_is_required": "Изисква се разрешение за камерата.\nМоля, активирайте го от настройките на приложението.",
|
||||||
"switchToETHWallet": "Моля, преминете към портфейл Ethereum и опитайте отново"
|
"switchToETHWallet": "Моля, преминете към портфейл Ethereum и опитайте отново",
|
||||||
|
"unspent_change": "Промяна"
|
||||||
}
|
}
|
||||||
|
|
|
@ -722,5 +722,6 @@
|
||||||
"domain_looks_up": "Vyhledávání domén",
|
"domain_looks_up": "Vyhledávání domén",
|
||||||
"require_for_exchanges_to_external_wallets": "Vyžadovat pro výměny do externích peněženek",
|
"require_for_exchanges_to_external_wallets": "Vyžadovat pro výměny do externích peněženek",
|
||||||
"camera_permission_is_required": "Vyžaduje se povolení fotoaparátu.\nPovolte jej v nastavení aplikace.",
|
"camera_permission_is_required": "Vyžaduje se povolení fotoaparátu.\nPovolte jej v nastavení aplikace.",
|
||||||
"switchToETHWallet": "Přejděte na peněženku Ethereum a zkuste to znovu"
|
"switchToETHWallet": "Přejděte na peněženku Ethereum a zkuste to znovu",
|
||||||
|
"unspent_change": "Změna"
|
||||||
}
|
}
|
||||||
|
|
|
@ -730,5 +730,6 @@
|
||||||
"domain_looks_up": "Domain-Suchen",
|
"domain_looks_up": "Domain-Suchen",
|
||||||
"require_for_exchanges_to_external_wallets": "Erforderlich für den Umtausch in externe Wallets",
|
"require_for_exchanges_to_external_wallets": "Erforderlich für den Umtausch in externe Wallets",
|
||||||
"camera_permission_is_required": "Eine Kameraerlaubnis ist erforderlich.\nBitte aktivieren Sie es in den App-Einstellungen.",
|
"camera_permission_is_required": "Eine Kameraerlaubnis ist erforderlich.\nBitte aktivieren Sie es in den App-Einstellungen.",
|
||||||
"switchToETHWallet": "Bitte wechseln Sie zu einem Ethereum-Wallet und versuchen Sie es erneut"
|
"switchToETHWallet": "Bitte wechseln Sie zu einem Ethereum-Wallet und versuchen Sie es erneut",
|
||||||
|
"unspent_change": "Wechselgeld"
|
||||||
}
|
}
|
||||||
|
|
|
@ -731,5 +731,6 @@
|
||||||
"domain_looks_up": "Domain lookups",
|
"domain_looks_up": "Domain lookups",
|
||||||
"require_for_exchanges_to_external_wallets": "Require for exchanges to external wallets",
|
"require_for_exchanges_to_external_wallets": "Require for exchanges to external wallets",
|
||||||
"camera_permission_is_required": "Camera permission is required. \nPlease enable it from app settings.",
|
"camera_permission_is_required": "Camera permission is required. \nPlease enable it from app settings.",
|
||||||
"switchToETHWallet": "Please switch to an Ethereum wallet and try again"
|
"switchToETHWallet": "Please switch to an Ethereum wallet and try again",
|
||||||
|
"unspent_change": "Change"
|
||||||
}
|
}
|
||||||
|
|
|
@ -730,5 +730,6 @@
|
||||||
"domain_looks_up": "Búsquedas de dominio",
|
"domain_looks_up": "Búsquedas de dominio",
|
||||||
"require_for_exchanges_to_external_wallets": "Requerido para intercambios a billeteras externas",
|
"require_for_exchanges_to_external_wallets": "Requerido para intercambios a billeteras externas",
|
||||||
"camera_permission_is_required": "Se requiere permiso de la cámara.\nHabilítelo desde la configuración de la aplicación.",
|
"camera_permission_is_required": "Se requiere permiso de la cámara.\nHabilítelo desde la configuración de la aplicación.",
|
||||||
"switchToETHWallet": "Cambie a una billetera Ethereum e inténtelo nuevamente."
|
"switchToETHWallet": "Cambie a una billetera Ethereum e inténtelo nuevamente.",
|
||||||
|
"unspent_change": "Cambiar"
|
||||||
}
|
}
|
||||||
|
|
|
@ -730,5 +730,6 @@
|
||||||
"domain_looks_up": "Recherches de domaine",
|
"domain_looks_up": "Recherches de domaine",
|
||||||
"require_for_exchanges_to_external_wallets": "Exiger pour les échanges vers des portefeuilles externes",
|
"require_for_exchanges_to_external_wallets": "Exiger pour les échanges vers des portefeuilles externes",
|
||||||
"camera_permission_is_required": "L'autorisation de la caméra est requise.\nVeuillez l'activer à partir des paramètres de l'application.",
|
"camera_permission_is_required": "L'autorisation de la caméra est requise.\nVeuillez l'activer à partir des paramètres de l'application.",
|
||||||
"switchToETHWallet": "Veuillez passer à un portefeuille (wallet) Ethereum et réessayer"
|
"switchToETHWallet": "Veuillez passer à un portefeuille (wallet) Ethereum et réessayer",
|
||||||
|
"unspent_change": "Changement"
|
||||||
}
|
}
|
||||||
|
|
|
@ -708,5 +708,6 @@
|
||||||
"domain_looks_up": "Binciken yanki",
|
"domain_looks_up": "Binciken yanki",
|
||||||
"require_for_exchanges_to_external_wallets": "Bukatar musanya zuwa wallet na waje",
|
"require_for_exchanges_to_external_wallets": "Bukatar musanya zuwa wallet na waje",
|
||||||
"camera_permission_is_required": "Ana buƙatar izinin kyamara.\nDa fatan za a kunna shi daga saitunan app.",
|
"camera_permission_is_required": "Ana buƙatar izinin kyamara.\nDa fatan za a kunna shi daga saitunan app.",
|
||||||
"switchToETHWallet": "Da fatan za a canza zuwa walat ɗin Ethereum kuma a sake gwadawa"
|
"switchToETHWallet": "Da fatan za a canza zuwa walat ɗin Ethereum kuma a sake gwadawa",
|
||||||
|
"unspent_change": "Canza"
|
||||||
}
|
}
|
||||||
|
|
|
@ -730,5 +730,6 @@
|
||||||
"domain_looks_up": "डोमेन लुकअप",
|
"domain_looks_up": "डोमेन लुकअप",
|
||||||
"require_for_exchanges_to_external_wallets": "बाहरी वॉलेट में एक्सचेंज की आवश्यकता है",
|
"require_for_exchanges_to_external_wallets": "बाहरी वॉलेट में एक्सचेंज की आवश्यकता है",
|
||||||
"camera_permission_is_required": "कैमरे की अनुमति आवश्यक है.\nकृपया इसे ऐप सेटिंग से सक्षम करें।",
|
"camera_permission_is_required": "कैमरे की अनुमति आवश्यक है.\nकृपया इसे ऐप सेटिंग से सक्षम करें।",
|
||||||
"switchToETHWallet": "कृपया एथेरियम वॉलेट पर स्विच करें और पुनः प्रयास करें"
|
"switchToETHWallet": "कृपया एथेरियम वॉलेट पर स्विच करें और पुनः प्रयास करें",
|
||||||
|
"unspent_change": "परिवर्तन"
|
||||||
}
|
}
|
||||||
|
|
|
@ -728,5 +728,6 @@
|
||||||
"domain_looks_up": "Pretraga domena",
|
"domain_looks_up": "Pretraga domena",
|
||||||
"require_for_exchanges_to_external_wallets": "Zahtijeva razmjene na vanjske novčanike",
|
"require_for_exchanges_to_external_wallets": "Zahtijeva razmjene na vanjske novčanike",
|
||||||
"camera_permission_is_required": "Potrebno je dopuštenje kamere.\nOmogućite ga u postavkama aplikacije.",
|
"camera_permission_is_required": "Potrebno je dopuštenje kamere.\nOmogućite ga u postavkama aplikacije.",
|
||||||
"switchToETHWallet": "Prijeđite na Ethereum novčanik i pokušajte ponovno"
|
"switchToETHWallet": "Prijeđite na Ethereum novčanik i pokušajte ponovno",
|
||||||
|
"unspent_change": "Promijeniti"
|
||||||
}
|
}
|
||||||
|
|
|
@ -718,5 +718,6 @@
|
||||||
"domain_looks_up": "Pencarian domain",
|
"domain_looks_up": "Pencarian domain",
|
||||||
"require_for_exchanges_to_external_wallets": "Memerlukan pertukaran ke dompet eksternal",
|
"require_for_exchanges_to_external_wallets": "Memerlukan pertukaran ke dompet eksternal",
|
||||||
"camera_permission_is_required": "Izin kamera diperlukan.\nSilakan aktifkan dari pengaturan aplikasi.",
|
"camera_permission_is_required": "Izin kamera diperlukan.\nSilakan aktifkan dari pengaturan aplikasi.",
|
||||||
"switchToETHWallet": "Silakan beralih ke dompet Ethereum dan coba lagi"
|
"switchToETHWallet": "Silakan beralih ke dompet Ethereum dan coba lagi",
|
||||||
|
"unspent_change": "Mengubah"
|
||||||
}
|
}
|
||||||
|
|
|
@ -730,5 +730,6 @@
|
||||||
"domain_looks_up": "Ricerche di domini",
|
"domain_looks_up": "Ricerche di domini",
|
||||||
"require_for_exchanges_to_external_wallets": "Richiede scambi con portafogli esterni",
|
"require_for_exchanges_to_external_wallets": "Richiede scambi con portafogli esterni",
|
||||||
"camera_permission_is_required": "È richiesta l'autorizzazione della fotocamera.\nAbilitalo dalle impostazioni dell'app.",
|
"camera_permission_is_required": "È richiesta l'autorizzazione della fotocamera.\nAbilitalo dalle impostazioni dell'app.",
|
||||||
"switchToETHWallet": "Passa a un portafoglio Ethereum e riprova"
|
"switchToETHWallet": "Passa a un portafoglio Ethereum e riprova",
|
||||||
|
"unspent_change": "Modifica"
|
||||||
}
|
}
|
||||||
|
|
|
@ -730,5 +730,6 @@
|
||||||
"domain_looks_up": "ドメイン検索",
|
"domain_looks_up": "ドメイン検索",
|
||||||
"require_for_exchanges_to_external_wallets": "外部ウォレットへの交換に必要",
|
"require_for_exchanges_to_external_wallets": "外部ウォレットへの交換に必要",
|
||||||
"camera_permission_is_required": "カメラの許可が必要です。\nアプリの設定から有効にしてください。",
|
"camera_permission_is_required": "カメラの許可が必要です。\nアプリの設定から有効にしてください。",
|
||||||
"switchToETHWallet": "イーサリアムウォレットに切り替えてもう一度お試しください"
|
"switchToETHWallet": "イーサリアムウォレットに切り替えてもう一度お試しください",
|
||||||
|
"unspent_change": "変化"
|
||||||
}
|
}
|
||||||
|
|
|
@ -728,5 +728,6 @@
|
||||||
"domain_looks_up": "도메인 조회",
|
"domain_looks_up": "도메인 조회",
|
||||||
"require_for_exchanges_to_external_wallets": "외부 지갑으로의 교환을 위해 필요",
|
"require_for_exchanges_to_external_wallets": "외부 지갑으로의 교환을 위해 필요",
|
||||||
"camera_permission_is_required": "카메라 권한이 필요합니다.\n앱 설정에서 활성화해 주세요.",
|
"camera_permission_is_required": "카메라 권한이 필요합니다.\n앱 설정에서 활성화해 주세요.",
|
||||||
"switchToETHWallet": "이더리움 지갑으로 전환한 후 다시 시도해 주세요."
|
"switchToETHWallet": "이더리움 지갑으로 전환한 후 다시 시도해 주세요.",
|
||||||
|
"unspent_change": "변화"
|
||||||
}
|
}
|
||||||
|
|
|
@ -728,5 +728,6 @@
|
||||||
"domain_looks_up": "ဒိုမိန်းရှာဖွေမှုများ",
|
"domain_looks_up": "ဒိုမိန်းရှာဖွေမှုများ",
|
||||||
"require_for_exchanges_to_external_wallets": "ပြင်ပပိုက်ဆံအိတ်များသို့ လဲလှယ်ရန် လိုအပ်သည်။",
|
"require_for_exchanges_to_external_wallets": "ပြင်ပပိုက်ဆံအိတ်များသို့ လဲလှယ်ရန် လိုအပ်သည်။",
|
||||||
"camera_permission_is_required": "ကင်မရာခွင့်ပြုချက် လိုအပ်ပါသည်။\nအက်ပ်ဆက်တင်များမှ ၎င်းကိုဖွင့်ပါ။",
|
"camera_permission_is_required": "ကင်မရာခွင့်ပြုချက် လိုအပ်ပါသည်။\nအက်ပ်ဆက်တင်များမှ ၎င်းကိုဖွင့်ပါ။",
|
||||||
"switchToETHWallet": "ကျေးဇူးပြု၍ Ethereum ပိုက်ဆံအိတ်သို့ ပြောင်းပြီး ထပ်စမ်းကြည့်ပါ။"
|
"switchToETHWallet": "ကျေးဇူးပြု၍ Ethereum ပိုက်ဆံအိတ်သို့ ပြောင်းပြီး ထပ်စမ်းကြည့်ပါ။",
|
||||||
|
"unspent_change": "ပေြာင်းလဲခြင်း"
|
||||||
}
|
}
|
||||||
|
|
|
@ -730,5 +730,6 @@
|
||||||
"domain_looks_up": "Domein opzoeken",
|
"domain_looks_up": "Domein opzoeken",
|
||||||
"require_for_exchanges_to_external_wallets": "Vereist voor uitwisselingen naar externe portemonnees",
|
"require_for_exchanges_to_external_wallets": "Vereist voor uitwisselingen naar externe portemonnees",
|
||||||
"camera_permission_is_required": "Cameratoestemming is vereist.\nSchakel dit in via de app-instellingen.",
|
"camera_permission_is_required": "Cameratoestemming is vereist.\nSchakel dit in via de app-instellingen.",
|
||||||
"switchToETHWallet": "Schakel over naar een Ethereum-portemonnee en probeer het opnieuw"
|
"switchToETHWallet": "Schakel over naar een Ethereum-portemonnee en probeer het opnieuw",
|
||||||
|
"unspent_change": "Wijziging"
|
||||||
}
|
}
|
||||||
|
|
|
@ -730,5 +730,6 @@
|
||||||
"domain_looks_up": "Wyszukiwanie domen",
|
"domain_looks_up": "Wyszukiwanie domen",
|
||||||
"require_for_exchanges_to_external_wallets": "Wymagaj wymiany na portfele zewnętrzne",
|
"require_for_exchanges_to_external_wallets": "Wymagaj wymiany na portfele zewnętrzne",
|
||||||
"camera_permission_is_required": "Wymagane jest pozwolenie na korzystanie z aparatu.\nWłącz tę funkcję w ustawieniach aplikacji.",
|
"camera_permission_is_required": "Wymagane jest pozwolenie na korzystanie z aparatu.\nWłącz tę funkcję w ustawieniach aplikacji.",
|
||||||
"switchToETHWallet": "Przejdź na portfel Ethereum i spróbuj ponownie"
|
"switchToETHWallet": "Przejdź na portfel Ethereum i spróbuj ponownie",
|
||||||
|
"unspent_change": "Zmiana"
|
||||||
}
|
}
|
||||||
|
|
|
@ -729,5 +729,6 @@
|
||||||
"domain_looks_up": "Pesquisas de domínio",
|
"domain_looks_up": "Pesquisas de domínio",
|
||||||
"require_for_exchanges_to_external_wallets": "Exigir trocas para carteiras externas",
|
"require_for_exchanges_to_external_wallets": "Exigir trocas para carteiras externas",
|
||||||
"camera_permission_is_required": "É necessária permissão da câmera.\nAtive-o nas configurações do aplicativo.",
|
"camera_permission_is_required": "É necessária permissão da câmera.\nAtive-o nas configurações do aplicativo.",
|
||||||
"switchToETHWallet": "Mude para uma carteira Ethereum e tente novamente"
|
"switchToETHWallet": "Mude para uma carteira Ethereum e tente novamente",
|
||||||
|
"unspent_change": "Mudar"
|
||||||
}
|
}
|
||||||
|
|
|
@ -730,5 +730,6 @@
|
||||||
"domain_looks_up": "Поиск доменов",
|
"domain_looks_up": "Поиск доменов",
|
||||||
"require_for_exchanges_to_external_wallets": "Требовать обмена на внешние кошельки",
|
"require_for_exchanges_to_external_wallets": "Требовать обмена на внешние кошельки",
|
||||||
"camera_permission_is_required": "Требуется разрешение камеры.\nПожалуйста, включите его в настройках приложения.",
|
"camera_permission_is_required": "Требуется разрешение камеры.\nПожалуйста, включите его в настройках приложения.",
|
||||||
"switchToETHWallet": "Пожалуйста, переключитесь на кошелек Ethereum и повторите попытку."
|
"switchToETHWallet": "Пожалуйста, переключитесь на кошелек Ethereum и повторите попытку.",
|
||||||
|
"unspent_change": "Изменять"
|
||||||
}
|
}
|
||||||
|
|
|
@ -728,5 +728,6 @@
|
||||||
"domain_looks_up": "การค้นหาโดเมน",
|
"domain_looks_up": "การค้นหาโดเมน",
|
||||||
"require_for_exchanges_to_external_wallets": "จำเป็นต้องแลกเปลี่ยนกับกระเป๋าเงินภายนอก",
|
"require_for_exchanges_to_external_wallets": "จำเป็นต้องแลกเปลี่ยนกับกระเป๋าเงินภายนอก",
|
||||||
"camera_permission_is_required": "ต้องได้รับอนุญาตจากกล้อง\nโปรดเปิดใช้งานจากการตั้งค่าแอป",
|
"camera_permission_is_required": "ต้องได้รับอนุญาตจากกล้อง\nโปรดเปิดใช้งานจากการตั้งค่าแอป",
|
||||||
"switchToETHWallet": "โปรดเปลี่ยนไปใช้กระเป๋าเงิน Ethereum แล้วลองอีกครั้ง"
|
"switchToETHWallet": "โปรดเปลี่ยนไปใช้กระเป๋าเงิน Ethereum แล้วลองอีกครั้ง",
|
||||||
|
"unspent_change": "เปลี่ยน"
|
||||||
}
|
}
|
||||||
|
|
|
@ -725,5 +725,6 @@
|
||||||
"domain_looks_up": "Mga paghahanap ng domain",
|
"domain_looks_up": "Mga paghahanap ng domain",
|
||||||
"require_for_exchanges_to_external_wallets": "Kinakailangan para sa mga palitan sa mga panlabas na wallet",
|
"require_for_exchanges_to_external_wallets": "Kinakailangan para sa mga palitan sa mga panlabas na wallet",
|
||||||
"camera_permission_is_required": "Kinakailangan ang pahintulot sa camera.\nMangyaring paganahin ito mula sa mga setting ng app.",
|
"camera_permission_is_required": "Kinakailangan ang pahintulot sa camera.\nMangyaring paganahin ito mula sa mga setting ng app.",
|
||||||
"switchToETHWallet": "Mangyaring lumipat sa isang Ethereum wallet at subukang muli"
|
"switchToETHWallet": "Mangyaring lumipat sa isang Ethereum wallet at subukang muli",
|
||||||
|
"unspent_change": "Baguhin"
|
||||||
}
|
}
|
||||||
|
|
|
@ -728,5 +728,6 @@
|
||||||
"domain_looks_up": "Etki alanı aramaları",
|
"domain_looks_up": "Etki alanı aramaları",
|
||||||
"require_for_exchanges_to_external_wallets": "Harici cüzdanlara geçiş yapılmasını zorunlu kılın",
|
"require_for_exchanges_to_external_wallets": "Harici cüzdanlara geçiş yapılmasını zorunlu kılın",
|
||||||
"camera_permission_is_required": "Kamera izni gereklidir.\nLütfen uygulama ayarlarından etkinleştirin.",
|
"camera_permission_is_required": "Kamera izni gereklidir.\nLütfen uygulama ayarlarından etkinleştirin.",
|
||||||
"switchToETHWallet": "Lütfen bir Ethereum cüzdanına geçin ve tekrar deneyin"
|
"switchToETHWallet": "Lütfen bir Ethereum cüzdanına geçin ve tekrar deneyin",
|
||||||
|
"unspent_change": "Değiştirmek"
|
||||||
}
|
}
|
||||||
|
|
|
@ -730,5 +730,6 @@
|
||||||
"domain_looks_up": "Пошук доменів",
|
"domain_looks_up": "Пошук доменів",
|
||||||
"require_for_exchanges_to_external_wallets": "Потрібен для обміну на зовнішні гаманці",
|
"require_for_exchanges_to_external_wallets": "Потрібен для обміну на зовнішні гаманці",
|
||||||
"camera_permission_is_required": "Потрібен дозвіл камери.\nУвімкніть його в налаштуваннях програми.",
|
"camera_permission_is_required": "Потрібен дозвіл камери.\nУвімкніть його в налаштуваннях програми.",
|
||||||
"switchToETHWallet": "Перейдіть на гаманець Ethereum і повторіть спробу"
|
"switchToETHWallet": "Перейдіть на гаманець Ethereum і повторіть спробу",
|
||||||
|
"unspent_change": "Зміна"
|
||||||
}
|
}
|
||||||
|
|
|
@ -722,5 +722,6 @@
|
||||||
"domain_looks_up": "ڈومین تلاش کرنا",
|
"domain_looks_up": "ڈومین تلاش کرنا",
|
||||||
"require_for_exchanges_to_external_wallets": "۔ﮯﮨ ﺕﺭﻭﺮﺿ ﯽﮐ ﮯﻟﺩﺎﺒﺗ ﮟﯿﻣ ﮮﻮﭩﺑ ﯽﻧﻭﺮﯿﺑ",
|
"require_for_exchanges_to_external_wallets": "۔ﮯﮨ ﺕﺭﻭﺮﺿ ﯽﮐ ﮯﻟﺩﺎﺒﺗ ﮟﯿﻣ ﮮﻮﭩﺑ ﯽﻧﻭﺮﯿﺑ",
|
||||||
"camera_permission_is_required": "۔ﮯﮨ ﺭﺎﮐﺭﺩ ﺕﺯﺎﺟﺍ ﯽﮐ ﮮﺮﻤﯿﮐ",
|
"camera_permission_is_required": "۔ﮯﮨ ﺭﺎﮐﺭﺩ ﺕﺯﺎﺟﺍ ﯽﮐ ﮮﺮﻤﯿﮐ",
|
||||||
"switchToETHWallet": "۔ﮟﯾﺮﮐ ﺶﺷﻮﮐ ﮦﺭﺎﺑﻭﺩ ﺭﻭﺍ ﮟﯾﺮﮐ ﭻﺋﻮﺳ ﺮﭘ ﭧﯿﻟﺍﻭ Ethereum ﻡﺮﮐ ﮦﺍﺮﺑ"
|
"switchToETHWallet": "۔ﮟﯾﺮﮐ ﺶﺷﻮﮐ ﮦﺭﺎﺑﻭﺩ ﺭﻭﺍ ﮟﯾﺮﮐ ﭻﺋﻮﺳ ﺮﭘ ﭧﯿﻟﺍﻭ Ethereum ﻡﺮﮐ ﮦﺍﺮﺑ",
|
||||||
|
"unspent_change": "تبدیل کریں"
|
||||||
}
|
}
|
||||||
|
|
|
@ -724,5 +724,6 @@
|
||||||
"domain_looks_up": "Awọn wiwa agbegbe",
|
"domain_looks_up": "Awọn wiwa agbegbe",
|
||||||
"require_for_exchanges_to_external_wallets": "Beere fun awọn paṣipaarọ si awọn apamọwọ ita",
|
"require_for_exchanges_to_external_wallets": "Beere fun awọn paṣipaarọ si awọn apamọwọ ita",
|
||||||
"camera_permission_is_required": "A nilo igbanilaaye kamẹra.\nJọwọ jeki o lati app eto.",
|
"camera_permission_is_required": "A nilo igbanilaaye kamẹra.\nJọwọ jeki o lati app eto.",
|
||||||
"switchToETHWallet": "Jọwọ yipada si apamọwọ Ethereum ki o tun gbiyanju lẹẹkansi"
|
"switchToETHWallet": "Jọwọ yipada si apamọwọ Ethereum ki o tun gbiyanju lẹẹkansi",
|
||||||
|
"unspent_change": "Yipada"
|
||||||
}
|
}
|
||||||
|
|
|
@ -729,5 +729,6 @@
|
||||||
"domain_looks_up": "域名查找",
|
"domain_looks_up": "域名查找",
|
||||||
"require_for_exchanges_to_external_wallets": "需要兑换到外部钱包",
|
"require_for_exchanges_to_external_wallets": "需要兑换到外部钱包",
|
||||||
"camera_permission_is_required": "需要相机许可。\n请从应用程序设置中启用它。",
|
"camera_permission_is_required": "需要相机许可。\n请从应用程序设置中启用它。",
|
||||||
"switchToETHWallet": "请切换到以太坊钱包并重试"
|
"switchToETHWallet": "请切换到以太坊钱包并重试",
|
||||||
|
"unspent_change": "改变"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue