Merge branch 'main' of https://github.com/cake-tech/cake_wallet into CW-659-Automated-Integrated-Tests

This commit is contained in:
Blazebrain 2024-08-19 16:38:11 +01:00
commit 9cf6014bf2
107 changed files with 2380 additions and 1004 deletions

View file

@ -60,7 +60,7 @@ jobs:
path: |
/opt/android/cake_wallet/cw_haven/android/.cxx
/opt/android/cake_wallet/scripts/monero_c/release
key: ${{ hashFiles('**/prepare_moneroc.sh' ,'**/build_monero_all.sh') }}
key: ${{ hashFiles('**/prepare_moneroc.sh' ,'**/build_monero_all.sh' ,'**/cache_dependencies.yml') }}
- if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }}
name: Generate Externals

View file

@ -81,7 +81,7 @@ jobs:
path: |
/opt/android/cake_wallet/cw_haven/android/.cxx
/opt/android/cake_wallet/scripts/monero_c/release
key: ${{ hashFiles('**/prepare_moneroc.sh' ,'**/build_monero_all.sh') }}
key: ${{ hashFiles('**/prepare_moneroc.sh' ,'**/build_monero_all.sh' ,'**/cache_dependencies.yml') }}
- if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }}
name: Generate Externals

View file

@ -75,7 +75,7 @@ jobs:
path: |
/opt/android/cake_wallet/cw_haven/android/.cxx
/opt/android/cake_wallet/scripts/monero_c/release
key: linux-${{ hashFiles('**/prepare_moneroc.sh' ,'**/build_monero_all.sh') }}
key: linux_${{ hashFiles('**/prepare_moneroc.sh' ,'**/build_monero_all.sh' ,'**/cache_dependencies.yml') }}
- if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }}
name: Generate Externals
@ -174,13 +174,14 @@ jobs:
with:
path: /opt/android/cake_wallet/build/linux/x64/release/${{env.BRANCH_NAME}}.zip
- name: Send Test APK
continue-on-error: true
uses: adrey/slack-file-upload-action@1.0.5
with:
token: ${{ secrets.SLACK_APP_TOKEN }}
path: /opt/android/cake_wallet/build/linux/x64/release/${{env.BRANCH_NAME}}.zip
channel: ${{ secrets.SLACK_APK_CHANNEL }}
title: "${{ env.BRANCH_NAME }}_linux.zip"
filename: ${{ env.BRANCH_NAME }}_linux.zip
initial_comment: ${{ github.event.head_commit.message }}
# Just as an artifact would be enough
# - name: Send Test APK
# continue-on-error: true
# uses: adrey/slack-file-upload-action@1.0.5
# with:
# token: ${{ secrets.SLACK_APP_TOKEN }}
# path: /opt/android/cake_wallet/build/linux/x64/release/${{env.BRANCH_NAME}}.zip
# channel: ${{ secrets.SLACK_APK_CHANNEL }}
# title: "${{ env.BRANCH_NAME }}_linux.zip"
# filename: ${{ env.BRANCH_NAME }}_linux.zip
# initial_comment: ${{ github.event.head_commit.message }}

View file

@ -2,3 +2,6 @@
uri: rpc.ankr.com
is_default: true
useSSL: true
-
uri: api.mainnet-beta.solana.com:443
useSSL: true

View file

@ -1,4 +1,3 @@
Monero synchronization improvements
Enhance error handling
UI enhancements
Scan and verify messages
Synchronization enhancements
Bug fixes

View file

@ -1,6 +1,3 @@
Wallets enhancements
Monero synchronization improvements
Improve wallet backups
Enhance error handling
UI enhancements
Scan and verify messages
Synchronization enhancements
Bug fixes

View file

@ -66,6 +66,7 @@ class ElectrumClient {
try {
await socket?.close();
socket = null;
} catch (_) {}
try {
@ -90,7 +91,8 @@ class ElectrumClient {
}
_setConnectionStatus(ConnectionStatus.connected);
socket!.listen((Uint8List event) {
socket!.listen(
(Uint8List event) {
try {
final msg = utf8.decode(event.toList());
final messagesList = msg.split("\n");
@ -103,7 +105,9 @@ class ElectrumClient {
} catch (e) {
print(e.toString());
}
}, onError: (Object error) {
},
onError: (Object error) {
socket = null;
final errorMsg = error.toString();
print(errorMsg);
unterminatedString = '';
@ -113,10 +117,14 @@ class ElectrumClient {
if (currentHost != null && isErrorForCurrentHost)
_setConnectionStatus(ConnectionStatus.failed);
}, onDone: () {
},
onDone: () {
socket = null;
unterminatedString = '';
if (host == socket?.address.host) _setConnectionStatus(ConnectionStatus.disconnected);
});
},
cancelOnError: true,
);
keepAlive();
}

View file

@ -42,6 +42,7 @@ import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:rxdart/subjects.dart';
import 'package:http/http.dart' as http;
import 'package:sp_scanner/sp_scanner.dart';
part 'electrum_wallet.g.dart';
@ -132,6 +133,7 @@ abstract class ElectrumWalletBase
final String? _mnemonic;
Bip32Slip10Secp256k1 get hd => accountHD.childKey(Bip32KeyIndex(0));
Bip32Slip10Secp256k1 get sideHd => accountHD.childKey(Bip32KeyIndex(1));
final EncryptionFileUtils encryptionFileUtils;
final String? passphrase;
@ -216,10 +218,7 @@ abstract class ElectrumWalletBase
if (electrumClient.isConnected) {
syncStatus = SyncedSyncStatus();
} else {
if (electrumClient.uri != null) {
await electrumClient.connectToUri(electrumClient.uri!, useSSL: electrumClient.useSSL);
startSync();
}
syncStatus = NotConnectedSyncStatus();
}
}
}
@ -263,8 +262,10 @@ abstract class ElectrumWalletBase
Future<Isolate>? _isolate;
void Function(FlutterErrorDetails)? _onError;
Timer? _reconnectTimer;
Timer? _autoSaveTimer;
static const int _autoSaveInterval = 30;
Timer? _updateFeeRateTimer;
static const int _autoSaveInterval = 1;
Future<void> init() async {
await walletAddresses.init();
@ -272,7 +273,7 @@ abstract class ElectrumWalletBase
await save();
_autoSaveTimer =
Timer.periodic(Duration(seconds: _autoSaveInterval), (_) async => await save());
Timer.periodic(Duration(minutes: _autoSaveInterval), (_) async => await save());
}
@action
@ -425,6 +426,10 @@ abstract class ElectrumWalletBase
await updateTransactions();
await updateAllUnspents();
await updateBalance();
updateFeeRates();
_updateFeeRateTimer ??=
Timer.periodic(const Duration(minutes: 1), (timer) async => await updateFeeRates());
if (alwaysScan == true) {
_setListeners(walletInfo.restoreHeight);
@ -591,7 +596,7 @@ abstract class ElectrumWalletBase
}
final derivationPath =
"${_hardenedDerivationPath(walletInfo.derivationInfo?.derivationPath ?? "m/0'")}"
"${_hardenedDerivationPath(walletInfo.derivationInfo?.derivationPath ?? electrum_path)}"
"/${utx.bitcoinAddressRecord.isHidden ? "1" : "0"}"
"/${utx.bitcoinAddressRecord.index}";
publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath);
@ -993,11 +998,29 @@ abstract class ElectrumWalletBase
bool hasTaprootInputs = false;
final transaction = txb.buildTransaction((txDigest, utxo, publicKey, sighash) {
final key = estimatedTx.inputPrivKeyInfos
.firstWhereOrNull((element) => element.privkey.getPublic().toHex() == publicKey);
String error = "Cannot find private key.";
ECPrivateInfo? key;
if (estimatedTx.inputPrivKeyInfos.isEmpty) {
error += "\nNo private keys generated.";
} else {
error += "\nAddress: ${utxo.ownerDetails.address.toAddress()}";
key = estimatedTx.inputPrivKeyInfos.firstWhereOrNull((element) {
final elemPubkey = element.privkey.getPublic().toHex();
if (elemPubkey == publicKey) {
return true;
} else {
error += "\nExpected: $publicKey";
error += "\nPubkey: $elemPubkey";
return false;
}
});
}
if (key == null) {
throw Exception("Cannot find private key");
throw Exception(error);
}
if (utxo.utxo.isP2tr()) {
@ -1195,6 +1218,7 @@ abstract class ElectrumWalletBase
await electrumClient.close();
} catch (_) {}
_autoSaveTimer?.cancel();
_updateFeeRateTimer?.cancel();
}
@action
@ -1353,7 +1377,7 @@ abstract class ElectrumWalletBase
if (confirmations > 0) return false;
if (transactionHex == null) {
if (transactionHex == null || transactionHex.isEmpty) {
return false;
}
@ -1851,11 +1875,70 @@ abstract class ElectrumWalletBase
? walletAddresses.allAddresses.firstWhere((element) => element.address == address).index
: null;
final HD = index == null ? hd : hd.childKey(Bip32KeyIndex(index));
final priv = ECPrivate.fromWif(
WifEncoder.encode(HD.privateKey.raw, netVer: network.wifNetVer),
netVersion: network.wifNetVer,
);
return priv.signMessage(StringUtils.encode(message));
final priv = ECPrivate.fromHex(HD.privateKey.privKey.toHex());
String messagePrefix = '\x18Bitcoin Signed Message:\n';
final hexEncoded = priv.signMessage(utf8.encode(message), messagePrefix: messagePrefix);
final decodedSig = hex.decode(hexEncoded);
return base64Encode(decodedSig);
}
@override
Future<bool> verifyMessage(String message, String signature, {String? address = null}) async {
if (address == null) {
return false;
}
List<int> sigDecodedBytes = [];
if (signature.endsWith('=')) {
sigDecodedBytes = base64.decode(signature);
} else {
sigDecodedBytes = hex.decode(signature);
}
if (sigDecodedBytes.length != 64 && sigDecodedBytes.length != 65) {
throw ArgumentException(
"signature must be 64 bytes without recover-id or 65 bytes with recover-id");
}
String messagePrefix = '\x18Bitcoin Signed Message:\n';
final messageHash = QuickCrypto.sha256Hash(
BitcoinSignerUtils.magicMessage(utf8.encode(message), messagePrefix));
List<int> correctSignature =
sigDecodedBytes.length == 65 ? sigDecodedBytes.sublist(1) : List.from(sigDecodedBytes);
List<int> rBytes = correctSignature.sublist(0, 32);
List<int> sBytes = correctSignature.sublist(32);
final sig = ECDSASignature(BigintUtils.fromBytes(rBytes), BigintUtils.fromBytes(sBytes));
List<int> possibleRecoverIds = [0, 1];
final baseAddress = addressTypeFromStr(address, network);
for (int recoveryId in possibleRecoverIds) {
final pubKey = sig.recoverPublicKey(messageHash, Curves.generatorSecp256k1, recoveryId);
final recoveredPub = ECPublic.fromBytes(pubKey!.toBytes());
String? recoveredAddress;
if (baseAddress is P2pkAddress) {
recoveredAddress = recoveredPub.toP2pkAddress().toAddress(network);
} else if (baseAddress is P2pkhAddress) {
recoveredAddress = recoveredPub.toP2pkhAddress().toAddress(network);
} else if (baseAddress is P2wshAddress) {
recoveredAddress = recoveredPub.toP2wshAddress().toAddress(network);
} else if (baseAddress is P2wpkhAddress) {
recoveredAddress = recoveredPub.toP2wpkhAddress().toAddress(network);
}
if (recoveredAddress == address) {
return true;
}
}
return false;
}
Future<void> _setInitialHeight() async {
@ -1901,13 +1984,6 @@ abstract class ElectrumWalletBase
break;
case ConnectionStatus.failed:
syncStatus = LostConnectionSyncStatus();
// wait for 5 seconds and then try to reconnect:
Future.delayed(Duration(seconds: 5), () {
electrumClient.connectToUri(
node!.uri,
useSSL: node!.useSSL ?? false,
);
});
break;
case ConnectionStatus.connecting:
syncStatus = ConnectingSyncStatus();
@ -1917,7 +1993,11 @@ abstract class ElectrumWalletBase
}
void _syncStatusReaction(SyncStatus syncStatus) async {
if (syncStatus is NotConnectedSyncStatus) {
if (syncStatus is SyncingSyncStatus) {
return;
}
if (syncStatus is NotConnectedSyncStatus || syncStatus is LostConnectionSyncStatus) {
// Needs to re-subscribe to all scripthashes when reconnected
_scripthashesUpdateSubject = {};
@ -1925,7 +2005,8 @@ abstract class ElectrumWalletBase
_isTryingToConnect = true;
Future.delayed(Duration(seconds: 10), () {
_reconnectTimer?.cancel();
_reconnectTimer = Timer(Duration(seconds: 10), () {
if (this.syncStatus is! SyncedSyncStatus && this.syncStatus is! SyncedTipSyncStatus) {
this.electrumClient.connectToUri(
node!.uri,

View file

@ -1,6 +1,9 @@
import 'package:bip39/bip39.dart' as bip39;
import 'dart:convert';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:blockchain_utils/blockchain_utils.dart';
import 'package:blockchain_utils/signer/ecdsa_signing_key.dart';
import 'package:bip39/bip39.dart' as bip39;
import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
@ -17,6 +20,9 @@ import 'package:cw_core/wallet_keys_file.dart';
import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:bitcoin_base/src/crypto/keypair/sign_utils.dart';
import 'package:pointycastle/ecc/api.dart';
import 'package:pointycastle/ecc/curves/secp256k1.dart';
part 'litecoin_wallet.g.dart';
@ -167,4 +173,127 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
return 0;
}
@override
Future<String> signMessage(String message, {String? address = null}) async {
final index = address != null
? walletAddresses.allAddresses.firstWhere((element) => element.address == address).index
: null;
final HD = index == null ? hd : hd.childKey(Bip32KeyIndex(index));
final priv = ECPrivate.fromHex(HD.privateKey.privKey.toHex());
final privateKey = ECDSAPrivateKey.fromBytes(
priv.toBytes(),
Curves.generatorSecp256k1,
);
final signature =
signLitecoinMessage(utf8.encode(message), privateKey: privateKey, bipPrive: priv.prive);
return base64Encode(signature);
}
List<int> _magicPrefix(List<int> message, List<int> messagePrefix) {
final encodeLength = IntUtils.encodeVarint(message.length);
return [...messagePrefix, ...encodeLength, ...message];
}
List<int> signLitecoinMessage(List<int> message,
{required ECDSAPrivateKey privateKey, required Bip32PrivateKey bipPrive}) {
String messagePrefix = '\x19Litecoin Signed Message:\n';
final messageHash = QuickCrypto.sha256Hash(magicMessage(message, messagePrefix));
final signingKey = EcdsaSigningKey(privateKey);
ECDSASignature ecdsaSign =
signingKey.signDigestDeterminstic(digest: messageHash, hashFunc: () => SHA256());
final n = Curves.generatorSecp256k1.order! >> 1;
BigInt newS;
if (ecdsaSign.s.compareTo(n) > 0) {
newS = Curves.generatorSecp256k1.order! - ecdsaSign.s;
} else {
newS = ecdsaSign.s;
}
final rawSig = ECDSASignature(ecdsaSign.r, newS);
final rawSigBytes = rawSig.toBytes(BitcoinSignerUtils.baselen);
final pub = bipPrive.publicKey;
final ECDomainParameters curve = ECCurve_secp256k1();
final point = curve.curve.decodePoint(pub.point.toBytes());
final rawSigEc = ECSignature(rawSig.r, rawSig.s);
final recId = SignUtils.findRecoveryId(
SignUtils.getHexString(messageHash, offset: 0, length: messageHash.length),
rawSigEc,
Uint8List.fromList(pub.uncompressed),
);
final v = recId + 27 + (point!.isCompressed ? 4 : 0);
final combined = Uint8List.fromList([v, ...rawSigBytes]);
return combined;
}
List<int> magicMessage(List<int> message, String messagePrefix) {
final prefixBytes = StringUtils.encode(messagePrefix);
final magic = _magicPrefix(message, prefixBytes);
return QuickCrypto.sha256Hash(magic);
}
@override
Future<bool> verifyMessage(String message, String signature, {String? address = null}) async {
if (address == null) {
return false;
}
List<int> sigDecodedBytes = [];
if (signature.endsWith('=')) {
sigDecodedBytes = base64.decode(signature);
} else {
sigDecodedBytes = hex.decode(signature);
}
if (sigDecodedBytes.length != 64 && sigDecodedBytes.length != 65) {
throw ArgumentException(
"litecoin signature must be 64 bytes without recover-id or 65 bytes with recover-id");
}
String messagePrefix = '\x19Litecoin Signed Message:\n';
final messageHash = QuickCrypto.sha256Hash(magicMessage(utf8.encode(message), messagePrefix));
List<int> correctSignature =
sigDecodedBytes.length == 65 ? sigDecodedBytes.sublist(1) : List.from(sigDecodedBytes);
List<int> rBytes = correctSignature.sublist(0, 32);
List<int> sBytes = correctSignature.sublist(32);
final sig = ECDSASignature(BigintUtils.fromBytes(rBytes), BigintUtils.fromBytes(sBytes));
List<int> possibleRecoverIds = [0, 1];
final baseAddress = addressTypeFromStr(address, network);
for (int recoveryId in possibleRecoverIds) {
final pubKey = sig.recoverPublicKey(messageHash, Curves.generatorSecp256k1, recoveryId);
final recoveredPub = ECPublic.fromBytes(pubKey!.toBytes());
String? recoveredAddress;
if (baseAddress is P2pkAddress) {
recoveredAddress = recoveredPub.toP2pkAddress().toAddress(network);
} else if (baseAddress is P2pkhAddress) {
recoveredAddress = recoveredPub.toP2pkhAddress().toAddress(network);
} else if (baseAddress is P2wshAddress) {
recoveredAddress = recoveredPub.toP2wshAddress().toAddress(network);
} else if (baseAddress is P2wpkhAddress) {
recoveredAddress = recoveredPub.toP2wpkhAddress().toAddress(network);
}
if (recoveredAddress == address) {
return true;
}
}
return false;
}
}

View file

@ -23,7 +23,7 @@ String generateP2SHAddress({
required int index,
}) =>
ECPublic.fromBip32(hd.childKey(Bip32KeyIndex(index)).publicKey)
.toP2wshInP2sh()
.toP2wpkhInP2sh()
.toAddress(network);
String generateP2WSHAddress({

View file

@ -67,11 +67,11 @@ packages:
source: git
version: "1.0.1"
bitcoin_base:
dependency: "direct main"
dependency: "direct overridden"
description:
path: "."
ref: cake-update-v4
resolved-ref: "574486bfcdbbaf978dcd006b46fc8716f880da29"
ref: cake-update-v5
resolved-ref: ff2b10eb27b0254ce4518d054332d97d77d9b380
url: "https://github.com/cake-tech/bitcoin_base"
source: git
version: "4.7.0"

View file

@ -25,10 +25,6 @@ dependencies:
ref: Add-Support-For-OP-Return-data
rxdart: ^0.27.5
cryptography: ^2.0.5
bitcoin_base:
git:
url: https://github.com/cake-tech/bitcoin_base
ref: cake-update-v4
blockchain_utils:
git:
url: https://github.com/cake-tech/blockchain_utils
@ -57,6 +53,10 @@ dependency_overrides:
url: https://github.com/cake-tech/ledger-flutter.git
ref: cake-v3
watcher: ^1.1.0
bitcoin_base:
git:
url: https://github.com/cake-tech/bitcoin_base
ref: cake-update-v5
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

View file

@ -202,11 +202,12 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
@override
Future<String> signMessage(String message, {String? address = null}) async {
final index = address != null
? walletAddresses.allAddresses
.firstWhere((element) => element.address == AddressUtils.toLegacyAddress(address))
.index
int? index;
try {
index = address != null
? walletAddresses.allAddresses.firstWhere((element) => element.address == address).index
: null;
} catch (_) {}
final HD = index == null ? hd : hd.childKey(Bip32KeyIndex(index));
final priv = ECPrivate.fromWif(
WifEncoder.encode(HD.privateKey.raw, netVer: network.wifNetVer),

View file

@ -25,10 +25,6 @@ dependencies:
git:
url: https://github.com/cake-tech/bitbox-flutter.git
ref: Add-Support-For-OP-Return-data
bitcoin_base:
git:
url: https://github.com/cake-tech/bitcoin_base
ref: cake-update-v4
blockchain_utils:
git:
url: https://github.com/cake-tech/blockchain_utils
@ -43,6 +39,10 @@ dev_dependencies:
dependency_overrides:
watcher: ^1.1.0
bitcoin_base:
git:
url: https://github.com/cake-tech/bitcoin_base
ref: cake-update-v5
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

View file

@ -69,7 +69,6 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
int calculateEstimatedFee(TransactionPriority priority, int? amount);
// void fetchTransactionsAsync(
// void Function(TransactionType transaction) onTransactionLoaded,
// {void Function() onFinished});
@ -92,7 +91,9 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
Future<void> renameWalletFiles(String newWalletName);
Future<String> signMessage(String message, {String? address = null}) => throw UnimplementedError();
Future<String> signMessage(String message, {String? address = null});
Future<bool> verifyMessage(String message, String signature, {String? address = null});
bool? isTestnet;
}

View file

@ -35,6 +35,7 @@ import 'package:mobx/mobx.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:web3dart/crypto.dart';
import 'package:web3dart/web3dart.dart';
import 'package:eth_sig_util/eth_sig_util.dart';
import 'evm_chain_transaction_info.dart';
import 'evm_erc20_balance.dart';
@ -112,6 +113,8 @@ abstract class EVMChainWalletBase
int? gasBaseFee = 0;
int estimatedGasUnits = 0;
Timer? _updateFeesTimer;
bool _isTransactionUpdating;
// TODO: remove after integrating our own node and having eth_newPendingTransactionFilter
@ -262,6 +265,7 @@ abstract class EVMChainWalletBase
void close() {
_client.stop();
_transactionsUpdateTimer?.cancel();
_updateFeesTimer?.cancel();
}
@action
@ -296,7 +300,7 @@ abstract class EVMChainWalletBase
await _updateEstimatedGasFeeParams();
Timer.periodic(const Duration(seconds: 10), (timer) async {
_updateFeesTimer ??= Timer.periodic(const Duration(seconds: 30), (timer) async {
await _updateEstimatedGasFeeParams();
});
@ -692,8 +696,21 @@ abstract class EVMChainWalletBase
}
@override
Future<String> signMessage(String message, {String? address}) async =>
bytesToHex(await _evmChainPrivateKey.signPersonalMessage(ascii.encode(message)));
Future<String> signMessage(String message, {String? address}) async {
return bytesToHex(await _evmChainPrivateKey.signPersonalMessage(ascii.encode(message)));
}
@override
Future<bool> verifyMessage(String message, String signature, {String? address}) async {
if (address == null) {
return false;
}
final recoveredAddress = EthSigUtil.recoverPersonalSignature(
message: ascii.encode(message),
signature: signature,
);
return recoveredAddress.toUpperCase() == address.toUpperCase();
}
Web3Client? getWeb3Client() => _client.getWeb3Client();

View file

@ -13,6 +13,8 @@ dependencies:
flutter:
sdk: flutter
web3dart: ^2.7.1
eth_sig_util: ^0.0.9
erc20: ^1.0.1
bip39: ^1.0.6
bip32: ^2.0.0
hex: ^0.2.0

View file

@ -10,10 +10,8 @@ import 'package:cw_haven/haven_transaction_info.dart';
import 'package:cw_haven/haven_wallet_addresses.dart';
import 'package:cw_core/monero_wallet_utils.dart';
import 'package:cw_haven/api/structs/pending_transaction.dart';
import 'package:flutter/foundation.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_haven/api/transaction_history.dart' as haven_transaction_history;
//import 'package:cw_haven/wallet.dart';
import 'package:cw_haven/api/wallet.dart' as haven_wallet;
import 'package:cw_haven/api/transaction_history.dart' as transaction_history;
import 'package:cw_haven/api/monero_output.dart';
@ -123,7 +121,8 @@ abstract class HavenWalletBase
login: node.login,
password: node.password,
useSSL: node.useSSL ?? false,
isLightWallet: false, // FIXME: hardcoded value
isLightWallet: false,
// FIXME: hardcoded value
socksProxyAddress: node.socksProxyAddress);
haven_wallet.setTrustedDaemon(node.trusted);
@ -419,4 +418,12 @@ abstract class HavenWalletBase
@override
String get password => _password;
@override
Future<String> signMessage(String message, {String? address = null}) =>
throw UnimplementedError();
@override
Future<bool> verifyMessage(String message, String signature, {String? address = null}) =>
throw UnimplementedError();
}

View file

@ -39,16 +39,30 @@ String getSeed() {
if (polyseed != "") {
return polyseed;
}
final legacy = monero.Wallet_seed(wptr!, seedOffset: '');
final legacy = getSeedLegacy("English");
return legacy;
}
String getSeedLegacy(String? language) {
var legacy = monero.Wallet_seed(wptr!, seedOffset: '');
switch (language) {
case "Chinese (Traditional)": language = "Chinese (simplified)"; break;
case "Chinese (Simplified)": language = "Chinese (simplified)"; break;
case "Korean": language = "English"; break;
case "Czech": language = "English"; break;
case "Japanese": language = "English"; break;
}
if (monero.Wallet_status(wptr!) != 0) {
monero.Wallet_setSeedLanguage(wptr!, language: language ?? "English");
legacy = monero.Wallet_seed(wptr!, seedOffset: '');
}
if (monero.Wallet_status(wptr!) != 0) {
final err = monero.Wallet_errorString(wptr!);
if (legacy.isNotEmpty) {
return "$err\n\n$legacy";
}
return err;
}
return legacy;
}
@ -302,3 +316,7 @@ Future<bool> trustedDaemon() async => monero.Wallet_trustedDaemon(wptr!);
String signMessage(String message, {String address = ""}) {
return monero.Wallet_signMessage(wptr!, message: message, address: address);
}
bool verifyMessage(String message, String address, String signature) {
return monero.Wallet_verifySignedMessage(wptr!, message: message, address: address, signature: signature);
}

View file

@ -8,10 +8,7 @@ import 'package:cw_monero/api/exceptions/wallet_opening_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';
import 'package:cw_monero/api/wallet.dart';
import 'package:flutter/foundation.dart';
import 'package:cw_monero/api/transaction_history.dart';
import 'package:cw_monero/api/wallet.dart';
import 'package:flutter/foundation.dart';
import 'package:monero/monero.dart' as monero;
class MoneroCException implements Exception {
@ -126,7 +123,16 @@ void restoreWalletFromKeysSync(
int nettype = 0,
int restoreHeight = 0}) {
txhistory = null;
final newWptr = monero.WalletManager_createWalletFromKeys(
final newWptr = spendKey != ""
? monero.WalletManager_createDeterministicWalletFromSpendKey(
wmPtr,
path: path,
password: password,
language: language,
spendKeyString: spendKey,
newWallet: true, // TODO(mrcyjanek): safe to remove
restoreHeight: restoreHeight)
: monero.WalletManager_createWalletFromKeys(
wmPtr,
path: path,
password: password,

View file

@ -783,4 +783,12 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
final useAddress = address ?? "";
return monero_wallet.signMessage(message, address: useAddress);
}
@override
Future<bool> verifyMessage(String message, String signature, {String? address = null}) async {
if (address == null) return false;
return monero_wallet.verifyMessage(message, address, signature);
}
}

View file

@ -119,7 +119,7 @@ class MoneroWalletService extends WalletService<
}
@override
Future<MoneroWallet> openWallet(String name, String password) async {
Future<MoneroWallet> openWallet(String name, String password, {bool? retryOnFailure}) async {
MoneroWallet? wallet;
try {
final path = await pathForWallet(name: name, type: getType());
@ -181,12 +181,12 @@ class MoneroWalletService extends WalletService<
wallet.onError != null) {
wallet.onError!(FlutterErrorDetails(exception: e, stack: s));
}
if (invalidPassword) {
if (invalidPassword || retryOnFailure == false) {
rethrow;
}
await restoreOrResetWalletFiles(name);
return openWallet(name, password);
return openWallet(name, password, retryOnFailure: false);
}
}

View file

@ -295,10 +295,10 @@ packages:
dependency: transitive
description:
name: hashlib
sha256: "5037d3b8c36384c03a728543ae67d962a56970c5432a50862279fe68ee4c8411"
sha256: d41795742c10947930630118c6836608deeb9047cd05aee32d2baeb697afd66a
url: "https://pub.dev"
source: hosted
version: "1.19.1"
version: "1.19.2"
hashlib_codecs:
dependency: transitive
description:
@ -576,10 +576,10 @@ packages:
dependency: "direct main"
description:
name: polyseed
sha256: edf28042e7b0b28f97a0469aa98e6e4015937cef6b9340cd6ad2822139c95217
sha256: "11d4dbee409db053c5e9cd77382b2f5115f43fc2529158a826a96f3ba505d770"
url: "https://pub.dev"
source: hosted
version: "0.0.5"
version: "0.0.6"
pool:
dependency: transitive
description:

View file

@ -19,7 +19,7 @@ dependencies:
flutter_mobx: ^2.0.6+1
intl: ^0.18.0
encrypt: ^5.0.1
polyseed: ^0.0.5
polyseed: ^0.0.6
cw_core:
path: ../cw_core
monero:

View file

@ -0,0 +1,37 @@
class BlockContentsResponse {
String type;
String account;
String previous;
String representative;
String balance;
String link;
String linkAsAccount;
String signature;
String work;
BlockContentsResponse({
required this.type,
required this.account,
required this.previous,
required this.representative,
required this.balance,
required this.link,
required this.linkAsAccount,
required this.signature,
required this.work,
});
factory BlockContentsResponse.fromJson(Map<String, dynamic> json) {
return BlockContentsResponse(
type: json['type'] as String,
account: json['account'] as String,
previous: json['previous'] as String,
representative: json['representative'] as String,
balance: json['balance'] as String,
link: json['link'] as String,
linkAsAccount: json['link_as_account'] as String,
signature: json['signature'] as String,
work: json['work'] as String,
);
}
}

View file

@ -2,11 +2,11 @@ import 'dart:async';
import 'dart:convert';
import 'package:cw_core/nano_account_info_response.dart';
import 'package:cw_nano/nano_block_info_response.dart';
import 'package:cw_core/n2_node.dart';
import 'package:cw_nano/nano_balance.dart';
import 'package:cw_nano/nano_transaction_model.dart';
import 'package:http/http.dart' as http;
import 'package:nanodart/nanodart.dart';
import 'package:cw_core/node.dart';
import 'package:nanoutil/nanoutil.dart';
import 'package:shared_preferences/shared_preferences.dart';
@ -111,6 +111,27 @@ class NanoClient {
}
}
Future<BlockContentsResponse?> getBlockContents(String block) async {
try {
final response = await http.post(
_node!.uri,
headers: CAKE_HEADERS,
body: jsonEncode(
{
"action": "block_info",
"json_block": "true",
"hash": block,
},
),
);
final data = await jsonDecode(response.body);
return BlockContentsResponse.fromJson(data["contents"] as Map<String, dynamic>);
} catch (e) {
print("error while getting block info $e");
return null;
}
}
Future<String> changeRep({
required String privateKey,
required String repAddress,
@ -135,8 +156,8 @@ class NanoClient {
};
// sign the change block:
final String hash = NanoBlocks.computeStateHash(
NanoAccountType.NANO,
final String hash = NanoSignatures.computeStateHash(
NanoBasedCurrency.NANO,
changeBlock["account"]!,
changeBlock["previous"]!,
changeBlock["representative"]!,
@ -248,7 +269,7 @@ class NanoClient {
}
final String representative = infoResponse.representative;
// link = destination address:
final String link = NanoAccounts.extractPublicKey(destinationAddress);
final String link = NanoDerivations.addressToPublicKey(destinationAddress);
final String linkAsAccount = destinationAddress;
// construct the send block:
@ -262,8 +283,8 @@ class NanoClient {
};
// sign the send block:
final String hash = NanoBlocks.computeStateHash(
NanoAccountType.NANO,
final String hash = NanoSignatures.computeStateHash(
NanoBasedCurrency.NANO,
sendBlock["account"]!,
sendBlock["previous"]!,
sendBlock["representative"]!,
@ -285,7 +306,6 @@ class NanoClient {
Future<void> receiveBlock({
required String blockHash,
required String source,
required String amountRaw,
required String destinationAddress,
required String privateKey,
@ -310,15 +330,56 @@ class NanoClient {
representative = infoData.representative;
}
if ((BigInt.tryParse(amountRaw) ?? BigInt.zero) <= BigInt.zero) {
throw Exception("amountRaw must be greater than zero");
}
BlockContentsResponse? frontierContents;
if (!openBlock) {
// get the block info of the frontier block:
frontierContents = await getBlockContents(frontier);
if (frontierContents == null) {
throw Exception("error while getting frontier block info");
}
final String frontierHash = NanoSignatures.computeStateHash(
NanoBasedCurrency.NANO,
frontierContents.account,
frontierContents.previous,
frontierContents.representative,
BigInt.parse(frontierContents.balance),
frontierContents.link,
);
bool valid = await NanoSignatures.verify(
frontierHash,
frontierContents.signature,
destinationAddress,
);
if (!valid) {
throw Exception(
"Frontier block signature is invalid! Potentially malicious block detected!");
}
}
// first get the account balance:
final BigInt currentBalance = (await getBalance(destinationAddress)).currentBalance;
late BigInt currentBalance;
if (!openBlock) {
currentBalance = BigInt.parse(frontierContents!.balance);
} else {
currentBalance = BigInt.zero;
}
final BigInt txAmount = BigInt.parse(amountRaw);
final BigInt balanceAfterTx = currentBalance + txAmount;
// link = send block hash:
final String link = blockHash;
// this "linkAsAccount" is meaningless:
final String linkAsAccount = NanoAccounts.createAccount(NanoAccountType.NANO, blockHash);
final String linkAsAccount =
NanoDerivations.publicKeyToAddress(blockHash, currency: NanoBasedCurrency.NANO);
// construct the receive block:
Map<String, String> receiveBlock = {
@ -332,8 +393,8 @@ class NanoClient {
};
// sign the receive block:
final String hash = NanoBlocks.computeStateHash(
NanoAccountType.NANO,
final String hash = NanoSignatures.computeStateHash(
NanoBasedCurrency.NANO,
receiveBlock["account"]!,
receiveBlock["previous"]!,
receiveBlock["representative"]!,
@ -345,7 +406,7 @@ class NanoClient {
// get PoW for the receive block:
String? work;
if (openBlock) {
work = await requestWork(NanoAccounts.extractPublicKey(destinationAddress));
work = await requestWork(NanoDerivations.addressToPublicKey(destinationAddress));
} else {
work = await requestWork(frontier);
}
@ -409,10 +470,8 @@ class NanoClient {
for (final blockHash in blocks.keys) {
final block = blocks[blockHash];
final String amountRaw = block["amount"] as String;
final String source = block["source"] as String;
await receiveBlock(
blockHash: blockHash,
source: source,
amountRaw: amountRaw,
privateKey: privateKey,
destinationAddress: destinationAddress,

View file

@ -27,7 +27,6 @@ import 'package:cw_nano/nano_wallet_addresses.dart';
import 'package:cw_nano/nano_wallet_keys.dart';
import 'package:cw_nano/pending_nano_transaction.dart';
import 'package:mobx/mobx.dart';
import 'package:nanodart/nanodart.dart';
import 'package:nanoutil/nanoutil.dart';
part 'nano_wallet.g.dart';
@ -107,7 +106,6 @@ abstract class NanoWalletBase
if (_derivationType == DerivationType.unknown) {
_derivationType = DerivationType.nano;
}
final String type = (_derivationType == DerivationType.nano) ? "standard" : "hd";
// our "mnemonic" is actually a hex form seed:
if (!_mnemonic.contains(' ')) {
@ -122,8 +120,10 @@ abstract class NanoWalletBase
_hexSeed = await NanoDerivations.hdMnemonicListToSeed(_mnemonic.split(' '));
}
}
NanoDerivationType derivationType =
type == "standard" ? NanoDerivationType.STANDARD : NanoDerivationType.HD;
final String type = (_derivationType == DerivationType.nano) ? "standard" : "hd";
NanoDerivationType derivationType = NanoDerivations.stringToType(type);
_privateKey = await NanoDerivations.universalSeedToPrivate(
_hexSeed!,
index: 0,
@ -216,8 +216,8 @@ abstract class NanoWalletBase
balanceAfterTx: runningBalance,
previousHash: previousHash,
);
previousHash = NanoBlocks.computeStateHash(
NanoAccountType.NANO,
previousHash = NanoSignatures.computeStateHash(
NanoBasedCurrency.NANO,
block["account"]!,
block["previous"]!,
block["representative"]!,
@ -535,4 +535,17 @@ abstract class NanoWalletBase
// Delete old name's dir and files
await Directory(currentDirPath).delete(recursive: true);
}
@override
Future<String> signMessage(String message, {String? address = null}) async {
return NanoSignatures.signMessage(message, privateKey!);
}
@override
Future<bool> verifyMessage(String message, String signature, {String? address = null}) async {
if (address == null) {
return false;
}
return await NanoSignatures.verifyMessage(message, signature, address);
}
}

View file

@ -513,7 +513,7 @@ packages:
source: hosted
version: "2.3.0"
nanodart:
dependency: "direct main"
dependency: transitive
description:
name: nanodart
sha256: "4b2f42d60307b54e8cf384d6193a567d07f8efd773858c0d5948246153c13282"
@ -524,11 +524,11 @@ packages:
dependency: "direct main"
description:
path: "."
ref: c37e72817cf0a28162f43124f79661d6c8e0098f
resolved-ref: c37e72817cf0a28162f43124f79661d6c8e0098f
ref: c01a9c552917008d8fbc6b540db657031625b04f
resolved-ref: c01a9c552917008d8fbc6b540db657031625b04f
url: "https://github.com/perishllc/nanoutil.git"
source: git
version: "1.0.0"
version: "1.0.3"
package_config:
dependency: transitive
description:

View file

@ -15,7 +15,6 @@ dependencies:
mobx: ^2.0.7+4
bip39: ^1.0.6
bip32: ^2.0.0
nanodart: ^2.0.0
decimal: ^2.3.3
libcrypto: ^0.2.2
ed25519_hd_key: ^2.2.0
@ -25,7 +24,7 @@ dependencies:
nanoutil:
git:
url: https://github.com/perishllc/nanoutil.git
ref: c37e72817cf0a28162f43124f79661d6c8e0098f
ref: c01a9c552917008d8fbc6b540db657031625b04f
cw_core:
path: ../cw_core

View file

@ -32,6 +32,8 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'package:solana/base58.dart';
import 'package:solana/metaplex.dart' as metaplex;
import 'package:solana/solana.dart';
import 'package:solana/src/crypto/ed25519_hd_keypair.dart';
import 'package:cryptography/cryptography.dart';
part 'solana_wallet.g.dart';
@ -571,17 +573,59 @@ abstract class SolanaWalletBase
});
}
Future<String> signSolanaMessage(String message) async {
@override
Future<String> signMessage(String message, {String? address}) async {
// Convert the message to bytes
final messageBytes = utf8.encode(message);
// Sign the message bytes with the wallet's private key
final signature = await _walletKeyPair!.sign(messageBytes);
final signature = (await _walletKeyPair!.sign(messageBytes)).toString();
// Convert the signature to a hexadecimal string
final hex = HEX.encode(signature.bytes);
return HEX.encode(utf8.encode(signature)).toUpperCase();
}
return hex;
List<List<int>> bytesFromSigString(String signatureString) {
final regex = RegExp(r'Signature\(\[(.+)\], publicKey: (.+)\)');
final match = regex.firstMatch(signatureString);
if (match != null) {
final bytesString = match.group(1)!;
final base58EncodedPublicKeyString = match.group(2)!;
final sigBytes = bytesString.split(', ').map(int.parse).toList();
List<int> pubKeyBytes = base58decode(base58EncodedPublicKeyString);
return [sigBytes, pubKeyBytes];
} else {
throw const FormatException('Invalid Signature string format');
}
}
@override
Future<bool> verifyMessage(String message, String signature, {String? address}) async {
String signatureString = utf8.decode(HEX.decode(signature));
List<List<int>> bytes = bytesFromSigString(signatureString);
final messageBytes = utf8.encode(message);
final sigBytes = bytes[0];
final pubKeyBytes = bytes[1];
if (address == null) {
return false;
}
// make sure the address derived from the public key provided matches the one we expect
final pub = Ed25519HDPublicKey(pubKeyBytes);
if (address != pub.toBase58()) {
return false;
}
return await verifySignature(
message: messageBytes,
signature: sigBytes,
publicKey: Ed25519HDPublicKey(pubKeyBytes),
);
}
SolanaClient? get solanaClient => _client.getSolanaClient;

View file

@ -580,8 +580,18 @@ abstract class TronWalletBase
}
@override
Future<String> signMessage(String message, {String? address}) async =>
_tronPrivateKey.signPersonalMessage(ascii.encode(message));
Future<String> signMessage(String message, {String? address}) async {
return _tronPrivateKey.signPersonalMessage(ascii.encode(message));
}
@override
Future<bool> verifyMessage(String message, String signature, {String? address}) async {
if (address == null) {
return false;
}
TronPublicKey pubKey = TronPublicKey.fromPersonalSignature(ascii.encode(message), signature)!;
return pubKey.toAddress().toString() == address;
}
String getTronBase58AddressFromHex(String hexAddress) => TronAddress(hexAddress).toAddress();

View file

@ -41,16 +41,30 @@ String getSeed() {
if (polyseed != "") {
return polyseed;
}
final legacy = wownero.Wallet_seed(wptr!, seedOffset: '');
final legacy = getSeedLegacy(null);
return legacy;
}
String getSeedLegacy(String? language) {
var legacy = wownero.Wallet_seed(wptr!, seedOffset: '');
switch (language) {
case "Chinese (Traditional)": language = "Chinese (simplified)"; break;
case "Chinese (Simplified)": language = "Chinese (simplified)"; break;
case "Korean": language = "English"; break;
case "Czech": language = "English"; break;
case "Japanese": language = "English"; break;
}
if (wownero.Wallet_status(wptr!) != 0) {
wownero.Wallet_setSeedLanguage(wptr!, language: language ?? "English");
legacy = wownero.Wallet_seed(wptr!, seedOffset: '');
}
if (wownero.Wallet_status(wptr!) != 0) {
final err = wownero.Wallet_errorString(wptr!);
if (legacy.isNotEmpty) {
return "$err\n\n$legacy";
}
return err;
}
return legacy;
}
@ -306,3 +320,7 @@ Future<bool> trustedDaemon() async => wownero.Wallet_trustedDaemon(wptr!);
String signMessage(String message, {String address = ""}) {
return wownero.Wallet_signMessage(wptr!, message: message, address: address);
}
bool verifyMessage(String message, String address, String signature) {
return wownero.Wallet_verifySignedMessage(wptr!, message: message, address: address, signature: signature);
}

View file

@ -140,7 +140,16 @@ void restoreWalletFromKeysSync(
int nettype = 0,
int restoreHeight = 0}) {
txhistory = null;
final newWptr = wownero.WalletManager_createWalletFromKeys(
final newWptr = spendKey != ""
? wownero.WalletManager_createDeterministicWalletFromSpendKey(
wmPtr,
path: path,
password: password,
language: language,
spendKeyString: spendKey,
newWallet: true, // TODO(mrcyjanek): safe to remove
restoreHeight: restoreHeight)
: wownero.WalletManager_createWalletFromKeys(
wmPtr,
path: path,
password: password,

View file

@ -743,4 +743,11 @@ abstract class WowneroWalletBase
final useAddress = address ?? "";
return wownero_wallet.signMessage(message, address: useAddress);
}
@override
Future<bool> verifyMessage(String message, String signature, {String? address = null}) async {
if (address == null) return false;
return wownero_wallet.verifyMessage(message, address, signature);
}
}

View file

@ -295,18 +295,18 @@ packages:
dependency: transitive
description:
name: hashlib
sha256: "71bf102329ddb8e50c8a995ee4645ae7f1728bb65e575c17196b4d8262121a96"
sha256: d41795742c10947930630118c6836608deeb9047cd05aee32d2baeb697afd66a
url: "https://pub.dev"
source: hosted
version: "1.12.0"
version: "1.19.2"
hashlib_codecs:
dependency: transitive
description:
name: hashlib_codecs
sha256: "49e2a471f74b15f1854263e58c2ac11f2b631b5b12c836f9708a35397d36d626"
sha256: "2b570061f5a4b378425be28a576c1e11783450355ad4345a19f606ff3d96db0f"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
version: "2.5.0"
hive:
dependency: transitive
description:
@ -568,10 +568,10 @@ packages:
dependency: "direct main"
description:
name: polyseed
sha256: edf28042e7b0b28f97a0469aa98e6e4015937cef6b9340cd6ad2822139c95217
sha256: "11d4dbee409db053c5e9cd77382b2f5115f43fc2529158a826a96f3ba505d770"
url: "https://pub.dev"
source: hosted
version: "0.0.5"
version: "0.0.6"
pool:
dependency: transitive
description:

View file

@ -19,7 +19,7 @@ dependencies:
flutter_mobx: ^2.0.6+1
intl: ^0.18.0
encrypt: ^5.0.1
polyseed: ^0.0.5
polyseed: ^0.0.6
cw_core:
path: ../cw_core
monero:

View file

@ -4,10 +4,6 @@ import workmanager
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
lazy var resolution : Resolution? = {
return try? Resolution()
}()
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?

View file

@ -275,7 +275,7 @@ class CWBitcoin extends Bitcoin {
return [DerivationType.bip39, DerivationType.electrum];
}
int _countOccurrences(String str, String charToCount) {
int _countCharOccurrences(String str, String charToCount) {
int count = 0;
for (int i = 0; i < str.length; i++) {
if (str[i] == charToCount) {
@ -330,7 +330,7 @@ class CWBitcoin extends Bitcoin {
);
String balancePath = dInfoCopy.derivationPath!;
int derivationDepth = _countOccurrences(balancePath, "/");
int derivationDepth = _countCharOccurrences(balancePath, '/');
// for BIP44
if (derivationDepth == 3 || derivationDepth == 1) {

View file

@ -124,12 +124,12 @@ class DFXBuyProvider extends BuyProvider {
switch (wallet.type) {
case WalletType.ethereum:
case WalletType.polygon:
return wallet.signMessage(message);
return await wallet.signMessage(message);
case WalletType.monero:
case WalletType.litecoin:
case WalletType.bitcoin:
case WalletType.bitcoinCash:
return wallet.signMessage(message, address: walletAddress);
return await wallet.signMessage(message, address: walletAddress);
default:
throw Exception("WalletType is not available for DFX ${wallet.type}");
}

View file

@ -37,15 +37,15 @@ class RobinhoodBuyProvider extends BuyProvider {
String get _apiSecret => secrets.exchangeHelperApiKey;
Future<String> getSignature(String message) {
Future<String> getSignature(String message) async {
switch (wallet.type) {
case WalletType.ethereum:
case WalletType.polygon:
return wallet.signMessage(message);
return await wallet.signMessage(message);
case WalletType.litecoin:
case WalletType.bitcoin:
case WalletType.bitcoinCash:
return wallet.signMessage(message, address: wallet.walletAddresses.address);
return await wallet.signMessage(message, address: wallet.walletAddresses.address);
default:
throw Exception("WalletType is not available for Robinhood ${wallet.type}");
}

View file

@ -1,4 +1,3 @@
import 'package:cake_wallet/core/secure_storage.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cw_core/wallet_info.dart';
@ -57,9 +56,9 @@ class WalletCreationService {
if (credentials.password == null) {
credentials.password = generateWalletPassword();
}
await keyService.saveWalletPassword(
password: credentials.password!, walletName: credentials.name);
}
if (_hasSeedPhraseLengthOption) {
credentials.seedPhraseLength = settingsStore.seedPhraseLength.value;
@ -99,9 +98,9 @@ class WalletCreationService {
if (credentials.password == null) {
credentials.password = generateWalletPassword();
}
await keyService.saveWalletPassword(
password: credentials.password!, walletName: credentials.name);
}
final wallet = await _service!.restoreFromKeys(credentials, isTestnet: isTestnet);
@ -118,9 +117,9 @@ class WalletCreationService {
if (credentials.password == null) {
credentials.password = generateWalletPassword();
}
await keyService.saveWalletPassword(
password: credentials.password!, walletName: credentials.name);
}
final wallet = await _service!.restoreFromSeed(credentials, isTestnet: isTestnet);

View file

@ -60,7 +60,9 @@ class WalletLoadingService {
String corruptedWalletsSeeds = "Corrupted wallets seeds (if retrievable, empty otherwise):";
try {
corruptedWalletsSeeds += await _getCorruptedWalletSeeds(name, type);
} catch (_) {}
} catch (e) {
corruptedWalletsSeeds += "\nFailed to fetch $name seeds: $e";
}
// try opening another wallet that is not corrupted to give user access to the app
final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
@ -90,7 +92,9 @@ class WalletLoadingService {
if (!corruptedWalletsSeeds.contains(seeds)) {
corruptedWalletsSeeds += seeds;
}
} catch (_) {}
} catch (e) {
corruptedWalletsSeeds += "\nFailed to fetch $name seeds: $e";
}
}
}

View file

@ -30,6 +30,12 @@ import 'package:cake_wallet/entities/contact.dart';
import 'package:cake_wallet/entities/contact_record.dart';
import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
import 'package:cake_wallet/src/screens/receive/address_list_page.dart';
import 'package:cake_wallet/view_model/link_view_model.dart';
import 'package:cake_wallet/tron/tron.dart';
import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
import 'package:cake_wallet/view_model/dashboard/sign_view_model.dart';
import 'package:cw_core/receive_page_option.dart';
import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/entities/qr_view_data.dart';
import 'package:cake_wallet/entities/template.dart';
@ -159,7 +165,6 @@ import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/node.dart';
import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart';
import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart';
@ -179,7 +184,6 @@ import 'package:cake_wallet/store/templates/exchange_template_store.dart';
import 'package:cake_wallet/store/templates/send_template_store.dart';
import 'package:cake_wallet/store/wallet_list_store.dart';
import 'package:cake_wallet/store/yat/yat_store.dart';
import 'package:cake_wallet/tron/tron.dart';
import 'package:cake_wallet/view_model/auth_view_model.dart';
import 'package:cake_wallet/view_model/backup_view_model.dart';
import 'package:cake_wallet/view_model/buy/buy_amount_view_model.dart';
@ -193,7 +197,6 @@ import 'package:cake_wallet/view_model/edit_backup_password_view_model.dart';
import 'package:cake_wallet/view_model/exchange/exchange_trade_view_model.dart';
import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
import 'package:cake_wallet/view_model/link_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
import 'package:cake_wallet/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart';
@ -224,7 +227,6 @@ import 'package:cake_wallet/view_model/wallet_unlock_loadable_view_model.dart';
import 'package:cake_wallet/view_model/wallet_unlock_verifiable_view_model.dart';
import 'package:cake_wallet/wownero/wownero.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/receive_page_option.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/foundation.dart';
@ -853,6 +855,8 @@ Future<void> setup({
getIt.registerFactoryParam<ContactPage, ContactRecord?, void>(
(ContactRecord? contact, _) => ContactPage(getIt.get<ContactViewModel>(param1: contact)));
getIt.registerFactory(() => AddressListPage(getIt.get<WalletAddressListViewModel>()));
getIt.registerFactory(() {
final appStore = getIt.get<AppStore>();
return NodeListViewModel(_nodeSource, appStore);
@ -1275,5 +1279,7 @@ Future<void> setup({
getIt.registerFactory(() => NFTViewModel(appStore, getIt.get<BottomSheetService>()));
getIt.registerFactory<TorPage>(() => TorPage(getIt.get<AppStore>()));
getIt.registerFactory(() => SignViewModel(getIt.get<AppStore>().wallet!));
_isSetupFinished = true;
}

View file

@ -21,16 +21,14 @@ Future<void> bootstrap(GlobalKey<NavigatorState> navigatorKey) async {
final settingsStore = getIt.get<SettingsStore>();
final fiatConversionStore = getIt.get<FiatConversionStore>();
final currentWalletName = getIt
.get<SharedPreferences>()
.getString(PreferencesKey.currentWalletName);
final currentWalletName =
getIt.get<SharedPreferences>().getString(PreferencesKey.currentWalletName);
if (currentWalletName != null) {
authenticationStore.installed();
}
startAuthenticationStateChange(authenticationStore, navigatorKey);
startCurrentWalletChangeReaction(
appStore, settingsStore, fiatConversionStore);
startCurrentWalletChangeReaction(appStore, settingsStore, fiatConversionStore);
startCurrentFiatChangeReaction(appStore, settingsStore, fiatConversionStore);
startCurrentFiatApiModeChangeReaction(appStore, settingsStore, fiatConversionStore);
startOnCurrentNodeChangeReaction(appStore);

View file

@ -7,14 +7,17 @@ import 'package:flutter/widgets.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/entities/load_current_wallet.dart';
import 'package:cake_wallet/store/authentication_store.dart';
import 'package:rxdart/subjects.dart';
ReactionDisposer? _onAuthenticationStateChange;
dynamic loginError;
StreamController<dynamic> authenticatedErrorStreamController = StreamController<dynamic>();
StreamController<dynamic> authenticatedErrorStreamController = BehaviorSubject<dynamic>();
void startAuthenticationStateChange(
AuthenticationStore authenticationStore, GlobalKey<NavigatorState> navigatorKey) {
AuthenticationStore authenticationStore,
GlobalKey<NavigatorState> navigatorKey,
) {
authenticatedErrorStreamController.stream.listen((event) {
if (authenticationStore.state == AuthenticationState.allowed) {
ExceptionHandler.showError(event.toString(), delayInSeconds: 3);

View file

@ -27,6 +27,7 @@ import 'package:cake_wallet/src/screens/dashboard/edit_token_page.dart';
import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/address_page.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/nft_details_page.dart';
import 'package:cake_wallet/src/screens/dashboard/sign_page.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart';
import 'package:cake_wallet/src/screens/disclaimer/disclaimer_page.dart';
import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
@ -42,6 +43,8 @@ import 'package:cake_wallet/src/screens/new_wallet/new_wallet_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart';
import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart';
import 'package:cake_wallet/src/screens/nodes/pow_node_create_or_edit_page.dart';
import 'package:cake_wallet/src/screens/receive/address_list_page.dart';
import 'package:cake_wallet/src/screens/restore/sweeping_wallet_page.dart';
import 'package:cake_wallet/src/screens/order_details/order_details_page.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart';
@ -99,6 +102,7 @@ import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/sign_view_model.dart';
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
@ -465,6 +469,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
return MaterialPageRoute<void>(
builder: (_) => getIt.get<ContactListPage>(param1: selectedCurrency));
case Routes.pickerWalletAddress:
return MaterialPageRoute<void>(builder: (_) => getIt.get<AddressListPage>());
case Routes.addressBookAddContact:
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<ContactPage>(param1: settings.arguments as ContactRecord?));
@ -696,6 +703,13 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.torPage:
return MaterialPageRoute<void>(builder: (_) => getIt.get<TorPage>());
case Routes.signPage:
return MaterialPageRoute<void>(
builder: (_) => SignPage(
getIt.get<SignViewModel>(),
),
);
case Routes.connectDevices:
final params = settings.arguments as ConnectDevicePageParams;
return MaterialPageRoute<void>(

View file

@ -31,6 +31,7 @@ class Routes {
static const nanoAccountCreation = '/nano_account_new';
static const addressBook = '/address_book';
static const pickerAddressBook = '/picker_address_book';
static const pickerWalletAddress = '/picker_wallet_address';
static const addressBookAddContact = '/address_book_add_contact';
static const showKeys = '/show_keys';
static const exchangeConfirm = '/exchange_confirm';
@ -103,5 +104,6 @@ class Routes {
static const nftDetailsPage = '/nft_details_page';
static const importNFTPage = '/import_nft_page';
static const torPage = '/tor_page';
static const signPage = '/sign_page';
static const connectDevices = '/device/connect';
}

View file

@ -322,31 +322,31 @@ class CakePayBuyCardDetailPage extends BasePage {
await showPopUp<void>(
context: context,
builder: (_) {
builder: (popupContext) {
return Observer(
builder: (_) => ConfirmSendingAlert(
alertTitle: S.of(context).confirm_sending,
paymentId: S.of(context).payment_id,
alertTitle: S.of(popupContext).confirm_sending,
paymentId: S.of(popupContext).payment_id,
paymentIdValue: order?.orderId,
expirationTime: cakePayPurchaseViewModel.formattedRemainingTime,
onDispose: () => _handleDispose(disposer),
amount: S.of(context).send_amount,
amount: S.of(popupContext).send_amount,
amountValue: pendingTransaction.amountFormatted,
fiatAmountValue:
cakePayPurchaseViewModel.sendViewModel.pendingTransactionFiatAmountFormatted,
fee: S.of(context).send_fee,
fee: S.of(popupContext).send_fee,
feeValue: pendingTransaction.feeFormatted,
feeFiatAmount:
cakePayPurchaseViewModel.sendViewModel.pendingTransactionFeeFiatAmountFormatted,
feeRate: pendingTransaction.feeRate,
outputs: cakePayPurchaseViewModel.sendViewModel.outputs,
rightButtonText: S.of(context).send,
leftButtonText: S.of(context).cancel,
rightButtonText: S.of(popupContext).send,
leftButtonText: S.of(popupContext).cancel,
actionRightButton: () async {
Navigator.of(context).pop();
Navigator.of(popupContext).pop();
await cakePayPurchaseViewModel.sendViewModel.commitTransaction();
},
actionLeftButton: () => Navigator.of(context).pop()));
actionLeftButton: () => Navigator.of(popupContext).pop()));
},
);
}

View file

@ -1,5 +1,4 @@
import 'package:another_flushbar/flushbar.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/entities/desktop_dropdown_item.dart';
import 'package:cake_wallet/generated/i18n.dart';
@ -8,8 +7,9 @@ import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/dropdown_item_widget.dart';
import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_arguments.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/themes/extensions/menu_theme.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/themes/extensions/menu_theme.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
@ -122,9 +122,7 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
}
void _onSelectedWallet(WalletListItem selectedWallet) async {
if (selectedWallet.isCurrent || !selectedWallet.isEnabled) {
return;
}
if (selectedWallet.isCurrent || !selectedWallet.isEnabled) return;
WidgetsBinding.instance.addPostFrameCallback((_) async {
final confirmed = await showPopUp<bool>(
@ -149,9 +147,7 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
Image _imageFor({required WalletType type, bool? isTestnet}) {
switch (type) {
case WalletType.bitcoin:
if (isTestnet == true) {
return tBitcoinIcon;
}
if (isTestnet == true) return tBitcoinIcon;
return bitcoinIcon;
case WalletType.monero:
return moneroIcon;
@ -173,6 +169,8 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
return solanaIcon;
case WalletType.tron:
return tronIcon;
case WalletType.wownero:
return wowneroIcon;
default:
return nonWalletTypeIcon;
}
@ -180,34 +178,33 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
Future<void> _loadWallet(WalletListItem wallet) async {
if (SettingsStoreBase.walletPasswordDirectInput) {
Navigator.of(context).pushNamed(
Routes.walletUnlockLoadable,
Navigator.of(context).pushNamed(Routes.walletUnlockLoadable,
arguments: WalletUnlockArguments(
callback: (bool isAuthenticatedSuccessfully, AuthPageState auth) async {
if (isAuthenticatedSuccessfully) {
auth.close();
setState(() {});
}
}, walletName: wallet.name,
},
walletName: wallet.name,
walletType: wallet.type));
return;
}
widget._authService.authenticateAction(context,
widget._authService.authenticateAction(
context,
onAuthSuccess: (isAuthenticatedSuccessfully) async {
if (!isAuthenticatedSuccessfully) {
return;
}
if (!isAuthenticatedSuccessfully) return;
try {
if (context.mounted) {
if (mounted) {
changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name));
}
await widget.walletListViewModel.loadWallet(wallet);
hideProgressText();
setState(() {});
} catch (e) {
if (context.mounted) {
if (mounted) {
changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
}
}

View file

@ -206,7 +206,7 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
),
contractAddress: _contractAddressController.text,
);
if (context.mounted) {
if (mounted) {
Navigator.pop(context);
}
}

View file

@ -287,8 +287,8 @@ class CryptoBalanceWidget extends StatelessWidget {
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
child: DashBoardRoundedCardWidget(
customBorder: 30,
title: "Monero wallet is broken",
subTitle: "Here are the things that are broken:\n - "
title: "This wallet has encountered an issue",
subTitle: "Here are the things that you should note:\n - "
+dashboardViewModel.isMoneroWalletBrokenReasons.join("\n - ")
+"\n\nPlease restart your wallet and if it doesn't help contact our support.",
onTap: () {},

View file

@ -8,6 +8,7 @@ import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/view_model/dashboard/cake_features_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter_svg/flutter_svg.dart';
@ -64,6 +65,19 @@ class CakeFeaturesPage extends StatelessWidget {
subTitle: S.of(context).nanogpt_subtitle,
onTap: () => _launchUrl("cake.nano-gpt.com"),
),
SizedBox(height: 20),
Observer(
builder: (context) {
if (!dashboardViewModel.hasSignMessages) {
return const SizedBox();
}
return DashBoardRoundedCardWidget(
onTap: () => Navigator.of(context).pushNamed(Routes.signPage),
title: S.current.sign_verify_message,
subTitle: S.current.sign_verify_message_sub,
);
},
),
],
),
),

View file

@ -0,0 +1,202 @@
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/sign_form.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/verify_form.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/dashboard/sign_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:keyboard_actions/keyboard_actions.dart';
import 'package:mobx/mobx.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
class SignPage extends BasePage {
SignPage(this.signViewModel)
: signFormKey = GlobalKey<SignFormState>(),
verifyFormKey = GlobalKey<VerifyFormState>(),
_pages = [],
_controller = PageController(initialPage: 0) {
_pages.add(SignForm(
key: signFormKey,
type: signViewModel.wallet.type,
includeAddress: signViewModel.signIncludesAddress,
));
_pages.add(VerifyForm(
key: verifyFormKey,
type: signViewModel.wallet.type,
));
}
@override
Widget middle(BuildContext context) => Observer(
builder: (_) => Text(
S.current.sign_verify_title,
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
fontFamily: 'Lato',
color: titleColor(context),
),
));
final SignViewModel signViewModel;
final PageController _controller;
final List<Widget> _pages;
final GlobalKey<SignFormState> signFormKey;
final GlobalKey<VerifyFormState> verifyFormKey;
bool _isEffectsInstalled = false;
@override
Widget body(BuildContext context) {
_setEffects(context);
return KeyboardActions(
config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
keyboardBarColor: Theme.of(context).extension<KeyboardTheme>()!.keyboardBarColor,
nextFocus: false,
actions: [
KeyboardActionsItem(
focusNode: FocusNode(),
toolbarButtons: [(_) => KeyboardDoneButton()],
)
],
),
child: Container(
height: 0,
color: Theme.of(context).colorScheme.background,
child: Center(
child: ConstrainedBox(
constraints:
BoxConstraints(maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: PageView.builder(
onPageChanged: (page) {
signViewModel.isSigning = page == 0;
},
controller: _controller,
itemCount: _pages.length,
itemBuilder: (_, index) => SingleChildScrollView(child: _pages[index]),
),
),
if (_pages.length > 1)
Padding(
padding: EdgeInsets.only(top: 10),
child: SmoothPageIndicator(
controller: _controller,
count: _pages.length,
effect: ColorTransitionEffect(
spacing: 6.0,
radius: 6.0,
dotWidth: 6.0,
dotHeight: 6.0,
dotColor: Theme.of(context).hintColor.withOpacity(0.5),
activeDotColor: Theme.of(context).hintColor,
),
),
),
Padding(
padding: EdgeInsets.only(top: 20, bottom: 24, left: 24, right: 24),
child: Column(
children: [
Observer(
builder: (context) {
return LoadingPrimaryButton(
onPressed: () async {
await _confirmForm(context);
},
text: signViewModel.isSigning
? S.current.sign_message
: S.current.verify_message,
color: Theme.of(context)
.extension<WalletListTheme>()!
.createNewWalletButtonBackgroundColor,
textColor: Theme.of(context)
.extension<WalletListTheme>()!
.restoreWalletButtonTextColor,
isLoading: signViewModel.state is IsExecutingState,
isDisabled: signViewModel.state is IsExecutingState,
);
},
),
],
),
)
],
),
),
),
),
);
}
void _setEffects(BuildContext context) async {
if (_isEffectsInstalled) {
return;
}
_isEffectsInstalled = true;
reaction((_) => signViewModel.state, (ExecutionState state) {
if (state is FailureState) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showPopUp<void>(
context: context,
builder: (_) {
return AlertWithOneAction(
alertTitle: S.current.error,
alertContent: state.error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop(),
);
});
});
}
if (state is ExecutedSuccessfullyState) {
if (signViewModel.isSigning) {
signFormKey.currentState!.signatureController.text = state.payload as String;
} else {
WidgetsBinding.instance.addPostFrameCallback((_) {
showPopUp<void>(
context: context,
builder: (_) {
return AlertWithOneAction(
alertTitle: S.current.successful,
alertContent: S.current.message_verified,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop(),
);
});
});
}
}
});
}
Future<void> _confirmForm(BuildContext context) async {
FocusManager.instance.primaryFocus?.unfocus();
if (signViewModel.isSigning) {
String message = signFormKey.currentState!.messageController.text;
String? address;
if (signViewModel.signIncludesAddress) {
address = signFormKey.currentState!.addressController.text;
}
await signViewModel.sign(message, address: address);
} else {
String message = verifyFormKey.currentState!.messageController.text;
String signature = verifyFormKey.currentState!.signatureController.text;
String address = verifyFormKey.currentState!.addressController.text;
await signViewModel.verify(message, signature, address: address);
}
}
}

View file

@ -0,0 +1,98 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/address_text_field.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class SignForm extends StatefulWidget {
SignForm({
Key? key,
required this.type,
required this.includeAddress,
}) : super(key: key);
final WalletType type;
final bool includeAddress;
@override
SignFormState createState() => SignFormState();
}
class SignFormState extends State<SignForm> {
SignFormState()
: formKey = GlobalKey<FormState>(),
messageController = TextEditingController(),
addressController = TextEditingController(),
signatureController = TextEditingController();
final TextEditingController messageController;
final TextEditingController addressController;
final TextEditingController signatureController;
final GlobalKey<FormState> formKey;
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(left: 24, right: 24),
child: Column(
children: [
Form(
key: formKey,
child: Column(
children: [
AddressTextField(
controller: messageController,
placeholder: S.current.message,
options: [AddressTextFieldOption.paste],
buttonColor: Theme.of(context).hintColor,
),
if (widget.includeAddress) ...[
const SizedBox(height: 20),
AddressTextField(
controller: addressController,
options: [
AddressTextFieldOption.paste,
AddressTextFieldOption.walletAddresses
],
buttonColor: Theme.of(context).hintColor,
onSelectedContact: (contact) {
addressController.text = contact.address;
},
selectedCurrency: walletTypeToCryptoCurrency(widget.type),
),
],
],
)),
const SizedBox(height: 20),
GestureDetector(
onTap: () async {
final text = signatureController.text;
if (text.isEmpty) {
return;
}
Clipboard.setData(ClipboardData(text: text));
showBar<void>(context, S.of(context).transaction_details_copied(text));
},
child: BaseTextFormField(
enabled: false,
controller: signatureController,
hintText: S.current.signature,
),
),
],
),
);
}
}

View file

@ -0,0 +1,92 @@
import 'package:cake_wallet/core/wallet_name_validator.dart';
import 'package:cake_wallet/entities/generate_name.dart';
import 'package:cake_wallet/entities/seed_type.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/address_text_field.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/picker.dart';
import 'package:cake_wallet/src/widgets/seed_language_picker.dart';
import 'package:cake_wallet/src/widgets/seed_widget.dart';
import 'package:cake_wallet/themes/extensions/address_theme.dart';
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/seed_type_view_model.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:mobx/mobx.dart';
import 'package:polyseed/polyseed.dart';
class VerifyForm extends StatefulWidget {
VerifyForm({
Key? key,
required this.type,
}) : super(key: key);
final WalletType type;
@override
VerifyFormState createState() => VerifyFormState();
}
class VerifyFormState extends State<VerifyForm> {
VerifyFormState()
: formKey = GlobalKey<FormState>(),
messageController = TextEditingController(),
addressController = TextEditingController(),
signatureController = TextEditingController();
final TextEditingController messageController;
final TextEditingController addressController;
final TextEditingController signatureController;
final GlobalKey<FormState> formKey;
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(left: 24, right: 24),
child: Form(
key: formKey,
child: Column(
children: [
AddressTextField(
controller: messageController,
placeholder: S.current.message,
options: [AddressTextFieldOption.paste],
buttonColor: Theme.of(context).hintColor,
),
const SizedBox(height: 20),
AddressTextField(
controller: addressController,
options: [AddressTextFieldOption.paste, AddressTextFieldOption.walletAddresses],
buttonColor: Theme.of(context).hintColor,
onSelectedContact: (contact) {
addressController.text = contact.address;
},
selectedCurrency: walletTypeToCryptoCurrency(widget.type),
),
const SizedBox(height: 20),
AddressTextField(
controller: signatureController,
placeholder: S.current.signature,
options: [AddressTextFieldOption.paste],
buttonColor: Theme.of(context).hintColor,
),
],
),
),
);
}
}

View file

@ -293,7 +293,7 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
if (state is TransactionCommitted) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (context.mounted) {
if (mounted) {
showPopUp<void>(
context: context,
builder: (BuildContext popupContext) {

View file

@ -75,7 +75,7 @@ class _WalletNameFormState extends State<WalletNameForm> {
_walletNewVM.hasWalletPassword ? TextEditingController() : null;
static const aspectRatioImage = 1.22;
static bool formProcessing = false;
bool _formProcessing = false;
final GlobalKey<FormState> _formKey;
final GlobalKey<SeedLanguageSelectorState> _languageSelectorKey;
@ -95,7 +95,7 @@ class _WalletNameFormState extends State<WalletNameForm> {
if (state is FailureState) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (context.mounted) {
if (mounted) {
showPopUp<void>(
context: context,
builder: (_) {
@ -350,11 +350,11 @@ class _WalletNameFormState extends State<WalletNameForm> {
}
void _confirmForm() async {
if (formProcessing) return;
formProcessing = true;
if (_formProcessing) return;
_formProcessing = true;
try {
if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
formProcessing = false;
_formProcessing = false;
return;
}
if (_walletNewVM.nameExists(_walletNewVM.name)) {
@ -374,10 +374,10 @@ class _WalletNameFormState extends State<WalletNameForm> {
: null);
}
} catch (e) {
formProcessing = false;
_formProcessing = false;
rethrow;
}
formProcessing = false;
_formProcessing = false;
}
bool get isPolyseed => widget._seedTypeViewModel.moneroSeedType == SeedType.polyseed;

View file

@ -0,0 +1,31 @@
import 'package:cake_wallet/src/screens/receive/widgets/address_list.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
class AddressListPage extends BasePage {
AddressListPage(this.addressListViewModel);
final WalletAddressListViewModel addressListViewModel;
@override
String get title => S.current.accounts_subaddresses;
@override
Widget body(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: <Widget>[
AddressList(
addressListViewModel: addressListViewModel,
onSelect: (String address) async {
Navigator.of(context).pop(address);
},
),
],
),
);
}
}

View file

@ -1,4 +1,5 @@
import 'package:cake_wallet/src/screens/nano_accounts/nano_account_list_page.dart';
import 'package:cake_wallet/src/screens/receive/widgets/address_list.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/themes/extensions/balance_page_theme.dart';
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
@ -122,108 +123,7 @@ class ReceivePage extends BasePage {
amountController: _amountController,
isLight: currentTheme.type == ThemeType.light),
),
Observer(
builder: (_) => ListView.separated(
padding: EdgeInsets.all(0),
separatorBuilder: (context, _) => const HorizontalSectionDivider(),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: addressListViewModel.items.length,
itemBuilder: (context, index) {
final item = addressListViewModel.items[index];
Widget cell = Container();
if (item is WalletAccountListHeader) {
cell = HeaderTile(
showTrailingButton: true,
walletAddressListViewModel: addressListViewModel,
trailingButtonTap: () async {
if (addressListViewModel.type == WalletType.monero ||
addressListViewModel.type == WalletType.wownero ||
addressListViewModel.type == WalletType.haven) {
await showPopUp<void>(
context: context,
builder: (_) => getIt.get<MoneroAccountListPage>());
} else {
await showPopUp<void>(
context: context,
builder: (_) => getIt.get<NanoAccountListPage>());
}
},
title: S.of(context).accounts,
trailingIcon: Icon(
Icons.arrow_forward_ios,
size: 14,
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
));
}
if (item is WalletAddressListHeader) {
final hasTitle = item.title != null;
cell = HeaderTile(
title: hasTitle ? item.title! : S.of(context).addresses,
walletAddressListViewModel: addressListViewModel,
showTrailingButton:
!addressListViewModel.isAutoGenerateSubaddressEnabled && !hasTitle,
showSearchButton: true,
trailingButtonTap: () =>
Navigator.of(context).pushNamed(Routes.newSubaddress),
trailingIcon: hasTitle
? null
: Icon(
Icons.add,
size: 20,
color:
Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
),
);
}
if (item is WalletAddressListItem) {
cell = Observer(builder: (_) {
final isCurrent = item.address == addressListViewModel.address.address;
final backgroundColor = isCurrent
? Theme.of(context)
.extension<ReceivePageTheme>()!
.currentTileBackgroundColor
: Theme.of(context)
.extension<ReceivePageTheme>()!
.tilesBackgroundColor;
final textColor = isCurrent
? Theme.of(context)
.extension<ReceivePageTheme>()!
.currentTileTextColor
: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor;
return AddressCell.fromItem(
item,
isCurrent: isCurrent,
hasBalance: addressListViewModel.isElectrumWallet,
backgroundColor: backgroundColor,
textColor: textColor,
onTap: item.isOneTimeReceiveAddress == true
? null
: (_) => addressListViewModel.setAddress(item),
onEdit: item.isOneTimeReceiveAddress == true || item.isPrimary
? null
: () => Navigator.of(context)
.pushNamed(Routes.newSubaddress, arguments: item),
onDelete: !addressListViewModel.isSilentPayments || item.isPrimary
? null
: () => addressListViewModel.deleteAddress(item),
);
});
}
return index != 0
? cell
: ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30), topRight: Radius.circular(30)),
child: cell,
);
})),
AddressList(addressListViewModel: addressListViewModel),
Padding(
padding: EdgeInsets.fromLTRB(24, 24, 24, 32),
child: Text(

View file

@ -0,0 +1,120 @@
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_list_page.dart';
import 'package:cake_wallet/src/screens/nano_accounts/nano_account_list_page.dart';
import 'package:cake_wallet/src/screens/receive/widgets/address_cell.dart';
import 'package:cake_wallet/src/screens/receive/widgets/header_tile.dart';
import 'package:cake_wallet/src/widgets/section_divider.dart';
import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_account_list_header.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_header.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class AddressList extends StatelessWidget {
const AddressList({
super.key,
required this.addressListViewModel,
this.onSelect,
});
final WalletAddressListViewModel addressListViewModel;
final Function(String)? onSelect;
@override
Widget build(BuildContext context) {
bool editable = onSelect == null;
return Observer(
builder: (_) => ListView.separated(
padding: EdgeInsets.all(0),
separatorBuilder: (context, _) => const HorizontalSectionDivider(),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: addressListViewModel.items.length,
itemBuilder: (context, index) {
final item = addressListViewModel.items[index];
Widget cell = Container();
if (item is WalletAccountListHeader) {
cell = HeaderTile(
showTrailingButton: true,
walletAddressListViewModel: addressListViewModel,
trailingButtonTap: () async {
if (addressListViewModel.type == WalletType.monero ||
addressListViewModel.type == WalletType.haven) {
await showPopUp<void>(
context: context, builder: (_) => getIt.get<MoneroAccountListPage>());
} else {
await showPopUp<void>(
context: context, builder: (_) => getIt.get<NanoAccountListPage>());
}
},
title: S.of(context).accounts,
trailingIcon: Icon(
Icons.arrow_forward_ios,
size: 14,
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
));
}
if (item is WalletAddressListHeader) {
cell = HeaderTile(
title: S.of(context).addresses,
walletAddressListViewModel: addressListViewModel,
showTrailingButton: !addressListViewModel.isAutoGenerateSubaddressEnabled,
showSearchButton: true,
trailingButtonTap: () => Navigator.of(context).pushNamed(Routes.newSubaddress),
trailingIcon: Icon(
Icons.add,
size: 20,
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
));
}
if (item is WalletAddressListItem) {
cell = Observer(builder: (_) {
final isCurrent = item.address == addressListViewModel.address.address && editable;
final backgroundColor = isCurrent
? Theme.of(context).extension<ReceivePageTheme>()!.currentTileBackgroundColor
: Theme.of(context).extension<ReceivePageTheme>()!.tilesBackgroundColor;
final textColor = isCurrent
? Theme.of(context).extension<ReceivePageTheme>()!.currentTileTextColor
: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor;
return AddressCell.fromItem(
item,
isCurrent: isCurrent,
hasBalance: addressListViewModel.isElectrumWallet,
backgroundColor: backgroundColor,
textColor: textColor,
onTap: (_) {
if (onSelect != null) {
onSelect!(item.address);
return;
}
addressListViewModel.setAddress(item);
},
onEdit: editable
? () => Navigator.of(context).pushNamed(Routes.newSubaddress, arguments: item)
: null,
);
});
}
return index != 0
? cell
: ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30), topRight: Radius.circular(30)),
child: cell,
);
},
),
);
}
}

View file

@ -2,7 +2,6 @@ import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_page.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_from_keys_form.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_from_seed_form.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
@ -81,7 +80,7 @@ class WalletRestorePage extends BasePage {
});
}
static bool formProcessing = false;
bool _formProcessing = false;
@override
Widget middle(BuildContext context) => Observer(
@ -355,8 +354,8 @@ class WalletRestorePage extends BasePage {
}
Future<void> _confirmForm(BuildContext context) async {
if (formProcessing) return;
formProcessing = true;
if (_formProcessing) return;
_formProcessing = true;
try {
// Dismissing all visible keyboard to provide context for navigation
FocusManager.instance.primaryFocus?.unfocus();
@ -375,13 +374,13 @@ class WalletRestorePage extends BasePage {
}
if (!formKey!.currentState!.validate()) {
formProcessing = false;
_formProcessing = false;
return;
}
if (walletRestoreViewModel.nameExists(name)) {
showNameExistsAlert(formContext!);
formProcessing = false;
_formProcessing = false;
return;
}
@ -430,10 +429,10 @@ class WalletRestorePage extends BasePage {
await walletRestoreViewModel.create(options: _credentials());
} catch (e) {
formProcessing = false;
_formProcessing = false;
rethrow;
}
formProcessing = false;
_formProcessing = false;
}
Future<void> showNameExistsAlert(BuildContext context) {

View file

@ -219,7 +219,7 @@ class RootState extends State<Root> with WidgetsBindingObserver {
void waitForWalletInstance(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (context.mounted) {
if (mounted) {
_walletReactionDisposer = reaction(
(_) => widget.appStore.wallet,
(WalletBase? wallet) {

View file

@ -82,7 +82,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
if (initialPaymentRequest != null &&
sendViewModel.walletCurrencyName != initialPaymentRequest!.scheme.toLowerCase()) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
if (context.mounted) {
if (mounted) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {

View file

@ -12,17 +12,14 @@ import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
import 'package:cake_wallet/utils/permission_handler.dart';
import 'package:permission_handler/permission_handler.dart';
enum AddressTextFieldOption { paste, qrCode, addressBook }
enum AddressTextFieldOption { paste, qrCode, addressBook, walletAddresses }
class AddressTextField extends StatelessWidget {
AddressTextField({
required this.controller,
this.isActive = true,
this.placeholder,
this.options = const [
AddressTextFieldOption.qrCode,
AddressTextFieldOption.addressBook,
],
this.options = const [AddressTextFieldOption.qrCode, AddressTextFieldOption.addressBook],
this.onURIScanned,
this.focusNode,
this.isBorderExist = true,
@ -34,6 +31,7 @@ class AddressTextField extends StatelessWidget {
this.validator,
this.onPushPasteButton,
this.onPushAddressBookButton,
this.onPushAddressPickerButton,
this.onSelectedContact,
this.selectedCurrency,
this.addressKey,
@ -58,6 +56,7 @@ class AddressTextField extends StatelessWidget {
final FocusNode? focusNode;
final Function(BuildContext context)? onPushPasteButton;
final Function(BuildContext context)? onPushAddressBookButton;
final Function(BuildContext context)? onPushAddressPickerButton;
final Function(ContactBase contact)? onSelectedContact;
final CryptoCurrency? selectedCurrency;
final Key? addressKey;
@ -105,14 +104,15 @@ class AddressTextField extends StatelessWidget {
top: 2,
right: 0,
child: SizedBox(
width: prefixIconWidth * options.length + (spaceBetweenPrefixIcons * options.length),
width:
(prefixIconWidth * options.length) + (spaceBetweenPrefixIcons * options.length),
child: Row(
mainAxisAlignment: responsiveLayoutUtil.shouldRenderMobileUI
? MainAxisAlignment.spaceBetween
: MainAxisAlignment.end,
children: [
SizedBox(width: 5),
if (this.options.contains(AddressTextFieldOption.paste)) ...[
SizedBox(width: 5),
Container(
width: prefixIconWidth,
height: prefixIconHeight,
@ -124,8 +124,8 @@ class AddressTextField extends StatelessWidget {
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: buttonColor ??
Theme.of(context).dialogTheme.backgroundColor,
color:
buttonColor ?? Theme.of(context).dialogTheme.backgroundColor,
borderRadius: BorderRadius.all(Radius.circular(6))),
child: Image.asset(
'assets/images/paste_ios.png',
@ -135,10 +135,12 @@ class AddressTextField extends StatelessWidget {
.textFieldButtonIconColor,
)),
),
)),
),
),
],
if (this.options.contains(AddressTextFieldOption.qrCode) &&
DeviceInfo.instance.isMobile) ...[
SizedBox(width: 5),
Container(
width: prefixIconWidth,
height: prefixIconHeight,
@ -150,8 +152,8 @@ class AddressTextField extends StatelessWidget {
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: buttonColor ??
Theme.of(context).dialogTheme.backgroundColor,
color:
buttonColor ?? Theme.of(context).dialogTheme.backgroundColor,
borderRadius: BorderRadius.all(Radius.circular(6))),
child: Image.asset(
'assets/images/qr_code_icon.png',
@ -161,10 +163,11 @@ class AddressTextField extends StatelessWidget {
.textFieldButtonIconColor,
)),
),
))
] else
SizedBox(width: 5),
),
),
],
if (this.options.contains(AddressTextFieldOption.addressBook)) ...[
SizedBox(width: 5),
Container(
width: prefixIconWidth,
height: prefixIconHeight,
@ -176,8 +179,8 @@ class AddressTextField extends StatelessWidget {
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: buttonColor ??
Theme.of(context).dialogTheme.backgroundColor,
color:
buttonColor ?? Theme.of(context).dialogTheme.backgroundColor,
borderRadius: BorderRadius.all(Radius.circular(6))),
child: Image.asset(
'assets/images/open_book.png',
@ -187,8 +190,36 @@ class AddressTextField extends StatelessWidget {
.textFieldButtonIconColor,
)),
),
))
]
),
),
],
if (this.options.contains(AddressTextFieldOption.walletAddresses)) ...[
SizedBox(width: 5),
Container(
width: prefixIconWidth,
height: prefixIconHeight,
padding: EdgeInsets.only(top: 0),
child: Semantics(
label: S.of(context).address_book,
child: InkWell(
onTap: () async => _presetWalletAddressPicker(context),
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color:
buttonColor ?? Theme.of(context).dialogTheme.backgroundColor,
borderRadius: BorderRadius.all(Radius.circular(6))),
child: Image.asset(
'assets/images/open_book.png',
color: iconColor ??
Theme.of(context)
.extension<SendPageTheme>()!
.textFieldButtonIconColor,
)),
),
),
)
],
],
),
))
@ -225,6 +256,15 @@ class AddressTextField extends StatelessWidget {
}
}
Future<void> _presetWalletAddressPicker(BuildContext context) async {
final address = await Navigator.of(context).pushNamed(Routes.pickerWalletAddress);
if (address is String) {
controller?.text = address;
onPushAddressPickerButton?.call(context);
}
}
Future<void> _pasteAddress(BuildContext context) async {
final clipboard = await Clipboard.getData('text/plain');
final address = clipboard?.text ?? '';

View file

@ -17,7 +17,7 @@ class SeedLanguagePickerOption {
final List<SeedLanguagePickerOption> seedLanguages = [
SeedLanguagePickerOption('English', S.current.seed_language_english,
Image.asset('assets/images/flags/usa.png'), [SeedType.legacy, SeedType.polyseed]),
SeedLanguagePickerOption('Chinese (simplified)', S.current.seed_language_chinese,
SeedLanguagePickerOption('Chinese (Simplified)', S.current.seed_language_chinese,
Image.asset('assets/images/flags/chn.png'), [SeedType.legacy, SeedType.polyseed]),
SeedLanguagePickerOption('Chinese (Traditional)', S.current.seed_language_chinese_traditional,
Image.asset('assets/images/flags/chn.png'), [SeedType.polyseed]),

View file

@ -1549,8 +1549,14 @@ abstract class SettingsStoreBase with Store {
final macInfo = await deviceInfoPlugin.macOsInfo;
deviceName = macInfo.computerName;
} else if (Platform.isWindows) {
try {
final windowsInfo = await deviceInfoPlugin.windowsInfo;
deviceName = windowsInfo.productName;
} catch (e) {
print(e);
print('likely digitalProductId is null wait till https://github.com/fluttercommunity/plus_plugins/pull/3188 is merged');
deviceName = "Windows Device";
}
}
return deviceName;

View file

@ -32,7 +32,6 @@ import 'package:cake_wallet/view_model/dashboard/trade_list_item.dart';
import 'package:cake_wallet/view_model/dashboard/transaction_list_item.dart';
import 'package:cake_wallet/view_model/settings/sync_mode.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:cake_wallet/wownero/wownero.dart' as wow;
import 'package:cryptography/cryptography.dart';
import 'package:cw_core/balance.dart';
import 'package:cw_core/cake_hive.dart';
@ -182,7 +181,8 @@ abstract class DashboardViewModelBase with Store {
final _accountTransactions = _wallet.transactionHistory.transactions.values
.where((tx) =>
wow.wownero!.getTransactionInfoAccountId(tx) == wow.wownero!.getCurrentAccount(wallet).id)
wow.wownero!.getTransactionInfoAccountId(tx) ==
wow.wownero!.getCurrentAccount(wallet).id)
.toList();
final sortedTransactions = [..._accountTransactions];
@ -362,12 +362,15 @@ abstract class DashboardViewModelBase with Store {
if (wallet.type != WalletType.monero) return [];
final keys = monero!.getKeys(wallet);
List<String> errors = [
if (keys['privateSpendKey'] == List.generate(64, (index) => "0").join("")) "Private spend key is 0",
// leaving these commented out for now, I'll be able to fix that properly in the airgap update
// to not cause work duplication, this will do the job as well, it will be slightly less precise
// about what happened - but still enough.
// if (keys['privateSpendKey'] == List.generate(64, (index) => "0").join("")) "Private spend key is 0",
if (keys['privateViewKey'] == List.generate(64, (index) => "0").join("")) "private view key is 0",
if (keys['publicSpendKey'] == List.generate(64, (index) => "0").join("")) "public spend key is 0",
if (keys['publicViewKey'] == List.generate(64, (index) => "0").join("")) "private view key is 0",
if (wallet.seed == null) "wallet seed is null",
if (wallet.seed == "") "wallet seed is empty",
// if (keys['publicSpendKey'] == List.generate(64, (index) => "0").join("")) "public spend key is 0",
if (keys['publicViewKey'] == List.generate(64, (index) => "0").join("")) "public view key is 0",
// if (wallet.seed == null) "wallet seed is null",
// if (wallet.seed == "") "wallet seed is empty",
if (monero!.getSubaddressList(wallet).getAll(wallet)[0].address == "41d7FXjswpK1111111111111111111111111111111111111111111111111111111111111111111111111111112KhNi4")
"primary address is invalid, you won't be able to receive / spend funds",
];
@ -479,6 +482,30 @@ abstract class DashboardViewModelBase with Store {
@computed
bool get hasPowNodes => wallet.type == WalletType.nano || wallet.type == WalletType.banano;
@computed
bool get hasSignMessages {
if (wallet.isHardwareWallet) {
return false;
}
switch (wallet.type) {
case WalletType.monero:
case WalletType.litecoin:
case WalletType.bitcoin:
case WalletType.bitcoinCash:
case WalletType.ethereum:
case WalletType.polygon:
case WalletType.solana:
case WalletType.nano:
case WalletType.banano:
case WalletType.tron:
case WalletType.wownero:
return true;
case WalletType.haven:
case WalletType.none:
return false;
}
}
bool get showRepWarning {
if (wallet.type != WalletType.nano) {
return false;
@ -572,7 +599,8 @@ abstract class DashboardViewModelBase with Store {
}
if (wallet.type == WalletType.wownero) {
return wow.wownero!.getTransactionInfoAccountId(tx) == wow.wownero!.getCurrentAccount(wallet).id;
return wow.wownero!.getTransactionInfoAccountId(tx) ==
wow.wownero!.getCurrentAccount(wallet).id;
}
return true;
@ -597,8 +625,8 @@ abstract class DashboardViewModelBase with Store {
.getTransactionHistory(wallet)
.transactions
.values
.where(
(tx) => monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id)
.where((tx) =>
monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id)
.toList();
transactions.addAll(_accountTransactions.map((transaction) => TransactionListItem(
@ -610,8 +638,9 @@ abstract class DashboardViewModelBase with Store {
.getTransactionHistory(wallet)
.transactions
.values
.where(
(tx) => wow.wownero!.getTransactionInfoAccountId(tx) == wow.wownero!.getCurrentAccount(wallet).id)
.where((tx) =>
wow.wownero!.getTransactionInfoAccountId(tx) ==
wow.wownero!.getCurrentAccount(wallet).id)
.toList();
transactions.addAll(_accountTransactions.map((transaction) => TransactionListItem(

View file

@ -0,0 +1,55 @@
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:mobx/mobx.dart';
part 'sign_view_model.g.dart';
class SignViewModel = SignViewModelBase with _$SignViewModel;
abstract class SignViewModelBase with Store {
SignViewModelBase(this.wallet) : state = InitialExecutionState();
final WalletBase wallet;
@observable
ExecutionState state;
@observable
bool isSigning = true;
bool get signIncludesAddress => [
WalletType.monero,
WalletType.bitcoin,
WalletType.bitcoinCash,
WalletType.litecoin,
WalletType.haven,
].contains(wallet.type);
@action
Future<void> sign(String message, {String? address}) async {
state = IsExecutingState();
try {
final signature = await wallet.signMessage(message, address: address);
state = ExecutedSuccessfullyState(payload: signature);
} catch (e) {
state = FailureState(e.toString());
}
}
@action
Future<void> verify(String message, String signature, {String? address}) async {
state = IsExecutingState();
try {
final sig = await wallet.verifyMessage(message, signature, address: address);
if (sig) {
state = ExecutedSuccessfullyState();
} else {
state = FailureState(S.current.signature_invalid_error);
}
} catch (e) {
state = FailureState(e.toString());
}
}
}

View file

@ -142,7 +142,16 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
_bestRate = 0;
_calculateBestRate();
});
if (isElectrumWallet) {
bitcoin!.updateFeeRates(wallet);
}
}
bool get isElectrumWallet =>
wallet.type == WalletType.bitcoin ||
wallet.type == WalletType.litecoin ||
wallet.type == WalletType.bitcoinCash;
bool _useTorOnly;
final Box<Trade> trades;

View file

@ -583,6 +583,30 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
String errorMessage = error.toString();
if (walletType == WalletType.solana) {
if (errorMessage.contains('insufficient lamports')) {
double solValueNeeded = 0.0;
// Regular expression to match the number after "need". This shows the exact lamports the user needs to perform the transaction.
RegExp regExp = RegExp(r'need (\d+)');
// Find the match
Match? match = regExp.firstMatch(errorMessage);
if (match != null) {
String neededAmount = match.group(1)!;
final lamportsNeeded = int.tryParse(neededAmount);
// 5000 lamport used here is the constant for sending a transaction on solana
int lamportsPerSol = 1000000000;
solValueNeeded =
lamportsNeeded != null ? ((lamportsNeeded + 5000) / lamportsPerSol) : 0.0;
return S.current.insufficient_lamports(solValueNeeded.toString());
} else {
print("No match found.");
return S.current.insufficient_lamport_for_tx;
}
}
if (errorMessage.contains('insufficient funds for rent')) {
return S.current.insufficientFundsForRentError;
}

View file

@ -6,6 +6,7 @@ import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/haven/haven.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/nano/nano.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/solana/solana.dart';
import 'package:cake_wallet/store/app_store.dart';
@ -438,6 +439,14 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
}
if (wallet.type == WalletType.nano) {
addressList.add(WalletAddressListItem(
isPrimary: true,
name: null,
address: wallet.walletAddresses.address,
));
}
if (wallet.type == WalletType.tron) {
final primaryAddress = tron!.getAddress(wallet);

View file

@ -110,8 +110,6 @@ abstract class WalletCreationVMBase with Store {
_appStore.authenticationStore.allowed();
state = ExecutedSuccessfullyState();
} catch (e, s) {
print("@@@@@@@@");
print(s);
state = FailureState(e.toString());
}
}
@ -119,9 +117,7 @@ abstract class WalletCreationVMBase with Store {
DerivationInfo? getDefaultDerivation() {
switch (this.type) {
case WalletType.nano:
return DerivationInfo(
derivationType: DerivationType.nano,
);
return DerivationInfo(derivationType: DerivationType.nano);
case WalletType.bitcoin:
case WalletType.litecoin:
return bitcoin!.getElectrumDerivations()[DerivationType.electrum]!.first;
@ -133,9 +129,7 @@ abstract class WalletCreationVMBase with Store {
DerivationInfo? getCommonRestoreDerivation() {
switch (this.type) {
case WalletType.nano:
return DerivationInfo(
derivationType: DerivationType.nano,
);
return DerivationInfo(derivationType: DerivationType.nano);
case WalletType.bitcoin:
return DerivationInfo(
derivationType: DerivationType.bip39,

View file

@ -17,15 +17,13 @@ PODS:
- in_app_review (0.2.0):
- FlutterMacOS
- OrderedSet (5.0.0)
- package_info (0.0.1):
- FlutterMacOS
- package_info_plus (0.0.1):
- FlutterMacOS
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- ReachabilitySwift (5.0.0)
- share_plus_macos (0.0.1):
- share_plus (0.0.1):
- FlutterMacOS
- shared_preferences_foundation (0.0.1):
- Flutter
@ -46,10 +44,9 @@ DEPENDENCIES:
- flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`)
- FlutterMacOS (from `Flutter/ephemeral`)
- in_app_review (from `Flutter/ephemeral/.symlinks/plugins/in_app_review/macos`)
- package_info (from `Flutter/ephemeral/.symlinks/plugins/package_info/macos`)
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
- share_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos`)
- share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`)
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
- sp_scanner (from `Flutter/ephemeral/.symlinks/plugins/sp_scanner/macos`)
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
@ -77,14 +74,12 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral
in_app_review:
:path: Flutter/ephemeral/.symlinks/plugins/in_app_review/macos
package_info:
:path: Flutter/ephemeral/.symlinks/plugins/package_info/macos
package_info_plus:
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
path_provider_foundation:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
share_plus_macos:
:path: Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos
share_plus:
:path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos
shared_preferences_foundation:
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
sp_scanner:
@ -104,11 +99,10 @@ SPEC CHECKSUMS:
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
in_app_review: a850789fad746e89bce03d4aeee8078b45a53fd0
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
package_info: 6eba2fd8d3371dda2d85c8db6fe97488f24b74b2
package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce
package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4
share_plus: 36537c04ce0c3e3f5bd297ce4318b6d5ee5fd6cf
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
sp_scanner: 269d96e0ec3173e69156be7239b95182be3b8303
url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399

View file

@ -94,15 +94,15 @@ dependencies:
# ref: main
socks5_proxy: ^1.0.4
flutter_svg: ^2.0.9
polyseed: ^0.0.5
polyseed: ^0.0.6
nostr_tools: ^1.0.9
solana: ^0.30.1
bitcoin_base:
git:
url: https://github.com/cake-tech/bitcoin_base
ref: cake-update-v4
ref: cake-update-v5
ledger_flutter: ^1.0.1
hashlib: 1.12.0
hashlib: ^1.19.2
dev_dependencies:
flutter_test:
@ -138,6 +138,10 @@ dependency_overrides:
url: https://github.com/cake-tech/web3dart.git
ref: cake
flutter_secure_storage_platform_interface: 1.0.2
bitcoin_base:
git:
url: https://github.com/cake-tech/bitcoin_base
ref: cake-update-v5
flutter_icons:
image_path: "assets/images/app_logo.png"

View file

@ -338,6 +338,8 @@
"incoming": "الواردة",
"incorrect_seed": "النص الذي تم إدخاله غير صالح.",
"inputs": "المدخلات",
"insufficient_lamport_for_tx": "ليس لديك ما يكفي من SOL لتغطية المعاملة ورسوم المعاملات الخاصة بها. يرجى إضافة المزيد من SOL إلى محفظتك أو تقليل كمية SOL التي ترسلها.",
"insufficient_lamports": "ليس لديك ما يكفي من SOL لتغطية المعاملة ورسوم المعاملات الخاصة بها. تحتاج على الأقل ${solValueNeeded} sol. يرجى إضافة المزيد من sol إلى محفظتك أو تقليل مبلغ sol الذي ترسله",
"insufficientFundsForRentError": "ليس لديك ما يكفي من SOL لتغطية رسوم المعاملة والإيجار للحساب. يرجى إضافة المزيد من sol إلى محفظتك أو تقليل مبلغ sol الذي ترسله",
"introducing_cake_pay": "نقدم لكم Cake Pay!",
"invalid_input": "مدخل غير صالح",
@ -368,6 +370,7 @@
"max_value": "الحد الأقصى: ${value} ${currency}",
"memo": "مذكرة:",
"message": "ﺔﻟﺎﺳﺭ",
"message_verified": "تم التحقق من الرسالة بنجاح",
"methods": " ﻕﺮﻃُ",
"min_amount": "الحد الأدنى: ${value}",
"min_value": "الحد الأدنى: ${value} ${currency}",
@ -659,7 +662,13 @@
"show_keys": "اظهار السييد / المفاتيح",
"show_market_place": "إظهار السوق",
"show_seed": "عرض السييد",
"sign_message": "تسجيل رسالة",
"sign_up": "اشتراك",
"sign_verify_message": "توقيع أو التحقق من الرسالة",
"sign_verify_message_sub": "قم بتوقيع أو التحقق من رسالة باستخدام المفتاح الخاص بك",
"sign_verify_title": "تسجيل / تحقق",
"signature": "إمضاء",
"signature_invalid_error": "التوقيع غير صالح للرسالة المقدمة",
"signTransaction": " ﺔﻠﻣﺎﻌﻤﻟﺍ ﻊﻴﻗﻮﺗ",
"signup_for_card_accept_terms": "قم بالتسجيل للحصول على البطاقة وقبول الشروط.",
"silent_payments": "مدفوعات صامتة",
@ -820,6 +829,7 @@
"value_type": "نوع القيمة",
"variable_pair_not_supported": "هذا الزوج المتغير غير مدعوم في التبادلات المحددة",
"verification": "تَحَقّق",
"verify_message": "تحقق من الرسالة",
"verify_with_2fa": "تحقق مع Cake 2FA",
"version": "الإصدار ${currentVersion}",
"view_all": "مشاهدة الكل",

View file

@ -338,6 +338,8 @@
"incoming": "Входящи",
"incorrect_seed": "Въведеният текст е невалиден.",
"inputs": "Входове",
"insufficient_lamport_for_tx": "Нямате достатъчно SOL, за да покриете транзакцията и таксата му за транзакция. Моля, добавете повече SOL към портфейла си или намалете сумата на SOL, която изпращате.",
"insufficient_lamports": "Нямате достатъчно SOL, за да покриете транзакцията и таксата му за транзакция. Имате нужда от поне ${solValueNeeded} sol. Моля, добавете повече SOL към портфейла си или намалете сумата на SOL, която изпращате",
"insufficientFundsForRentError": "Нямате достатъчно SOL, за да покриете таксата за транзакцията и наемането на сметката. Моля, добавете повече SOL към портфейла си или намалете сумата на SOL, която изпращате",
"introducing_cake_pay": "Запознайте се с Cake Pay!",
"invalid_input": "Невалиден вход",
@ -368,6 +370,7 @@
"max_value": "Макс: ${value} ${currency}",
"memo": "Мемо:",
"message": "Съобщение",
"message_verified": "Съобщението беше успешно проверено",
"methods": "Методи",
"min_amount": "Мин: ${value}",
"min_value": "Мин: ${value} ${currency}",
@ -659,7 +662,13 @@
"show_keys": "Покажи seed/keys",
"show_market_place": "Покажи пазар",
"show_seed": "Покажи seed",
"sign_message": "Съобщение за подписване",
"sign_up": "Регистрация",
"sign_verify_message": "Подпишете или проверете съобщението",
"sign_verify_message_sub": "Подпишете или проверете съобщение с помощта на вашия личен ключ",
"sign_verify_title": "Подпишете / проверете",
"signature": "Подпис",
"signature_invalid_error": "Подписът не е валиден за даденото съобщение",
"signTransaction": "Подпишете транзакция",
"signup_for_card_accept_terms": "Регистрайте се за картата и приемете условията.",
"silent_payments": "Мълчаливи плащания",
@ -820,6 +829,7 @@
"value_type": "Тип стойност",
"variable_pair_not_supported": "Този variable pair не се поддържа от избраната борса",
"verification": "Потвърждаване",
"verify_message": "Проверете съобщението",
"verify_with_2fa": "Проверете с Cake 2FA",
"version": "Версия ${currentVersion}",
"view_all": "Виж всички",

View file

@ -338,6 +338,8 @@
"incoming": "Příchozí",
"incorrect_seed": "Zadaný text není správný.",
"inputs": "Vstupy",
"insufficient_lamport_for_tx": "Nemáte dostatek SOL na pokrytí transakce a jejího transakčního poplatku. Laskavě přidejte do své peněženky více solu nebo snižte množství Sol, kterou odesíláte.",
"insufficient_lamports": "Nemáte dostatek SOL na pokrytí transakce a jejího transakčního poplatku. Potřebujete alespoň ${solValueNeeded} sol. Laskavě přidejte do své peněženky více SOL nebo snižte množství Sol, kterou odesíláte",
"insufficientFundsForRentError": "Nemáte dostatek SOL na pokrytí transakčního poplatku a nájemného za účet. Laskavě přidejte do své peněženky více SOL nebo snižte množství Sol, kterou odesíláte",
"introducing_cake_pay": "Představujeme Cake Pay!",
"invalid_input": "Neplatný vstup",
@ -368,6 +370,7 @@
"max_value": "Max: ${value} ${currency}",
"memo": "Memo:",
"message": "Zpráva",
"message_verified": "Zpráva byla úspěšně ověřena",
"methods": "Metody",
"min_amount": "Min: ${value}",
"min_value": "Min: ${value} ${currency}",
@ -659,7 +662,13 @@
"show_keys": "Zobrazit seed/klíče",
"show_market_place": "Zobrazit trh",
"show_seed": "Zobrazit seed",
"sign_message": "Podepsat zprávu",
"sign_up": "Registrovat se",
"sign_verify_message": "Podepište nebo ověřte zprávu",
"sign_verify_message_sub": "Podepište nebo ověřte zprávu pomocí soukromého klíče",
"sign_verify_title": "Podepsat / ověřit",
"signature": "Podpis",
"signature_invalid_error": "Podpis není platný pro danou zprávu",
"signTransaction": "Podepsat transakci",
"signup_for_card_accept_terms": "Zaregistrujte se pro kartu a souhlaste s podmínkami.",
"silent_payments": "Tiché platby",
@ -820,6 +829,7 @@
"value_type": "Typ hodnoty",
"variable_pair_not_supported": "Tento pár s tržním kurzem není ve zvolené směnárně podporován",
"verification": "Ověření",
"verify_message": "Ověřit zprávu",
"verify_with_2fa": "Ověřte pomocí Cake 2FA",
"version": "Verze ${currentVersion}",
"view_all": "Zobrazit vše",

View file

@ -338,6 +338,8 @@
"incoming": "Eingehend",
"incorrect_seed": "Der eingegebene Text ist ungültig.",
"inputs": "Eingänge",
"insufficient_lamport_for_tx": "Sie haben nicht genug SOL, um die Transaktion und ihre Transaktionsgebühr abzudecken. Bitte fügen Sie Ihrer Brieftasche mehr Sol hinzu oder reduzieren Sie die SO -Menge, die Sie senden.",
"insufficient_lamports": "Sie haben nicht genug SOL, um die Transaktion und ihre Transaktionsgebühr abzudecken. Sie brauchen mindestens ${solValueNeeded} Sol. Bitte fügen Sie mehr Sol zu Ihrer Wallet hinzu oder reduzieren Sie den von Ihnen gesendeten Sol -Betrag",
"insufficientFundsForRentError": "Sie haben nicht genug SOL, um die Transaktionsgebühr und die Miete für das Konto zu decken. Bitte fügen Sie mehr Sol zu Ihrer Brieftasche hinzu oder reduzieren Sie den von Ihnen gesendeten Sol -Betrag",
"introducing_cake_pay": "Einführung von Cake Pay!",
"invalid_input": "Ungültige Eingabe",
@ -368,6 +370,7 @@
"max_value": "Max: ${value} ${currency}",
"memo": "Memo:",
"message": "Nachricht",
"message_verified": "Die Nachricht wurde erfolgreich überprüft",
"methods": "Methoden",
"min_amount": "Min: ${value}",
"min_value": "Min: ${value} ${currency}",
@ -660,7 +663,13 @@
"show_keys": "Seed/Schlüssel anzeigen",
"show_market_place": "Marktplatz anzeigen",
"show_seed": "Seed zeigen",
"sign_message": "Nachricht unterschreiben",
"sign_up": "Anmelden",
"sign_verify_message": "Nachricht unterschreiben oder überprüfen",
"sign_verify_message_sub": "Unterschreiben oder überprüfen Sie eine Nachricht mit Ihrem privaten Schlüssel",
"sign_verify_title": "Zeichen / überprüfen",
"signature": "Signatur",
"signature_invalid_error": "Die Signatur gilt nicht für die angegebene Nachricht",
"signTransaction": "Transaktion unterzeichnen",
"signup_for_card_accept_terms": "Melden Sie sich für die Karte an und akzeptieren Sie die Bedingungen.",
"silent_payments": "Stille Zahlungen",
@ -822,6 +831,7 @@
"value_type": "Werttyp",
"variable_pair_not_supported": "Dieses Variablenpaar wird von den ausgewählten Börsen nicht unterstützt",
"verification": "Verifizierung",
"verify_message": "Nachricht überprüfen",
"verify_with_2fa": "Verifizieren Sie mit Cake 2FA",
"version": "Version ${currentVersion}",
"view_all": "Alle anzeigen",

View file

@ -338,6 +338,8 @@
"incoming": "Incoming",
"incorrect_seed": "The text entered is not valid.",
"inputs": "Inputs",
"insufficient_lamport_for_tx": "You do not have enough SOL to cover the transaction and its transaction fee. Kindly add more SOL to your wallet or reduce the SOL amount you\\'re sending.",
"insufficient_lamports": "You do not have enough SOL to cover the transaction and its transaction fee. You need at least ${solValueNeeded} SOL. Kindly add more SOL to your wallet or reduce the SOL amount you\\'re sending",
"insufficientFundsForRentError": "You do not have enough SOL to cover the transaction fee and rent for the account. Kindly add more SOL to your wallet or reduce the SOL amount you\\'re sending",
"introducing_cake_pay": "Introducing Cake Pay!",
"invalid_input": "Invalid input",
@ -368,6 +370,7 @@
"max_value": "Max: ${value} ${currency}",
"memo": "Memo:",
"message": "Message",
"message_verified": "The message was successfully verified",
"methods": "Methods",
"min_amount": "Min: ${value}",
"min_value": "Min: ${value} ${currency}",
@ -660,7 +663,13 @@
"show_keys": "Show seed/keys",
"show_market_place": "Show Marketplace",
"show_seed": "Show seed",
"sign_message": "Sign Message",
"sign_up": "Sign Up",
"sign_verify_message": "Sign or verify message",
"sign_verify_message_sub": "Sign or verify a message using your private key",
"sign_verify_title": "Sign / Verify",
"signature": "Signature",
"signature_invalid_error": "The signature is not valid for the message given",
"signTransaction": "Sign Transaction",
"signup_for_card_accept_terms": "Sign up for the card and accept the terms.",
"silent_payments": "Silent Payments",
@ -821,6 +830,7 @@
"value_type": "Value Type",
"variable_pair_not_supported": "This variable pair is not supported with the selected exchanges",
"verification": "Verification",
"verify_message": "Verify Message",
"verify_with_2fa": "Verify with Cake 2FA",
"version": "Version ${currentVersion}",
"view_all": "View all",

View file

@ -338,6 +338,8 @@
"incoming": "Entrante",
"incorrect_seed": "El texto ingresado no es válido.",
"inputs": "Entradas",
"insufficient_lamport_for_tx": "No tiene suficiente SOL para cubrir la transacción y su tarifa de transacción. Por favor, agregue más SOL a su billetera o reduzca la cantidad de sol que está enviando.",
"insufficient_lamports": "No tiene suficiente SOL para cubrir la transacción y su tarifa de transacción. Necesita al menos ${solValueNeeded} sol. Por favor, agregue más sol a su billetera o reduzca la cantidad de sol que está enviando",
"insufficientFundsForRentError": "No tiene suficiente SOL para cubrir la tarifa de transacción y alquilar para la cuenta. Por favor, agregue más sol a su billetera o reduzca la cantidad de sol que está enviando",
"introducing_cake_pay": "¡Presentamos Cake Pay!",
"invalid_input": "Entrada inválida",
@ -368,6 +370,7 @@
"max_value": "Max: ${value} ${currency}",
"memo": "Memorándum:",
"message": "Mensaje",
"message_verified": "El mensaje fue verificado con éxito",
"methods": "Métodos",
"min_amount": "Mínimo: ${value}",
"min_value": "Min: ${value} ${currency}",
@ -660,7 +663,13 @@
"show_keys": "Mostrar semilla/claves",
"show_market_place": "Mostrar mercado",
"show_seed": "Mostrar semilla",
"sign_message": "Mensaje de firma",
"sign_up": "Registrarse",
"sign_verify_message": "Firmar o verificar el mensaje",
"sign_verify_message_sub": "Firmar o verificar un mensaje usando su clave privada",
"sign_verify_title": "Firmar / verificar",
"signature": "Firma",
"signature_invalid_error": "La firma no es válida para el mensaje dado",
"signTransaction": "Firmar transacción",
"signup_for_card_accept_terms": "Regístrese para obtener la tarjeta y acepte los términos.",
"silent_payments": "Pagos silenciosos",
@ -821,6 +830,7 @@
"value_type": "Tipo de valor",
"variable_pair_not_supported": "Este par de variables no es compatible con los intercambios seleccionados",
"verification": "Verificación",
"verify_message": "Mensaje de verificación",
"verify_with_2fa": "Verificar con Cake 2FA",
"version": "Versión ${currentVersion}",
"view_all": "Ver todo",

View file

@ -338,6 +338,8 @@
"incoming": "Entrantes",
"incorrect_seed": "Le texte entré est invalide.",
"inputs": "Contributions",
"insufficient_lamport_for_tx": "Vous n'avez pas assez de sol pour couvrir la transaction et ses frais de transaction. Veuillez ajouter plus de Sol à votre portefeuille ou réduire la quantité de Sol que vous envoyez.",
"insufficient_lamports": "Vous n'avez pas assez de sol pour couvrir la transaction et ses frais de transaction. Vous avez besoin d'au moins ${solValueNeeded} sol. Veuillez ajouter plus de Sol à votre portefeuille ou réduire la quantité de sol que vous envoyez",
"insufficientFundsForRentError": "Vous n'avez pas assez de SOL pour couvrir les frais de transaction et le loyer pour le compte. Veuillez ajouter plus de Sol à votre portefeuille ou réduire la quantité de sol que vous envoyez",
"introducing_cake_pay": "Présentation de Cake Pay !",
"invalid_input": "Entrée invalide",
@ -368,6 +370,7 @@
"max_value": "Max: ${value} ${currency}",
"memo": "Mémo :",
"message": "Message",
"message_verified": "Le message a été vérifié avec succès",
"methods": "Méthodes",
"min_amount": "Min : ${value}",
"min_value": "Min: ${value} ${currency}",
@ -659,7 +662,13 @@
"show_keys": "Visualiser la phrase secrète (seed) et les clefs",
"show_market_place": "Afficher la place de marché",
"show_seed": "Visualiser la phrase secrète (seed)",
"sign_message": "Signer le message",
"sign_up": "S'inscrire",
"sign_verify_message": "Signer ou vérifier le message",
"sign_verify_message_sub": "Signez ou vérifiez un message en utilisant votre clé privée",
"sign_verify_title": "Signe / vérifier",
"signature": "Signature",
"signature_invalid_error": "La signature n'est pas valable pour le message donné",
"signTransaction": "Signer une transaction",
"signup_for_card_accept_terms": "Inscrivez-vous pour la carte et acceptez les conditions.",
"silent_payments": "Paiements silencieux",
@ -820,6 +829,7 @@
"value_type": "Type de valeur",
"variable_pair_not_supported": "Cette paire variable n'est pas prise en charge avec les échanges sélectionnés",
"verification": "Vérification",
"verify_message": "Vérifier le message",
"verify_with_2fa": "Vérifier avec Cake 2FA",
"version": "Version ${currentVersion}",
"view_all": "Voir tout",

View file

@ -338,6 +338,8 @@
"incoming": "Mai shigowa",
"incorrect_seed": "rubutun da aka shigar ba shi da inganci.",
"inputs": "Abubuwan da ke ciki",
"insufficient_lamport_for_tx": "Ba ku da isasshen sool don rufe ma'amala da kuɗin ma'amala. Da unara ƙara ƙarin sool a cikin walat ɗinku ko rage adadin Sol ɗin da kuke aikawa.",
"insufficient_lamports": "Ba ku da isasshen sool don rufe ma'amala da kuɗin ma'amala. Kuna buƙatar aƙalla ${solValueNeeded} Sol. Da kyau ƙara ƙarin sool zuwa walat ɗinku ko rage adadin Sol ɗin da kuke aikawa",
"insufficientFundsForRentError": "Ba ku da isasshen Sol don rufe kuɗin ma'amala da haya don asusun. Da kyau ƙara ƙarin sool zuwa walat ɗinku ko rage adadin Sol ɗin da kuke aikawa",
"introducing_cake_pay": "Gabatar da Cake Pay!",
"invalid_input": "Shigar da ba daidai ba",
@ -368,6 +370,7 @@
"max_value": "Max: ${value} ${currency}",
"memo": "Memo:",
"message": "Sako",
"message_verified": "An yi nasarar tabbatar da sakon",
"methods": "Hanyoyin",
"min_amount": "Min: ${value}",
"min_value": "Min: ${value} ${currency}",
@ -661,7 +664,13 @@
"show_keys": "Nuna iri/maɓallai",
"show_market_place": "Nuna dan kasuwa",
"show_seed": "Nuna iri",
"sign_message": "Sa hannu",
"sign_up": "Shiga",
"sign_verify_message": "Shiga ko Tabbatar da Saƙo",
"sign_verify_message_sub": "Shiga ko tabbatar da saƙo ta amfani da Maɓallinku na sirri",
"sign_verify_title": "Sa hannu / Tabbatar",
"signature": "Sa hannu",
"signature_invalid_error": "Sa hannu ba shi da inganci ga sakon da aka bayar",
"signTransaction": "Sa hannu Ma'amala",
"signup_for_card_accept_terms": "Yi rajista don katin kuma karɓi sharuɗɗan.",
"silent_payments": "Biya silent",
@ -822,6 +831,7 @@
"value_type": "Nau'in darajar",
"variable_pair_not_supported": "Ba a samun goyan bayan wannan m biyu tare da zaɓaɓɓun musayar",
"verification": "tabbatar",
"verify_message": "Tabbatar saƙon",
"verify_with_2fa": "Tabbatar da Cake 2FA",
"version": "Sigar ${currentVersion}",
"view_all": "Duba duka",

View file

@ -338,6 +338,8 @@
"incoming": "आने वाली",
"incorrect_seed": "दर्ज किया गया पाठ मान्य नहीं है।",
"inputs": "इनपुट",
"insufficient_lamport_for_tx": "आपके पास लेनदेन और इसके लेनदेन शुल्क को कवर करने के लिए पर्याप्त सोल नहीं है। कृपया अपने बटुए में अधिक सोल जोड़ें या आपके द्वारा भेजे जा रहे सोल राशि को कम करें।",
"insufficient_lamports": "आपके पास लेनदेन और इसके लेनदेन शुल्क को कवर करने के लिए पर्याप्त सोल नहीं है। आपको कम से कम ${solValueNeeded} सोल की आवश्यकता है। कृपया अपने बटुए में अधिक सोल जोड़ें या सोल राशि को कम करें जिसे आप भेज रहे हैं",
"insufficientFundsForRentError": "आपके पास लेन -देन शुल्क और खाते के लिए किराए को कवर करने के लिए पर्याप्त सोल नहीं है। कृपया अपने बटुए में अधिक सोल जोड़ें या सोल राशि को कम करें जिसे आप भेज रहे हैं",
"introducing_cake_pay": "परिचय Cake Pay!",
"invalid_input": "अमान्य निवेश",
@ -368,6 +370,7 @@
"max_value": "मैक्स: ${value} ${currency}",
"memo": "ज्ञापन:",
"message": "संदेश",
"message_verified": "संदेश को सफलतापूर्वक सत्यापित किया गया था",
"methods": "तरीकों",
"min_amount": "न्यूनतम: ${value}",
"min_value": "मिन: ${value} ${currency}",
@ -661,7 +664,13 @@
"show_keys": "बीज / कुंजियाँ दिखाएँ",
"show_market_place": "बाज़ार दिखाएँ",
"show_seed": "बीज दिखाओ",
"sign_message": "हस्ताक्षर संदेश",
"sign_up": "साइन अप करें",
"sign_verify_message": "संदेश पर हस्ताक्षर या सत्यापित करें",
"sign_verify_message_sub": "अपनी निजी कुंजी का उपयोग करके किसी संदेश पर हस्ताक्षर या सत्यापित करें",
"sign_verify_title": "हस्ताक्षर / सत्यापित करें",
"signature": "हस्ताक्षर",
"signature_invalid_error": "हस्ताक्षर दिए गए संदेश के लिए मान्य नहीं है",
"signTransaction": "लेन-देन पर हस्ताक्षर करें",
"signup_for_card_accept_terms": "कार्ड के लिए साइन अप करें और शर्तें स्वीकार करें।",
"silent_payments": "मूक भुगतान",
@ -822,6 +831,7 @@
"value_type": "मान प्रकार",
"variable_pair_not_supported": "यह परिवर्तनीय जोड़ी चयनित एक्सचेंजों के साथ समर्थित नहीं है",
"verification": "सत्यापन",
"verify_message": "संदेश सत्यापित करें",
"verify_with_2fa": "केक 2FA के साथ सत्यापित करें",
"version": "संस्करण ${currentVersion}",
"view_all": "सभी देखें",

View file

@ -338,6 +338,8 @@
"incoming": "Dolazno",
"incorrect_seed": "Uneseni tekst nije valjan.",
"inputs": "Unosi",
"insufficient_lamport_for_tx": "Nemate dovoljno SOL -a da pokriva transakciju i njegovu transakcijsku naknadu. Ljubazno dodajte više sol u svoj novčanik ili smanjite količinu SOL -a koju šaljete.",
"insufficient_lamports": "Nemate dovoljno SOL -a da pokriva transakciju i njegovu transakcijsku naknadu. Trebate najmanje ${solValueNeeded} sol. Ljubazno dodajte više sol u svoj novčanik ili smanjite količinu SOL -a koju šaljete",
"insufficientFundsForRentError": "Nemate dovoljno SOL -a za pokrivanje naknade za transakciju i najamninu za račun. Ljubazno dodajte više sol u svoj novčanik ili smanjite količinu SOL -a koju šaljete",
"introducing_cake_pay": "Predstavljamo Cake Pay!",
"invalid_input": "Pogrešan unos",
@ -368,6 +370,7 @@
"max_value": "Maks.: ${value} ${currency}",
"memo": "Memo:",
"message": "Poruka",
"message_verified": "Poruka je uspješno provjerena",
"methods": "Metode",
"min_amount": "Minimalno: ${value}",
"min_value": "Min.: ${value} ${currency}",
@ -659,7 +662,13 @@
"show_keys": "Prikaži pristupni izraz/ključ",
"show_market_place": "Prikaži tržište",
"show_seed": "Prikaži pristupni izraz",
"sign_message": "Poruka",
"sign_up": "Prijavite se",
"sign_verify_message": "Potpisati ili provjeriti poruku",
"sign_verify_message_sub": "Potpišite ili provjerite poruku pomoću privatnog ključa",
"sign_verify_title": "Potpisati / provjeriti",
"signature": "Potpis",
"signature_invalid_error": "Potpis ne vrijedi za danu poruku",
"signTransaction": "Potpišite transakciju",
"signup_for_card_accept_terms": "Prijavite se za karticu i prihvatite uvjete.",
"silent_payments": "Tiha plaćanja",
@ -820,6 +829,7 @@
"value_type": "Tipa vrijednosti",
"variable_pair_not_supported": "Ovaj par varijabli nije podržan s odabranim burzama",
"verification": "Potvrda",
"verify_message": "Provjerite poruku",
"verify_with_2fa": "Provjerite s Cake 2FA",
"version": "Verzija ${currentVersion}",
"view_all": "Prikaži sve",

View file

@ -338,6 +338,8 @@
"incoming": "Masuk",
"incorrect_seed": "Teks yang dimasukkan tidak valid.",
"inputs": "Input",
"insufficient_lamport_for_tx": "Anda tidak memiliki cukup SOL untuk menutupi transaksi dan biaya transaksinya. Mohon tambahkan lebih banyak sol ke dompet Anda atau kurangi jumlah sol yang Anda kirim.",
"insufficient_lamports": "Anda tidak memiliki cukup SOL untuk menutupi transaksi dan biaya transaksinya. Anda membutuhkan setidaknya ${solValueNeeded} sol. Mohon tambahkan lebih banyak sol ke dompet Anda atau kurangi jumlah sol yang Anda kirim",
"insufficientFundsForRentError": "Anda tidak memiliki cukup SOL untuk menutupi biaya transaksi dan menyewa untuk akun tersebut. Mohon tambahkan lebih banyak sol ke dompet Anda atau kurangi jumlah sol yang Anda kirim",
"introducing_cake_pay": "Perkenalkan Cake Pay!",
"invalid_input": "Masukan tidak valid",
@ -368,6 +370,7 @@
"max_value": "Max: ${value} ${currency}",
"memo": "Memo:",
"message": "Pesan",
"message_verified": "Pesan itu berhasil diverifikasi",
"methods": "Metode",
"min_amount": "Min: ${value}",
"min_value": "Min: ${value} ${currency}",
@ -662,7 +665,13 @@
"show_keys": "Tampilkan seed/kunci",
"show_market_place": "Tampilkan Pasar",
"show_seed": "Tampilkan seed",
"sign_message": "Pesan tanda",
"sign_up": "Daftar",
"sign_verify_message": "Tanda tangan atau verifikasi pesan",
"sign_verify_message_sub": "Menandatangani atau memverifikasi pesan menggunakan kunci pribadi Anda",
"sign_verify_title": "Tanda / verifikasi",
"signature": "Tanda tangan",
"signature_invalid_error": "Tanda tangan tidak valid untuk pesan yang diberikan",
"signTransaction": "Tandatangani Transaksi",
"signup_for_card_accept_terms": "Daftar untuk kartu dan terima syarat dan ketentuan.",
"silent_payments": "Pembayaran diam",
@ -823,6 +832,7 @@
"value_type": "Jenis Nilai",
"variable_pair_not_supported": "Pasangan variabel ini tidak didukung dengan bursa yang dipilih",
"verification": "Verifikasi",
"verify_message": "Verifikasi pesan",
"verify_with_2fa": "Verifikasi dengan Cake 2FA",
"version": "Versi ${currentVersion}",
"view_all": "Lihat Semua",

View file

@ -339,6 +339,8 @@
"incoming": "In arrivo",
"incorrect_seed": "Il testo inserito non è valido.",
"inputs": "Input",
"insufficient_lamport_for_tx": "Non hai abbastanza SOL per coprire la transazione e la sua quota di transazione. Si prega di aggiungere più SOL al tuo portafoglio o ridurre l'importo SOL che stai inviando.",
"insufficient_lamports": "Non hai abbastanza SOL per coprire la transazione e la sua quota di transazione. Hai bisogno di almeno ${solValueNeeded} sol. Si prega di aggiungere più SOL al tuo portafoglio o ridurre l'importo SOL che stai inviando",
"insufficientFundsForRentError": "Non hai abbastanza SOL per coprire la tassa di transazione e l'affitto per il conto. Si prega di aggiungere più SOL al tuo portafoglio o ridurre l'importo SOL che stai inviando",
"introducing_cake_pay": "Presentazione di Cake Pay!",
"invalid_input": "Inserimento non valido",
@ -369,6 +371,7 @@
"max_value": "Max: ${value} ${currency}",
"memo": "Memo:",
"message": "Messaggio",
"message_verified": "Il messaggio è stato verificato con successo",
"methods": "Metodi",
"min_amount": "Min: ${value}",
"min_value": "Min: ${value} ${currency}",
@ -661,7 +664,13 @@
"show_keys": "Mostra seme/chiavi",
"show_market_place": "Mostra mercato",
"show_seed": "Mostra seme",
"sign_message": "Messaggio di firma",
"sign_up": "Registrati",
"sign_verify_message": "Firmare o verificare il messaggio",
"sign_verify_message_sub": "Firma o verifica un messaggio utilizzando la chiave privata",
"sign_verify_title": "Firmare / verificare",
"signature": "Firma",
"signature_invalid_error": "La firma non è valida per il messaggio dato",
"signTransaction": "Firma la transazione",
"signup_for_card_accept_terms": "Registrati per la carta e accetta i termini.",
"silent_payments": "Pagamenti silenziosi",
@ -822,6 +831,7 @@
"value_type": "Tipo di valore",
"variable_pair_not_supported": "Questa coppia di variabili non è supportata con gli scambi selezionati",
"verification": "Verifica",
"verify_message": "Verificare il messaggio",
"verify_with_2fa": "Verifica con Cake 2FA",
"version": "Versione ${currentVersion}",
"view_all": "Visualizza tutto",

View file

@ -339,6 +339,8 @@
"incoming": "着信",
"incorrect_seed": "入力されたテキストは無効です。",
"inputs": "入力",
"insufficient_lamport_for_tx": "トランザクションとその取引手数料をカバーするのに十分なSOLがありません。財布にソルを追加するか、送信するソル量を減らしてください。",
"insufficient_lamports": "トランザクションとその取引手数料をカバーするのに十分なSOLがありません。少なくとも${solValueNeeded} solが必要です。財布にソルを追加するか、送信するソル量を減らしてください",
"insufficientFundsForRentError": "アカウントの取引料金とレンタルをカバーするのに十分なソルがありません。財布にソルを追加するか、送信するソル量を減らしてください",
"introducing_cake_pay": "序章Cake Pay",
"invalid_input": "無効入力",
@ -369,6 +371,7 @@
"max_value": "マックス: ${value} ${currency}",
"memo": "メモ:",
"message": "メッセージ",
"message_verified": "メッセージは正常に検証されました",
"methods": "メソッド",
"min_amount": "最小: ${value}",
"min_value": "分: ${value} ${currency}",
@ -660,7 +663,13 @@
"show_keys": "シード/キーを表示する",
"show_market_place": "マーケットプレイスを表示",
"show_seed": "シードを表示",
"sign_message": "署名メッセージ",
"sign_up": "サインアップ",
"sign_verify_message": "メッセージに署名または確認します",
"sign_verify_message_sub": "秘密鍵を使用してメッセージに署名または確認します",
"sign_verify_title": "署名 /検証",
"signature": "サイン",
"signature_invalid_error": "署名は、指定されたメッセージに対して無効です",
"signTransaction": "トランザクションに署名する",
"signup_for_card_accept_terms": "カードにサインアップして、利用規約に同意してください。",
"silent_payments": "サイレント支払い",
@ -821,6 +830,7 @@
"value_type": "値タイプ",
"variable_pair_not_supported": "この変数ペアは、選択した取引所ではサポートされていません",
"verification": "検証",
"verify_message": "メッセージを確認します",
"verify_with_2fa": "Cake 2FA で検証する",
"version": "バージョン ${currentVersion}",
"view_all": "すべて表示",

View file

@ -338,6 +338,8 @@
"incoming": "들어오는",
"incorrect_seed": "입력하신 텍스트가 유효하지 않습니다.",
"inputs": "입력",
"insufficient_lamport_for_tx": "거래 및 거래 수수료를 충당하기에 충분한 SOL이 없습니다. 지갑에 더 많은 솔을 추가하거나 보내는 솔을 줄입니다.",
"insufficient_lamports": "거래 및 거래 수수료를 충당하기에 충분한 SOL이 없습니다. 최소 ${solValueNeeded} sol이 필요합니다. 지갑에 더 많은 솔을 추가하거나 보내는 솔을 줄이십시오.",
"insufficientFundsForRentError": "거래 수수료와 계좌 임대료를 충당하기에 충분한 SOL이 없습니다. 지갑에 더 많은 솔을 추가하거나 보내는 솔을 줄이십시오.",
"introducing_cake_pay": "소개 Cake Pay!",
"invalid_input": "잘못된 입력",
@ -368,6 +370,7 @@
"max_value": "맥스: ${value} ${currency}",
"memo": "메모:",
"message": "메시지",
"message_verified": "메시지가 성공적으로 확인되었습니다",
"methods": "행동 양식",
"min_amount": "최소: ${value}",
"min_value": "최소: ${value} ${currency}",
@ -660,7 +663,13 @@
"show_keys": "시드 / 키 표시",
"show_market_place": "마켓플레이스 표시",
"show_seed": "종자 표시",
"sign_message": "서명 메시지",
"sign_up": "가입",
"sign_verify_message": "메시지에 서명하거나 확인하십시오",
"sign_verify_message_sub": "개인 키를 사용하여 메시지에 서명하거나 확인하십시오",
"sign_verify_title": "서명 / 확인",
"signature": "서명",
"signature_invalid_error": "서명은 주어진 메시지에 유효하지 않습니다",
"signTransaction": "거래 서명",
"signup_for_card_accept_terms": "카드에 가입하고 약관에 동의합니다.",
"silent_payments": "조용한 지불",
@ -821,6 +830,7 @@
"value_type": "가치 유형",
"variable_pair_not_supported": "이 변수 쌍은 선택한 교환에서 지원되지 않습니다.",
"verification": "검증",
"verify_message": "메시지를 확인하십시오",
"verify_with_2fa": "케이크 2FA로 확인",
"version": "버전 ${currentVersion}",
"view_all": "모두 보기",

View file

@ -338,6 +338,8 @@
"incoming": "ဝင်လာ",
"incorrect_seed": "ထည့်သွင်းထားသော စာသားသည် မမှန်ကန်ပါ။",
"inputs": "သွင်းငေှ",
"insufficient_lamport_for_tx": "သငျသညျငွေပေးငွေယူနှင့်၎င်း၏ငွေပေးငွေယူကြေးကိုဖုံးလွှမ်းရန် sol ရှိသည်မဟုတ်ကြဘူး။ ကြင်နာစွာသင်၏ပိုက်ဆံအိတ်သို့ပိုမို sol ကိုထပ်ထည့်ပါသို့မဟုတ်သင်ပို့လွှတ်ခြင်း sol ပမာဏကိုလျှော့ချပါ။",
"insufficient_lamports": "သငျသညျငွေပေးငွေယူနှင့်၎င်း၏ငွေပေးငွေယူကြေးကိုဖုံးလွှမ်းရန် sol ရှိသည်မဟုတ်ကြဘူး။ သင်အနည်းဆုံး ${solValueNeeded} s ကိုလိုအပ်ပါတယ်။ ကြင်နာစွာသင်၏ပိုက်ဆံအိတ်သို့ပိုမို sol ကိုထပ်ထည့်ပါသို့မဟုတ်သင်ပို့နေသော sol ပမာဏကိုလျှော့ချပါ",
"insufficientFundsForRentError": "သင်ငွေပေးချေမှုအခကြေးငွေကိုဖုံးအုပ်ရန်နှင့်အကောင့်ငှားရန်လုံလောက်သော sol ရှိသည်မဟုတ်ကြဘူး။ ကြင်နာစွာသင်၏ပိုက်ဆံအိတ်သို့ပိုမို sol ကိုပိုမိုထည့်ပါသို့မဟုတ်သင်ပို့ခြင်း sol ပမာဏကိုလျှော့ချပါ",
"introducing_cake_pay": "Cake Pay ကို မိတ်ဆက်ခြင်း။",
"invalid_input": "ထည့်သွင်းမှု မမှန်ကန်ပါ။",
@ -368,6 +370,7 @@
"max_value": "အများဆုံး- ${value} ${currency}",
"memo": "မှတ်စုတို:",
"message": "မက်ဆေ့ချ်",
"message_verified": "မက်ဆေ့ခ်ျကိုအောင်မြင်စွာအတည်ပြုခဲ့သည်",
"methods": "နည်းလမ်းများ",
"min_amount": "အနည်းဆုံး- ${value}",
"min_value": "အနည်းဆုံး- ${value} ${currency}",
@ -659,7 +662,13 @@
"show_keys": "မျိုးစေ့ /သော့များကို ပြပါ။",
"show_market_place": "စျေးကွက်ကိုပြသပါ။",
"show_seed": "မျိုးစေ့ကိုပြပါ။",
"sign_message": "လက်မှတ်စာ",
"sign_up": "ဆိုင်းအပ်",
"sign_verify_message": "မက်ဆေ့ခ်ျကိုလက်မှတ်ထိုးသို့မဟုတ်အတည်ပြုရန်",
"sign_verify_message_sub": "သင်၏ကိုယ်ပိုင်သော့ကို သုံး. မက်ဆေ့ခ်ျကိုလက်မှတ်ထိုးပါ",
"sign_verify_title": "လက်မှတ်ထိုး / အတည်ပြုရန်",
"signature": "လက်မှတ်",
"signature_invalid_error": "အဆိုပါလက်မှတ်ပေးထားသောမက်ဆေ့ခ်ျကိုများအတွက်မမှန်ကန်ပါ",
"signTransaction": "ငွေလွှဲဝင်ပါ။",
"signup_for_card_accept_terms": "ကတ်အတွက် စာရင်းသွင်းပြီး စည်းကမ်းချက်များကို လက်ခံပါ။",
"silent_payments": "အသံတိတ်ငွေပေးချေမှု",
@ -820,6 +829,7 @@
"value_type": "Value အမျိုးအစား",
"variable_pair_not_supported": "ရွေးချယ်ထားသော ဖလှယ်မှုများဖြင့် ဤပြောင်းလဲနိုင်သောအတွဲကို ပံ့ပိုးမထားပါ။",
"verification": "စိစစ်ခြင်း။",
"verify_message": "မက်ဆေ့ခ်ျကိုအတည်ပြုရန်",
"verify_with_2fa": "Cake 2FA ဖြင့် စစ်ဆေးပါ။",
"version": "ဗားရှင်း ${currentVersion}",
"view_all": "အားလုံးကိုကြည့်ရှုပါ။",

View file

@ -338,6 +338,8 @@
"incoming": "inkomend",
"incorrect_seed": "De ingevoerde tekst is niet geldig.",
"inputs": "Invoer",
"insufficient_lamport_for_tx": "U hebt niet genoeg SOL om de transactie en de transactiekosten te dekken. Voeg vriendelijk meer SOL toe aan uw portemonnee of verminder de SOL -hoeveelheid die u verzendt.",
"insufficient_lamports": "U hebt niet genoeg SOL om de transactie en de transactiekosten te dekken. Je hebt minstens ${solValueNeeded} sol nodig. Voeg vriendelijk meer Sol toe aan uw portemonnee of verminder de SOL -hoeveelheid die u verzendt",
"insufficientFundsForRentError": "U hebt niet genoeg SOL om de transactiekosten en huur voor de rekening te dekken. Voeg vriendelijk meer SOL toe aan uw portemonnee of verminder de SOL -hoeveelheid die u verzendt",
"introducing_cake_pay": "Introductie van Cake Pay!",
"invalid_input": "Ongeldige invoer",
@ -368,6 +370,7 @@
"max_value": "Max: ${value} ${currency}",
"memo": "Memo:",
"message": "Bericht",
"message_verified": "Het bericht is succesvol geverifieerd",
"methods": "Methoden",
"min_amount": "Min: ${value}",
"min_value": "Min: ${value} ${currency}",
@ -659,7 +662,13 @@
"show_keys": "Toon zaad/sleutels",
"show_market_place": "Toon Marktplaats",
"show_seed": "Toon zaad",
"sign_message": "Aanmeldingsbericht",
"sign_up": "Aanmelden",
"sign_verify_message": "Teken of verifieer bericht",
"sign_verify_message_sub": "Teken of verifieer een bericht met uw privésleutel",
"sign_verify_title": "Ondertekenen / verifiëren",
"signature": "Handtekening",
"signature_invalid_error": "De handtekening is niet geldig voor het gegeven bericht",
"signTransaction": "Transactie ondertekenen",
"signup_for_card_accept_terms": "Meld je aan voor de kaart en accepteer de voorwaarden.",
"silent_payments": "Stille betalingen",
@ -820,6 +829,7 @@
"value_type": "Waarde type",
"variable_pair_not_supported": "Dit variabelenpaar wordt niet ondersteund met de geselecteerde uitwisselingen",
"verification": "Verificatie",
"verify_message": "Verifieer bericht",
"verify_with_2fa": "Controleer met Cake 2FA",
"version": "Versie ${currentVersion}",
"view_all": "Alles bekijken",

View file

@ -338,6 +338,8 @@
"incoming": "Przychodzące",
"incorrect_seed": "Wprowadzony seed jest nieprawidłowy.",
"inputs": "Wejścia",
"insufficient_lamport_for_tx": "Nie masz wystarczającej ilości SOL, aby pokryć transakcję i opłatę za transakcję. Uprzejmie dodaj więcej sol do portfela lub zmniejsz wysyłaną kwotę SOL.",
"insufficient_lamports": "Nie masz wystarczającej ilości SOL, aby pokryć transakcję i opłatę za transakcję. Potrzebujesz przynajmniej ${solValueNeeded} sol. Uprzejmie dodaj więcej sol do portfela lub zmniejsz wysyłaną kwotę SOL, którą wysyłasz",
"insufficientFundsForRentError": "Nie masz wystarczającej ilości SOL, aby pokryć opłatę za transakcję i czynsz za konto. Uprzejmie dodaj więcej sol do portfela lub zmniejsz solę, którą wysyłasz",
"introducing_cake_pay": "Przedstawiamy Cake Pay!",
"invalid_input": "Nieprawidłowe dane wejściowe",
@ -368,6 +370,7 @@
"max_value": "Max: ${value} ${currency}",
"memo": "Notatka:",
"message": "Wiadomość",
"message_verified": "Wiadomość została pomyślnie zweryfikowana",
"methods": "Metody",
"min_amount": "Min: ${value}",
"min_value": "Min: ${value} ${currency}",
@ -659,7 +662,13 @@
"show_keys": "Pokaż seed/klucze",
"show_market_place": "Pokaż rynek",
"show_seed": "Pokaż frazy seed",
"sign_message": "Podpisuj wiadomość",
"sign_up": "Zarejestruj się",
"sign_verify_message": "Podpisz lub zweryfikuj wiadomość",
"sign_verify_message_sub": "Podpisz lub zweryfikuj wiadomość za pomocą klucza prywatnego",
"sign_verify_title": "Podpisać / weryfikować",
"signature": "Podpis",
"signature_invalid_error": "Podpis nie jest ważny dla podanej wiadomości",
"signTransaction": "Podpisz transakcję",
"signup_for_card_accept_terms": "Zarejestruj się, aby otrzymać kartę i zaakceptuj warunki.",
"silent_payments": "Ciche płatności",
@ -820,6 +829,7 @@
"value_type": "Typ wartości",
"variable_pair_not_supported": "Ta para zmiennych nie jest obsługiwana na wybranych giełdach",
"verification": "Weryfikacja",
"verify_message": "Sprawdź wiadomość",
"verify_with_2fa": "Sprawdź za pomocą Cake 2FA",
"version": "Wersja ${currentVersion}",
"view_all": "Wyświetl wszystko",

View file

@ -338,6 +338,8 @@
"incoming": "Recebidas",
"incorrect_seed": "O texto digitado não é válido.",
"inputs": "Entradas",
"insufficient_lamport_for_tx": "Você não tem Sol suficiente para cobrir a transação e sua taxa de transação. Por favor, adicione mais sol à sua carteira ou reduza a quantidade de sol que você envia.",
"insufficient_lamports": "Você não tem Sol suficiente para cobrir a transação e sua taxa de transação. Você precisa de pelo menos ${solValueNeeded} sol. Por favor, adicione mais sol à sua carteira ou reduza a quantidade de sol que você está enviando",
"insufficientFundsForRentError": "Você não tem Sol suficiente para cobrir a taxa de transação e o aluguel da conta. Por favor, adicione mais sol à sua carteira ou reduza a quantidade de sol que você envia",
"introducing_cake_pay": "Apresentando o Cake Pay!",
"invalid_input": "Entrada inválida",
@ -369,6 +371,7 @@
"max_value": "Máx: ${value} ${currency}",
"memo": "Memorando:",
"message": "Mensagem",
"message_verified": "A mensagem foi verificada com sucesso",
"methods": "Métodos",
"min_amount": "Mínimo: ${valor}",
"min_value": "Mín: ${value} ${currency}",
@ -661,7 +664,13 @@
"show_keys": "Mostrar semente/chaves",
"show_market_place": "Mostrar mercado",
"show_seed": "Mostrar semente",
"sign_message": "Mensagem de assinar",
"sign_up": "Inscrever-se",
"sign_verify_message": "Assinar ou verificar mensagem",
"sign_verify_message_sub": "Assine ou verifique uma mensagem usando sua chave privada",
"sign_verify_title": "Assinar / verificar",
"signature": "Assinatura",
"signature_invalid_error": "A assinatura não é válida para a mensagem dada",
"signTransaction": "Assinar transação",
"signup_for_card_accept_terms": "Cadastre-se no cartão e aceite os termos.",
"silent_payments": "Pagamentos silenciosos",
@ -822,6 +831,7 @@
"value_type": "Tipo de valor",
"variable_pair_not_supported": "Este par de variáveis não é compatível com as trocas selecionadas",
"verification": "Verificação",
"verify_message": "Verifique a mensagem",
"verify_with_2fa": "Verificar com Cake 2FA",
"version": "Versão ${currentVersion}",
"view_all": "Ver todos",

View file

@ -338,6 +338,8 @@
"incoming": "Входящие",
"incorrect_seed": "Введённый текст некорректный.",
"inputs": "Входы",
"insufficient_lamport_for_tx": "У вас недостаточно Sol, чтобы покрыть транзакцию и плату за транзакцию. Пожалуйста, добавьте больше Sol в свой кошелек или уменьшите сумму Sol, которую вы отправляете.",
"insufficient_lamports": "У вас недостаточно Sol, чтобы покрыть транзакцию и плату за транзакцию. Вам нужен как минимум ${solValueNeeded} sol. Пожалуйста, добавьте больше Sol в свой кошелек или уменьшите сумму Sol, которую вы отправляете",
"insufficientFundsForRentError": "У вас недостаточно Sol, чтобы покрыть плату за транзакцию и аренду для счета. Пожалуйста, добавьте больше Sol в свой кошелек или уменьшите сумму Sol, которую вы отправляете",
"introducing_cake_pay": "Представляем Cake Pay!",
"invalid_input": "Неверный Ввод",
@ -368,6 +370,7 @@
"max_value": "Макс: ${value} ${currency}",
"memo": "Памятка:",
"message": "Сообщение",
"message_verified": "Сообщение было успешно проверено",
"methods": "Методы",
"min_amount": "Минимум: ${value}",
"min_value": "Мин: ${value} ${currency}",
@ -660,7 +663,13 @@
"show_keys": "Показать мнемоническую фразу/ключи",
"show_market_place": "Показать торговую площадку",
"show_seed": "Показать мнемоническую фразу",
"sign_message": "Сообщение о знаке",
"sign_up": "Зарегистрироваться",
"sign_verify_message": "Подписать или проверить сообщение",
"sign_verify_message_sub": "Подписать или проверить сообщение, используя свой закрытый ключ",
"sign_verify_title": "Знак / проверка",
"signature": "Подпись",
"signature_invalid_error": "Подпись недопустима для данного сообщения",
"signTransaction": "Подписать транзакцию",
"signup_for_card_accept_terms": "Подпишитесь на карту и примите условия.",
"silent_payments": "Молчаливые платежи",
@ -821,6 +830,7 @@
"value_type": "Тип значения",
"variable_pair_not_supported": "Эта пара переменных не поддерживается выбранными биржами.",
"verification": "Проверка",
"verify_message": "Проверьте сообщение",
"verify_with_2fa": "Подтвердить с помощью Cake 2FA",
"version": "Версия ${currentVersion}",
"view_all": "Просмотреть все",

View file

@ -338,6 +338,8 @@
"incoming": "ขาเข้า",
"incorrect_seed": "ข้อความที่ป้อนไม่ถูกต้อง",
"inputs": "อินพุต",
"insufficient_lamport_for_tx": "คุณไม่มีโซลเพียงพอที่จะครอบคลุมการทำธุรกรรมและค่าธรรมเนียมการทำธุรกรรม กรุณาเพิ่มโซลให้มากขึ้นลงในกระเป๋าเงินของคุณหรือลดจำนวนโซลที่คุณส่งมา",
"insufficient_lamports": "คุณไม่มีโซลเพียงพอที่จะครอบคลุมการทำธุรกรรมและค่าธรรมเนียมการทำธุรกรรม คุณต้องการอย่างน้อย ${solValueNeeded} SOL กรุณาเพิ่มโซลให้มากขึ้นลงในกระเป๋าเงินของคุณหรือลดจำนวนโซลที่คุณกำลังส่ง",
"insufficientFundsForRentError": "คุณไม่มีโซลเพียงพอที่จะครอบคลุมค่าธรรมเนียมการทำธุรกรรมและค่าเช่าสำหรับบัญชี กรุณาเพิ่มโซลให้มากขึ้นลงในกระเป๋าเงินของคุณหรือลดจำนวนโซลที่คุณส่งมา",
"introducing_cake_pay": "ยินดีต้อนรับสู่ Cake Pay!",
"invalid_input": "อินพุตไม่ถูกต้อง",
@ -368,6 +370,7 @@
"max_value": "ขั้นสูง: ${value} ${currency}",
"memo": "หมายเหตุ:",
"message": "ข้อความ",
"message_verified": "ข้อความได้รับการตรวจสอบอย่างประสบความสำเร็จ",
"methods": "วิธีการ",
"min_amount": "จำนวนขั้นต่ำ: ${value}",
"min_value": "ขั้นต่ำ: ${value} ${currency}",
@ -659,7 +662,13 @@
"show_keys": "แสดงซีด/คีย์",
"show_market_place": "แสดงตลาดกลาง",
"show_seed": "แสดงซีด",
"sign_message": "ลงนามข้อความ",
"sign_up": "สมัครสมาชิก",
"sign_verify_message": "ลงชื่อเข้าใช้หรือตรวจสอบข้อความ",
"sign_verify_message_sub": "ลงชื่อเข้าใช้หรือตรวจสอบข้อความโดยใช้คีย์ส่วนตัวของคุณ",
"sign_verify_title": "ลงชื่อ / ตรวจสอบ",
"signature": "ลายเซ็น",
"signature_invalid_error": "ลายเซ็นไม่ถูกต้องสำหรับข้อความที่ให้ไว้",
"signTransaction": "ลงนามในการทำธุรกรรม",
"signup_for_card_accept_terms": "ลงทะเบียนสำหรับบัตรและยอมรับเงื่อนไข",
"silent_payments": "การชำระเงินเงียบ",
@ -820,6 +829,7 @@
"value_type": "ประเภทค่า",
"variable_pair_not_supported": "คู่ความสัมพันธ์ที่เปลี่ยนแปลงได้นี้ไม่สนับสนุนกับหุ้นที่เลือก",
"verification": "การตรวจสอบ",
"verify_message": "ยืนยันข้อความ",
"verify_with_2fa": "ตรวจสอบกับ Cake 2FA",
"version": "เวอร์ชัน ${currentVersion}",
"view_all": "ดูทั้งหมด",

File diff suppressed because it is too large Load diff

View file

@ -338,6 +338,8 @@
"incoming": "Gelen",
"incorrect_seed": "Girilen metin geçerli değil.",
"inputs": "Girişler",
"insufficient_lamport_for_tx": "İşlemi ve işlem ücretini karşılamak için yeterli SOL'unuz yok. Lütfen cüzdanınıza daha fazla SOL ekleyin veya gönderdiğiniz sol miktarını azaltın.",
"insufficient_lamports": "İşlemi ve işlem ücretini karşılamak için yeterli SOL'unuz yok. En az ${solValueNeeded} Sol'a ihtiyacınız var. Lütfen cüzdanınıza daha fazla sol ekleyin veya gönderdiğiniz sol miktarını azaltın",
"insufficientFundsForRentError": "İşlem ücretini karşılamak ve hesap için kiralamak için yeterli SOL'nuz yok. Lütfen cüzdanınıza daha fazla sol ekleyin veya gönderdiğiniz sol miktarını azaltın",
"introducing_cake_pay": "Cake Pay ile tanışın!",
"invalid_input": "Geçersiz Giriş",
@ -368,6 +370,7 @@
"max_value": "En fazla: ${value} ${currency}",
"memo": "Memo:",
"message": "İleti",
"message_verified": "Mesaj başarıyla doğrulandı",
"methods": "Yöntemler",
"min_amount": "Min: ${value}",
"min_value": "En az: ${value} ${currency}",
@ -659,7 +662,13 @@
"show_keys": "Tohumları/anahtarları göster",
"show_market_place": "Pazar Yerini Göster",
"show_seed": "Tohumları göster",
"sign_message": "İşaret mesajı",
"sign_up": "Kaydol",
"sign_verify_message": "Mesajı işaretleyin veya doğrulayın",
"sign_verify_message_sub": "Özel anahtarınızı kullanarak bir mesajı imzalayın veya doğrulayın",
"sign_verify_title": "İşaretle / Doğrula",
"signature": "İmza",
"signature_invalid_error": "İmza verilen mesaj için geçerli değil",
"signTransaction": "İşlem İmzala",
"signup_for_card_accept_terms": "Kart için kaydol ve koşulları kabul et.",
"silent_payments": "Sessiz ödemeler",
@ -820,6 +829,7 @@
"value_type": "Değer türü",
"variable_pair_not_supported": "Bu değişken paritesi seçilen borsalarda desteklenmemekte",
"verification": "Doğrulama",
"verify_message": "Mesajı Doğrula",
"verify_with_2fa": "Cake 2FA ile Doğrulayın",
"version": "Sürüm ${currentVersion}",
"view_all": "Hepsini göster",

View file

@ -338,6 +338,8 @@
"incoming": "Вхідні",
"incorrect_seed": "Введений текст невірний.",
"inputs": "Вхoди",
"insufficient_lamport_for_tx": "У вас недостатньо SOL, щоб покрити транзакцію та її плату за трансакцію. Будь ласка, додайте до свого гаманця більше SOL або зменшіть суму, яку ви надсилаєте.",
"insufficient_lamports": "У вас недостатньо SOL, щоб покрити транзакцію та її плату за трансакцію. Вам потрібно щонайменше ${solValueNeeded} sol. Будь ласка, додайте до свого гаманця більше SOL або зменшіть суму Sol, яку ви надсилаєте",
"insufficientFundsForRentError": "У вас недостатньо SOL, щоб покрити плату за транзакцію та оренду на рахунок. Будь ласка, додайте до свого гаманця більше SOL або зменшіть суму, яку ви надсилаєте",
"introducing_cake_pay": "Представляємо Cake Pay!",
"invalid_input": "Неправильні дані",
@ -368,6 +370,7 @@
"max_value": "Макс: ${value} ${currency}",
"memo": "Пам’ятка:",
"message": "повідомлення",
"message_verified": "Повідомлення було успішно перевірено",
"methods": "методи",
"min_amount": "Мінімум: ${value}",
"min_value": "Мін: ${value} ${currency}",
@ -660,7 +663,13 @@
"show_keys": "Показати мнемонічну фразу/ключі",
"show_market_place": "Відображати маркетплейс",
"show_seed": "Показати мнемонічну фразу",
"sign_message": "Підпишіть повідомлення",
"sign_up": "Зареєструватися",
"sign_verify_message": "Підпишіть або перевірити повідомлення",
"sign_verify_message_sub": "Підпишіть або перевірте повідомлення за допомогою вашого приватного ключа",
"sign_verify_title": "Знак / Перевірка",
"signature": "Підпис",
"signature_invalid_error": "Підпис не є дійсним для наведеного повідомлення",
"signTransaction": "Підписати транзакцію",
"signup_for_card_accept_terms": "Зареєструйтеся на картку та прийміть умови.",
"silent_payments": "Мовчазні платежі",
@ -821,6 +830,7 @@
"value_type": "Тип значення",
"variable_pair_not_supported": "Ця пара змінних не підтримується вибраними біржами",
"verification": "Перевірка",
"verify_message": "Перевірте повідомлення",
"verify_with_2fa": "Перевірте за допомогою Cake 2FA",
"version": "Версія ${currentVersion}",
"view_all": "Переглянути все",

View file

@ -338,6 +338,8 @@
"incoming": "آنے والا",
"incorrect_seed": "درج کردہ متن درست نہیں ہے۔",
"inputs": "آدانوں",
"insufficient_lamport_for_tx": "آپ کے پاس ٹرانزیکشن اور اس کے لین دین کی فیس کا احاطہ کرنے کے لئے کافی SOL نہیں ہے۔ برائے مہربانی اپنے بٹوے میں مزید سول شامل کریں یا آپ کو بھیجنے والی سول رقم کو کم کریں۔",
"insufficient_lamports": "آپ کے پاس ٹرانزیکشن اور اس کے لین دین کی فیس کا احاطہ کرنے کے لئے کافی SOL نہیں ہے۔ آپ کو کم از کم ${solValueNeeded} sol کی ضرورت ہے۔ برائے مہربانی اپنے بٹوے میں مزید SOL شامل کریں یا آپ جس SOL رقم کو بھیج رہے ہو اسے کم کریں",
"insufficientFundsForRentError": "آپ کے پاس ٹرانزیکشن فیس اور اکاؤنٹ کے لئے کرایہ لینے کے ل enough اتنا SOL نہیں ہے۔ برائے مہربانی اپنے بٹوے میں مزید سول شامل کریں یا آپ کو بھیجنے والی سول رقم کو کم کریں",
"introducing_cake_pay": "Cake پے کا تعارف!",
"invalid_input": "غلط ان پٹ",
@ -368,6 +370,7 @@
"max_value": "زیادہ سے زیادہ: ${value} ${currency}",
"memo": "میمو:",
"message": "ﻡﺎﻐﯿﭘ",
"message_verified": "پیغام کی کامیابی کے ساتھ تصدیق کی گئی",
"methods": "ﮯﻘﯾﺮﻃ",
"min_amount": "کم سے کم: ${value}",
"min_value": "کم سے کم: ${value} ${currency}",
@ -661,7 +664,13 @@
"show_keys": "بیج / چابیاں دکھائیں۔",
"show_market_place": "بازار دکھائیں۔",
"show_seed": "بیج دکھائیں۔",
"sign_message": "سائن پیغام",
"sign_up": "سائن اپ",
"sign_verify_message": "پیغام پر دستخط کریں یا تصدیق کریں",
"sign_verify_message_sub": "اپنی نجی کلید کا استعمال کرتے ہوئے کسی پیغام پر دستخط کریں یا اس کی تصدیق کریں",
"sign_verify_title": "سائن / تصدیق کریں",
"signature": "دستخط",
"signature_invalid_error": "دستخط دیئے گئے پیغام کے لئے درست نہیں ہے",
"signTransaction": "۔ﮟﯾﺮﮐ ﻂﺨﺘﺳﺩ ﺮﭘ ﻦﯾﺩ ﻦﯿﻟ",
"signup_for_card_accept_terms": "کارڈ کے لیے سائن اپ کریں اور شرائط کو قبول کریں۔",
"silent_payments": "خاموش ادائیگی",
@ -822,6 +831,7 @@
"value_type": "قدر کی قسم",
"variable_pair_not_supported": "یہ متغیر جوڑا منتخب ایکسچینجز کے ساتھ تعاون یافتہ نہیں ہے۔",
"verification": "تصدیق",
"verify_message": "پیغام کی تصدیق کریں",
"verify_with_2fa": "کیک 2FA سے تصدیق کریں۔",
"version": "ورژن ${currentVersion}",
"view_all": "سب دیکھیں",

View file

@ -339,6 +339,8 @@
"incoming": "Wọ́n tó ń bọ̀",
"incorrect_seed": "Ọ̀rọ̀ tí a tẹ̀ kì í ṣe èyí.",
"inputs": "Igbewọle",
"insufficient_lamport_for_tx": "O ko ni sosi to lati bo idunadura ati idiyele iṣowo rẹ. Fi agbara kun Sol diẹ sii si apamọwọ rẹ tabi dinku sodo naa ti o \\ 'tun n firanṣẹ.",
"insufficient_lamports": "O ko ni sosi to lati bo idunadura ati idiyele iṣowo rẹ. O nilo o kere ju ${solValueNeeded}. Fi agbara kun Sol diẹ sii si apamọwọ rẹ tabi dinku soso ti o n firanṣẹ",
"insufficientFundsForRentError": "O ko ni Sol kan lati bo owo isanwo naa ki o yalo fun iroyin naa. Fi agbara kun Sol diẹ sii si apamọwọ rẹ tabi dinku soso naa ti o \\ 'tun n firanṣẹ",
"introducing_cake_pay": "Ẹ bá Cake Pay!",
"invalid_input": "Iṣawọle ti ko tọ",
@ -369,6 +371,7 @@
"max_value": "kò gbọ́dọ̀ tóbi ju ${value} ${currency}",
"memo": "Àkọsílẹ̀:",
"message": "Ifiranṣẹ",
"message_verified": "Ifiranṣẹ naa ni aṣeyọri ni ifijišẹ",
"methods": "Awọn ọna",
"min_amount": "kò kéré ju: ${value}",
"min_value": "kò gbọ́dọ̀ kéré ju ${value} ${currency}",
@ -660,7 +663,13 @@
"show_keys": "Wo hóró / àwọn kọ́kọ́rọ́",
"show_market_place": "Wa Sopọ Pataki",
"show_seed": "Wo hóró",
"sign_message": "Ifiranṣẹ Ami",
"sign_up": "Forúkọ sílẹ̀",
"sign_verify_message": "Ami tabi ṣayẹwo ifiranṣẹ",
"sign_verify_message_sub": "Wọle tabi ṣayẹwo ifiranṣẹ kan nipa lilo bọtini ikọkọ rẹ",
"sign_verify_title": "Ami / Daju",
"signature": "Ibọwọlu",
"signature_invalid_error": "Ibuwọlu ko wulo fun ifiranṣẹ ti a fun",
"signTransaction": "Wole Idunadura",
"signup_for_card_accept_terms": "Ẹ f'orúkọ sílẹ̀ láti gba káàdì àti àjọrò.",
"silent_payments": "Awọn sisanwo ipalọlọ",
@ -821,6 +830,7 @@
"value_type": "Iru iye",
"variable_pair_not_supported": "A kì í ṣe k'á fi àwọn ilé pàṣípààrọ̀ yìí ṣe pàṣípààrọ̀ irú owó méji yìí",
"verification": "Ìjẹ́rìísí",
"verify_message": "Daju ifiranṣẹ",
"verify_with_2fa": "Ṣeẹda pẹlu Cake 2FA",
"version": "Àtúnse ${currentVersion}",
"view_all": "Wo gbogbo nǹkan kan",

View file

@ -338,6 +338,8 @@
"incoming": "收到",
"incorrect_seed": "输入的文字无效。",
"inputs": "输入",
"insufficient_lamport_for_tx": "您没有足够的溶胶来支付交易及其交易费用。请在您的钱包中添加更多溶胶或减少您发送的溶胶量。",
"insufficient_lamports": "您没有足够的溶胶来支付交易及其交易费用。您至少需要${solValueNeeded} sol。请在您的钱包中添加更多溶胶或减少您发送的溶胶量",
"insufficientFundsForRentError": "您没有足够的溶胶来支付该帐户的交易费和租金。请在钱包中添加更多溶胶或减少您发送的溶胶量",
"introducing_cake_pay": "介绍 Cake Pay!",
"invalid_input": "输入无效",
@ -368,6 +370,7 @@
"max_value": "最大: ${value} ${currency}",
"memo": "备忘录:",
"message": "信息",
"message_verified": "该消息已成功验证",
"methods": "方法",
"min_amount": "最小值: ${value}",
"min_value": "最小: ${value} ${currency}",
@ -659,7 +662,13 @@
"show_keys": "显示种子/密钥",
"show_market_place": "显示市场",
"show_seed": "显示种子",
"sign_message": "标志消息",
"sign_up": "注册",
"sign_verify_message": "签名或验证消息",
"sign_verify_message_sub": "使用您的私钥签名或验证消息",
"sign_verify_title": "签名 /验证",
"signature": "签名",
"signature_invalid_error": "签名对于给出的消息无效",
"signTransaction": "签署交易",
"signup_for_card_accept_terms": "注册卡并接受条款。",
"silent_payments": "无声付款",
@ -820,6 +829,7 @@
"value_type": "值类型",
"variable_pair_not_supported": "所选交易所不支持此变量对",
"verification": "验证",
"verify_message": "验证消息",
"verify_with_2fa": "用 Cake 2FA 验证",
"version": "版本 ${currentVersion}",
"view_all": "查看全部",

Some files were not shown because too many files have changed in this diff Show more