mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-11-13 09:04:09 +01:00
CW-555-Add-Solana-Wallet (#1272)
* chore: Create cw_solana package and clean up files * feat: Add Solana Wallet - Create, Restore form seed, restore from Key, Restore from QR, Send, Receive, transaction history, spl tokens * fix: Make transactions file specific to solana only for solana transactions * chore: Revert inject app details script * fix: Fix issue with node and switch current node to main beta instead of testnet * fix: Fix merge conflicts and adjust migration version * fix: Fetch spl token error Signed-off-by: Blazebrain <davidadegoke16@gmail.com> * fix: Diplay and activate spl tokens bug * fix: Review and fixes * fix: reverted formatting for cryptocurrency class * fix: Review comments, split sending flow into signing and sending separately, fix issues * fix: Revert throwing unimplenented error * chore: Fix comment * chore: Fix comment * fix: Errors in flow * Update provider_types.dart [skip ci] * fix: Issues with solana wallet * Update solana_wallet.dart [skip ci] * fix: Review comments * fix: Date time config * fix: Revert bash script for app details * fix: Error with balance, displaying fees, fixing sent or received identifier bug, displaying token symbol with token transaction item in transactions list * fix: Issues with address validation when sending spl tokens and walletconnect initial setup * fix: Issues with sending, fetching transactions history, almost wrapping up walletconnect * fix: Adjust imports that would affect monerocom building successfully * fix: Refine transaction direction and continue work on walletconnect * feat: Display SPL token transfers in the transaction history and finally settle the transaction direction * fix: Delay in transactions history dispaly, show native token transactions first, then process spl token transactions * feat: Switch node and revert solana chain id to previous id * fix: Remove print statement * fix: Remove await for transactions, fetch all transaction histories instantly and adjust solana send success message * chore: Code refactoring and streamlined wallet type check for solana send success message * fix: Make timeout error for node silent and add spl token images --------- Signed-off-by: Blazebrain <davidadegoke16@gmail.com> Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
co-authored by
Omar Hatem
parent
5a7ea87543
commit
109bba4301
@@ -1,6 +1,7 @@
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/core/validator.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
|
||||
@@ -8,9 +9,7 @@ class AddressValidator extends TextValidator {
|
||||
AddressValidator({required CryptoCurrency type})
|
||||
: super(
|
||||
errorMessage: S.current.error_text_address,
|
||||
useAdditionalValidation: type == CryptoCurrency.btc
|
||||
? bitcoin.Address.validateAddress
|
||||
: null,
|
||||
useAdditionalValidation: type == CryptoCurrency.btc ? bitcoin.Address.validateAddress : null,
|
||||
pattern: getPattern(type),
|
||||
length: getLength(type));
|
||||
|
||||
@@ -130,6 +129,12 @@ class AddressValidator extends TextValidator {
|
||||
if (type is Erc20Token) {
|
||||
return [42];
|
||||
}
|
||||
|
||||
if (solana != null) {
|
||||
final length = solana!.getValidationLength(type);
|
||||
if (length != null) return length;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case CryptoCurrency.xmr:
|
||||
return null;
|
||||
@@ -192,11 +197,11 @@ class AddressValidator extends TextValidator {
|
||||
case CryptoCurrency.sc:
|
||||
return [76];
|
||||
case CryptoCurrency.sol:
|
||||
case CryptoCurrency.usdtSol:
|
||||
case CryptoCurrency.usdcsol:
|
||||
return [32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44];
|
||||
case CryptoCurrency.trx:
|
||||
return [34];
|
||||
case CryptoCurrency.usdcsol:
|
||||
return [32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44];
|
||||
case CryptoCurrency.usdt:
|
||||
return [34];
|
||||
case CryptoCurrency.usdttrc20:
|
||||
@@ -286,6 +291,8 @@ class AddressValidator extends TextValidator {
|
||||
'|bitcoincash:q[0-9a-zA-Z]{42}([^0-9a-zA-Z]|\$)'
|
||||
'|([^0-9a-zA-Z]|^)q[0-9a-zA-Z]{41}([^0-9a-zA-Z]|\$)'
|
||||
'|([^0-9a-zA-Z]|^)q[0-9a-zA-Z]{42}([^0-9a-zA-Z]|\$)';
|
||||
case CryptoCurrency.sol:
|
||||
return '([^0-9a-zA-Z]|^)[1-9A-HJ-NP-Za-km-z]{43,44}([^0-9a-zA-Z]|\$)';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:cake_wallet/haven/haven.dart';
|
||||
import 'package:cake_wallet/core/validator.dart';
|
||||
import 'package:cake_wallet/entities/mnemonic_item.dart';
|
||||
import 'package:cake_wallet/polygon/polygon.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/nano/nano.dart';
|
||||
@@ -37,6 +38,8 @@ class SeedValidator extends Validator<MnemonicItem> {
|
||||
return nano!.getNanoWordList(language);
|
||||
case WalletType.polygon:
|
||||
return polygon!.getPolygonWordList(language);
|
||||
case WalletType.solana:
|
||||
return solana!.getSolanaWordList(language);
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import 'package:cake_wallet/core/wallet_connect/evm_chain_service.dart';
|
||||
import 'package:cake_wallet/core/wallet_connect/chain_service/eth/evm_chain_service.dart';
|
||||
|
||||
enum EVMChainId {
|
||||
ethereum,
|
||||
+3
-3
@@ -3,7 +3,7 @@ import 'dart:developer';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:cake_wallet/core/wallet_connect/eth_transaction_model.dart';
|
||||
import 'package:cake_wallet/core/wallet_connect/evm_chain_id.dart';
|
||||
import 'package:cake_wallet/core/wallet_connect/chain_service/eth/evm_chain_id.dart';
|
||||
import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
@@ -20,8 +20,8 @@ import 'package:eth_sig_util/util/utils.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart';
|
||||
import 'package:web3dart/web3dart.dart';
|
||||
import 'chain_service.dart';
|
||||
import 'wallet_connect_key_service.dart';
|
||||
import '../chain_service.dart';
|
||||
import '../../wallet_connect_key_service.dart';
|
||||
|
||||
class EvmChainServiceImpl implements ChainService {
|
||||
final AppStore appStore;
|
||||
@@ -0,0 +1,28 @@
|
||||
class SolanaSignMessage {
|
||||
final String pubkey;
|
||||
final String message;
|
||||
|
||||
SolanaSignMessage({
|
||||
required this.pubkey,
|
||||
required this.message,
|
||||
});
|
||||
|
||||
factory SolanaSignMessage.fromJson(Map<String, dynamic> json) {
|
||||
return SolanaSignMessage(
|
||||
pubkey: json['pubkey'] as String,
|
||||
message: json['message'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'pubkey': pubkey,
|
||||
'message': message,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SolanaSignMessage(pubkey: $pubkey, message: $message)';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
class SolanaSignTransaction {
|
||||
final String? feePayer;
|
||||
final String? recentBlockhash;
|
||||
final String transaction;
|
||||
final List<SolanaInstruction>? instructions;
|
||||
|
||||
SolanaSignTransaction({
|
||||
required this.feePayer,
|
||||
required this.recentBlockhash,
|
||||
required this.instructions,
|
||||
required this.transaction,
|
||||
});
|
||||
|
||||
factory SolanaSignTransaction.fromJson(Map<String, dynamic> json) {
|
||||
return SolanaSignTransaction(
|
||||
feePayer:json['feePayer'] !=null ? json['feePayer'] as String: null,
|
||||
recentBlockhash: json['recentBlockhash']!=null? json['recentBlockhash'] as String: null,
|
||||
instructions:json['instructions']!=null? (json['instructions'] as List<dynamic>)
|
||||
.map((e) => SolanaInstruction.fromJson(e as Map<String, dynamic>))
|
||||
.toList(): null,
|
||||
transaction: json['transaction'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'feePayer': feePayer,
|
||||
'recentBlockhash': recentBlockhash,
|
||||
'instructions': instructions,
|
||||
'transaction': transaction,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SolanaSignTransaction(feePayer: $feePayer, recentBlockhash: $recentBlockhash, instructions: $instructions, transaction: $transaction)';
|
||||
}
|
||||
}
|
||||
|
||||
class SolanaInstruction {
|
||||
final String programId;
|
||||
final List<SolanaKeyMetadata> keys;
|
||||
final List<int> data;
|
||||
|
||||
SolanaInstruction({
|
||||
required this.programId,
|
||||
required this.keys,
|
||||
required this.data,
|
||||
});
|
||||
|
||||
factory SolanaInstruction.fromJson(Map<String, dynamic> json) {
|
||||
return SolanaInstruction(
|
||||
programId: json['programId'] as String,
|
||||
keys: (json['keys'] as List<dynamic>)
|
||||
.map((e) => SolanaKeyMetadata.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
data: (json['data'] as List<dynamic>).map((e) => e as int).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'programId': programId,
|
||||
'keys': keys,
|
||||
'data': data,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SolanaInstruction(programId: $programId, keys: $keys, data: $data)';
|
||||
}
|
||||
}
|
||||
|
||||
class SolanaKeyMetadata {
|
||||
final String pubkey;
|
||||
final bool isSigner;
|
||||
final bool isWritable;
|
||||
|
||||
SolanaKeyMetadata({
|
||||
required this.pubkey,
|
||||
required this.isSigner,
|
||||
required this.isWritable,
|
||||
});
|
||||
|
||||
factory SolanaKeyMetadata.fromJson(Map<String, dynamic> json) {
|
||||
return SolanaKeyMetadata(
|
||||
pubkey: json['pubkey'] as String,
|
||||
isSigner: json['isSigner'] as bool,
|
||||
isWritable: json['isWritable'] as bool,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'pubkey': pubkey,
|
||||
'isSigner': isSigner,
|
||||
'isWritable': isWritable,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SolanaKeyMetadata(pubkey: $pubkey, isSigner: $isSigner, isWritable: $isWritable)';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import 'solana_chain_service.dart';
|
||||
|
||||
enum SolanaChainId {
|
||||
mainnet,
|
||||
testnet,
|
||||
devnet,
|
||||
}
|
||||
|
||||
extension SolanaChainIdX on SolanaChainId {
|
||||
String chain() {
|
||||
String name = '';
|
||||
|
||||
switch (this) {
|
||||
case SolanaChainId.mainnet:
|
||||
name = '4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ';
|
||||
break;
|
||||
case SolanaChainId.testnet:
|
||||
name = '8E9rvCKLFQia2Y35HXjjpWzj8weVo44K';
|
||||
break;
|
||||
case SolanaChainId.devnet:
|
||||
name = '';
|
||||
break;
|
||||
}
|
||||
|
||||
return '${SolanaChainServiceImpl.namespace}:$name';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:cake_wallet/core/wallet_connect/chain_service/solana/entities/solana_sign_message.dart';
|
||||
import 'package:cake_wallet/core/wallet_connect/chain_service/solana/solana_chain_id.dart';
|
||||
import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_connect/widgets/message_display_widget.dart';
|
||||
import 'package:cake_wallet/core/wallet_connect/models/connection_model.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_connect/widgets/connection_widget.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_connect/widgets/modals/web3_request_modal.dart';
|
||||
import 'package:solana/base58.dart';
|
||||
import 'package:solana/solana.dart';
|
||||
import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart';
|
||||
import '../chain_service.dart';
|
||||
import '../../wallet_connect_key_service.dart';
|
||||
import 'entities/solana_sign_transaction.dart';
|
||||
|
||||
class SolanaChainServiceImpl implements ChainService {
|
||||
final BottomSheetService bottomSheetService;
|
||||
final Web3Wallet wallet;
|
||||
final WalletConnectKeyService wcKeyService;
|
||||
|
||||
static const namespace = 'solana';
|
||||
static const solSignTransaction = 'solana_signTransaction';
|
||||
static const solSignMessage = 'solana_signMessage';
|
||||
|
||||
final SolanaChainId reference;
|
||||
|
||||
final SolanaClient solanaClient;
|
||||
|
||||
final Ed25519HDKeyPair? ownerKeyPair;
|
||||
|
||||
SolanaChainServiceImpl({
|
||||
required this.reference,
|
||||
required this.wcKeyService,
|
||||
required this.bottomSheetService,
|
||||
required this.wallet,
|
||||
required this.ownerKeyPair,
|
||||
required String webSocketUrl,
|
||||
required Uri rpcUrl,
|
||||
SolanaClient? solanaClient,
|
||||
}) : solanaClient = solanaClient ??
|
||||
SolanaClient(
|
||||
rpcUrl: rpcUrl,
|
||||
websocketUrl: Uri.parse(webSocketUrl),
|
||||
timeout: const Duration(minutes: 2),
|
||||
) {
|
||||
for (final String event in getEvents()) {
|
||||
wallet.registerEventEmitter(chainId: getChainId(), event: event);
|
||||
}
|
||||
wallet.registerRequestHandler(
|
||||
chainId: getChainId(),
|
||||
method: solSignTransaction,
|
||||
handler: solanaSignTransaction,
|
||||
);
|
||||
wallet.registerRequestHandler(
|
||||
chainId: getChainId(),
|
||||
method: solSignMessage,
|
||||
handler: solanaSignMessage,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
@override
|
||||
String getChainId() {
|
||||
return reference.chain();
|
||||
}
|
||||
|
||||
@override
|
||||
List<String> getEvents() {
|
||||
return [''];
|
||||
}
|
||||
|
||||
Future<String?> requestAuthorization(String? text) async {
|
||||
// Show the bottom sheet
|
||||
final bool? isApproved = await bottomSheetService.queueBottomSheet(
|
||||
widget: Web3RequestModal(
|
||||
child: ConnectionWidget(
|
||||
title: S.current.signTransaction,
|
||||
info: [
|
||||
ConnectionModel(
|
||||
text: text,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
) as bool?;
|
||||
|
||||
if (isApproved != null && isApproved == false) {
|
||||
return 'User rejected signature';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<String> solanaSignTransaction(String topic, dynamic parameters) async {
|
||||
log('received solana sign transaction request $parameters');
|
||||
|
||||
final solanaSignTx =
|
||||
SolanaSignTransaction.fromJson(parameters as Map<String, dynamic>);
|
||||
|
||||
final String? authError = await requestAuthorization('Confirm request to sign transaction?');
|
||||
|
||||
if (authError != null) {
|
||||
return authError;
|
||||
}
|
||||
|
||||
try {
|
||||
final message =
|
||||
await solanaClient.rpcClient.getMessageFromEncodedTx(solanaSignTx.transaction);
|
||||
|
||||
final sign = await ownerKeyPair?.signMessage(
|
||||
message: message,
|
||||
recentBlockhash: solanaSignTx.recentBlockhash ?? '',
|
||||
);
|
||||
|
||||
if (sign == null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
String signature = sign.signatures.first.toBase58();
|
||||
|
||||
print(signature);
|
||||
print(signature.runtimeType);
|
||||
|
||||
bottomSheetService.queueBottomSheet(
|
||||
isModalDismissible: true,
|
||||
widget: BottomSheetMessageDisplayWidget(
|
||||
message: S.current.awaitDAppProcessing,
|
||||
isError: false,
|
||||
),
|
||||
);
|
||||
|
||||
return signature;
|
||||
} catch (e) {
|
||||
log('An error has occurred while signing transaction: ${e.toString()}');
|
||||
bottomSheetService.queueBottomSheet(
|
||||
isModalDismissible: true,
|
||||
widget: BottomSheetMessageDisplayWidget(
|
||||
message: '${S.current.errorSigningTransaction}: ${e.toString()}',
|
||||
),
|
||||
);
|
||||
return 'Failed';
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> solanaSignMessage(String topic, dynamic parameters) async {
|
||||
log('received solana sign message request: $parameters');
|
||||
|
||||
final solanaSignMessage = SolanaSignMessage.fromJson(parameters as Map<String, dynamic>);
|
||||
|
||||
final String? authError = await requestAuthorization('Confirm request to sign message?');
|
||||
|
||||
if (authError != null) {
|
||||
return authError;
|
||||
}
|
||||
Signature? sign;
|
||||
|
||||
try {
|
||||
sign = await ownerKeyPair?.sign(base58decode(solanaSignMessage.message));
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
|
||||
if (sign == null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
String signature = sign.toBase58();
|
||||
|
||||
return signature;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cake_wallet/core/wallet_connect/models/chain_key_model.dart';
|
||||
import 'package:cake_wallet/polygon/polygon.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
@@ -13,7 +14,6 @@ abstract class WalletConnectKeyService {
|
||||
/// If the chain is not found, returns an empty list.
|
||||
/// - [chain]: The chain to get the keys for.
|
||||
List<ChainKeyModel> getKeysForChain(WalletBase wallet);
|
||||
|
||||
}
|
||||
|
||||
class KeyServiceImpl implements WalletConnectKeyService {
|
||||
@@ -23,6 +23,8 @@ class KeyServiceImpl implements WalletConnectKeyService {
|
||||
return ethereum!.getPrivateKey(wallet);
|
||||
case WalletType.polygon:
|
||||
return polygon!.getPrivateKey(wallet);
|
||||
case WalletType.solana:
|
||||
return solana!.getPrivateKey(wallet);
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
@@ -34,6 +36,8 @@ class KeyServiceImpl implements WalletConnectKeyService {
|
||||
return ethereum!.getPublicKey(wallet);
|
||||
case WalletType.polygon:
|
||||
return polygon!.getPublicKey(wallet);
|
||||
case WalletType.solana:
|
||||
return solana!.getPublicKey(wallet);
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
@@ -53,6 +57,14 @@ class KeyServiceImpl implements WalletConnectKeyService {
|
||||
privateKey: _getPrivateKeyForWallet(wallet),
|
||||
publicKey: _getPublicKeyForWallet(wallet),
|
||||
),
|
||||
ChainKeyModel(
|
||||
chains: [
|
||||
'solana:4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ', // main-net
|
||||
'solana:8E9rvCKLFQia2Y35HXjjpWzj8weVo44K', // test-net
|
||||
],
|
||||
privateKey: _getPrivateKeyForWallet(wallet),
|
||||
publicKey: _getPublicKeyForWallet(wallet),
|
||||
),
|
||||
];
|
||||
return keys;
|
||||
}
|
||||
|
||||
@@ -2,23 +2,27 @@ import 'dart:async';
|
||||
import 'dart:developer';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:cake_wallet/core/wallet_connect/evm_chain_id.dart';
|
||||
import 'package:cake_wallet/core/wallet_connect/evm_chain_service.dart';
|
||||
import 'package:cake_wallet/core/wallet_connect/chain_service/eth/evm_chain_id.dart';
|
||||
import 'package:cake_wallet/core/wallet_connect/chain_service/eth/evm_chain_service.dart';
|
||||
import 'package:cake_wallet/core/wallet_connect/wallet_connect_key_service.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/core/wallet_connect/models/auth_request_model.dart';
|
||||
import 'package:cake_wallet/core/wallet_connect/models/chain_key_model.dart';
|
||||
import 'package:cake_wallet/core/wallet_connect/models/session_request_model.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_connect/widgets/connection_request_widget.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_connect/widgets/message_display_widget.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_connect/widgets/modals/web3_request_modal.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:eth_sig_util/eth_sig_util.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart';
|
||||
|
||||
import 'chain_service/solana/solana_chain_id.dart';
|
||||
import 'chain_service/solana/solana_chain_service.dart';
|
||||
import 'wc_bottom_sheet_service.dart';
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
|
||||
@@ -114,14 +118,34 @@ abstract class Web3WalletServiceBase with Store {
|
||||
final newAuthRequests = _web3Wallet.completeRequests.getAll();
|
||||
auth.addAll(newAuthRequests);
|
||||
|
||||
for (final cId in EVMChainId.values) {
|
||||
EvmChainServiceImpl(
|
||||
reference: cId,
|
||||
appStore: appStore,
|
||||
wcKeyService: walletKeyService,
|
||||
bottomSheetService: _bottomSheetHandler,
|
||||
wallet: _web3Wallet,
|
||||
);
|
||||
if (isEVMCompatibleChain(appStore.wallet!.type)) {
|
||||
for (final cId in EVMChainId.values) {
|
||||
EvmChainServiceImpl(
|
||||
reference: cId,
|
||||
appStore: appStore,
|
||||
wcKeyService: walletKeyService,
|
||||
bottomSheetService: _bottomSheetHandler,
|
||||
wallet: _web3Wallet,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (appStore.wallet!.type == WalletType.solana) {
|
||||
for (final cId in SolanaChainId.values) {
|
||||
final node = appStore.settingsStore.getCurrentNode(appStore.wallet!.type);
|
||||
final rpcUri = node.uri;
|
||||
final webSocketUri = 'wss://${node.uriRaw}/ws${node.uri.path}';
|
||||
|
||||
SolanaChainServiceImpl(
|
||||
reference: cId,
|
||||
rpcUrl: rpcUri,
|
||||
webSocketUrl: webSocketUri,
|
||||
wcKeyService: walletKeyService,
|
||||
bottomSheetService: _bottomSheetHandler,
|
||||
wallet: _web3Wallet,
|
||||
ownerKeyPair: solana!.getWalletKeyPair(appStore.wallet!),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -21,6 +21,7 @@ import 'package:cake_wallet/ionia/ionia_gift_card.dart';
|
||||
import 'package:cake_wallet/ionia/ionia_tip.dart';
|
||||
import 'package:cake_wallet/polygon/polygon.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.dart';
|
||||
import 'package:cake_wallet/src/screens/buy/buy_options_page.dart';
|
||||
import 'package:cake_wallet/src/screens/buy/webview_page.dart';
|
||||
@@ -863,6 +864,8 @@ Future<void> setup({
|
||||
return nano!.createNanoWalletService(_walletInfoSource);
|
||||
case WalletType.polygon:
|
||||
return polygon!.createPolygonWalletService(_walletInfoSource);
|
||||
case WalletType.solana:
|
||||
return solana!.createSolanaWalletService(_walletInfoSource);
|
||||
default:
|
||||
throw Exception('Unexpected token: ${param1.toString()} for generating of WalletService');
|
||||
}
|
||||
@@ -1174,7 +1177,7 @@ Future<void> setup({
|
||||
getIt.registerFactoryParam<EditTokenPage, HomeSettingsViewModel, Map<String, dynamic>>(
|
||||
(homeSettingsViewModel, arguments) => EditTokenPage(
|
||||
homeSettingsViewModel: homeSettingsViewModel,
|
||||
erc20token: arguments['token'] as Erc20Token?,
|
||||
token: arguments['token'] as CryptoCurrency?,
|
||||
initialContractAddress: arguments['contractAddress'] as String?,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -30,6 +30,7 @@ const polygonDefaultNodeUri = 'polygon-bor.publicnode.com';
|
||||
const cakeWalletBitcoinCashDefaultNodeUri = 'bitcoincash.stackwallet.com:50002';
|
||||
const nanoDefaultNodeUri = 'rpc.nano.to';
|
||||
const nanoDefaultPowNodeUri = 'rpc.nano.to';
|
||||
const solanaDefaultNodeUri = 'rpc.ankr.com';
|
||||
|
||||
Future<void> defaultSettingsMigration(
|
||||
{required int version,
|
||||
@@ -186,10 +187,15 @@ Future<void> defaultSettingsMigration(
|
||||
await rewriteSecureStoragePin(secureStorage: secureStorage);
|
||||
break;
|
||||
case 26:
|
||||
/// commented out as it was a probable cause for some users to have white screen issues
|
||||
/// maybe due to multiple access on Secure Storage at once
|
||||
/// or long await time on start of the app
|
||||
// await insecureStorageMigration(secureStorage: secureStorage, sharedPreferences: sharedPreferences);
|
||||
|
||||
/// commented out as it was a probable cause for some users to have white screen issues
|
||||
/// maybe due to multiple access on Secure Storage at once
|
||||
/// or long await time on start of the app
|
||||
// await insecureStorageMigration(secureStorage: secureStorage, sharedPreferences: sharedPreferences);
|
||||
case 27:
|
||||
await addSolanaNodeList(nodes: nodes);
|
||||
await changeSolanaCurrentNodeToDefault(
|
||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -384,6 +390,11 @@ Node getMoneroDefaultNode({required Box<Node> nodes}) {
|
||||
}
|
||||
}
|
||||
|
||||
Node? getSolanaDefaultNode({required Box<Node> nodes}) {
|
||||
return nodes.values.firstWhereOrNull((Node node) => node.uriRaw == solanaDefaultNodeUri) ??
|
||||
nodes.values.firstWhereOrNull((node) => node.type == WalletType.solana);
|
||||
}
|
||||
|
||||
Future<void> insecureStorageMigration({
|
||||
required SharedPreferences sharedPreferences,
|
||||
required FlutterSecureStorage secureStorage,
|
||||
@@ -673,6 +684,7 @@ Future<void> checkCurrentNodes(
|
||||
final currentNanoPowNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoPowNodeIdKey);
|
||||
final currentBitcoinCashNodeId =
|
||||
sharedPreferences.getInt(PreferencesKey.currentBitcoinCashNodeIdKey);
|
||||
final currentSolanaNodeId = sharedPreferences.getInt(PreferencesKey.currentSolanaNodeIdKey);
|
||||
final currentMoneroNode =
|
||||
nodeSource.values.firstWhereOrNull((node) => node.key == currentMoneroNodeId);
|
||||
final currentBitcoinElectrumServer =
|
||||
@@ -691,6 +703,8 @@ Future<void> checkCurrentNodes(
|
||||
powNodeSource.values.firstWhereOrNull((node) => node.key == currentNanoPowNodeId);
|
||||
final currentBitcoinCashNodeServer =
|
||||
nodeSource.values.firstWhereOrNull((node) => node.key == currentBitcoinCashNodeId);
|
||||
final currentSolanaNodeServer =
|
||||
nodeSource.values.firstWhereOrNull((node) => node.key == currentSolanaNodeId);
|
||||
if (currentMoneroNode == null) {
|
||||
final newCakeWalletNode = Node(uri: newCakeWalletMoneroUri, type: WalletType.monero);
|
||||
await nodeSource.add(newCakeWalletNode);
|
||||
@@ -750,6 +764,12 @@ Future<void> checkCurrentNodes(
|
||||
await nodeSource.add(node);
|
||||
await sharedPreferences.setInt(PreferencesKey.currentPolygonNodeIdKey, node.key as int);
|
||||
}
|
||||
|
||||
if (currentSolanaNodeServer == null) {
|
||||
final node = Node(uri: solanaDefaultNodeUri, type: WalletType.solana);
|
||||
await nodeSource.add(node);
|
||||
await sharedPreferences.setInt(PreferencesKey.currentSolanaNodeIdKey, node.key as int);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> resetBitcoinElectrumServer(
|
||||
@@ -861,3 +881,20 @@ Future<void> changePolygonCurrentNodeToDefault(
|
||||
|
||||
await sharedPreferences.setInt(PreferencesKey.currentPolygonNodeIdKey, nodeId);
|
||||
}
|
||||
|
||||
Future<void> addSolanaNodeList({required Box<Node> nodes}) async {
|
||||
final nodeList = await loadDefaultSolanaNodes();
|
||||
for (var node in nodeList) {
|
||||
if (nodes.values.firstWhereOrNull((element) => element.uriRaw == node.uriRaw) == null) {
|
||||
await nodes.add(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> changeSolanaCurrentNodeToDefault(
|
||||
{required SharedPreferences sharedPreferences, required Box<Node> nodes}) async {
|
||||
final node = getSolanaDefaultNode(nodes: nodes);
|
||||
final nodeId = node?.key as int? ?? 0;
|
||||
|
||||
await sharedPreferences.setInt(PreferencesKey.currentSolanaNodeIdKey, nodeId);
|
||||
}
|
||||
|
||||
@@ -149,6 +149,23 @@ Future<List<Node>> loadDefaultPolygonNodes() async {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
Future<List<Node>> loadDefaultSolanaNodes() async {
|
||||
final nodesRaw = await rootBundle.loadString('assets/solana_node_list.yml');
|
||||
final loadedNodes = loadYaml(nodesRaw) as YamlList;
|
||||
final nodes = <Node>[];
|
||||
|
||||
for (final raw in loadedNodes) {
|
||||
if (raw is Map) {
|
||||
final node = Node.fromMap(Map<String, Object>.from(raw));
|
||||
|
||||
node.type = WalletType.solana;
|
||||
nodes.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
Future<void> resetToDefault(Box<Node> nodeSource) async {
|
||||
final moneroNodes = await loadDefaultNodes();
|
||||
final bitcoinElectrumServerList = await loadBitcoinElectrumServerList();
|
||||
@@ -158,6 +175,7 @@ Future<void> resetToDefault(Box<Node> nodeSource) async {
|
||||
final ethereumNodes = await loadDefaultEthereumNodes();
|
||||
final nanoNodes = await loadDefaultNanoNodes();
|
||||
final polygonNodes = await loadDefaultPolygonNodes();
|
||||
final solanaNodes = await loadDefaultSolanaNodes();
|
||||
|
||||
final nodes = moneroNodes +
|
||||
bitcoinElectrumServerList +
|
||||
@@ -166,7 +184,8 @@ Future<void> resetToDefault(Box<Node> nodeSource) async {
|
||||
ethereumNodes +
|
||||
bitcoinCashElectrumServerList +
|
||||
nanoNodes +
|
||||
polygonNodes;
|
||||
polygonNodes +
|
||||
solanaNodes;
|
||||
|
||||
await nodeSource.clear();
|
||||
await nodeSource.addAll(nodes);
|
||||
|
||||
@@ -13,6 +13,7 @@ class PreferencesKey {
|
||||
static const currentBananoPowNodeIdKey = 'current_node_id_banano_pow';
|
||||
static const currentFiatCurrencyKey = 'current_fiat_currency';
|
||||
static const currentBitcoinCashNodeIdKey = 'current_node_id_bch';
|
||||
static const currentSolanaNodeIdKey = 'current_node_id_sol';
|
||||
static const currentTransactionPriorityKeyLegacy = 'current_fee_priority';
|
||||
static const currentBalanceDisplayModeKey = 'current_balance_display_mode';
|
||||
static const shouldSaveRecipientAddressKey = 'save_recipient_address';
|
||||
|
||||
@@ -21,12 +21,13 @@ List<TransactionPriority> priorityForWalletType(WalletType type) {
|
||||
return ethereum!.getTransactionPriorities();
|
||||
case WalletType.bitcoinCash:
|
||||
return bitcoinCash!.getTransactionPriorities();
|
||||
// no such thing for nano/banano:
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
return [];
|
||||
case WalletType.polygon:
|
||||
return polygon!.getTransactionPriorities();
|
||||
// no such thing for nano/banano/solana:
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
case WalletType.solana:
|
||||
return [];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -67,6 +67,8 @@ class ProvidersHelper {
|
||||
return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.robinhood];
|
||||
case WalletType.polygon:
|
||||
return [ProviderType.askEachTime, ProviderType.dfx];
|
||||
case WalletType.solana:
|
||||
return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.robinhood];
|
||||
case WalletType.none:
|
||||
case WalletType.haven:
|
||||
return [];
|
||||
@@ -88,6 +90,13 @@ class ProvidersHelper {
|
||||
return [ProviderType.askEachTime, ProviderType.moonpaySell];
|
||||
case WalletType.polygon:
|
||||
return [ProviderType.askEachTime, ProviderType.dfx];
|
||||
case WalletType.solana:
|
||||
return [
|
||||
ProviderType.askEachTime,
|
||||
ProviderType.onramper,
|
||||
ProviderType.robinhood,
|
||||
ProviderType.moonpaySell,
|
||||
];
|
||||
case WalletType.monero:
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
|
||||
@@ -119,12 +119,13 @@ class CWEthereum extends Ethereum {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> addErc20Token(WalletBase wallet, Erc20Token token) async =>
|
||||
await (wallet as EthereumWallet).addErc20Token(token);
|
||||
Future<void> addErc20Token(WalletBase wallet, CryptoCurrency token) async {
|
||||
await (wallet as EthereumWallet).addErc20Token(token as Erc20Token);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteErc20Token(WalletBase wallet, Erc20Token token) async =>
|
||||
await (wallet as EthereumWallet).deleteErc20Token(token);
|
||||
Future<void> deleteErc20Token(WalletBase wallet, CryptoCurrency token) async =>
|
||||
await (wallet as EthereumWallet).deleteErc20Token(token as Erc20Token);
|
||||
|
||||
@override
|
||||
Future<Erc20Token?> getErc20Token(WalletBase wallet, String contractAddress) async {
|
||||
@@ -153,4 +154,6 @@ class CWEthereum extends Ethereum {
|
||||
Web3Client? getWeb3Client(WalletBase wallet) {
|
||||
return (wallet as EthereumWallet).getWeb3Client();
|
||||
}
|
||||
|
||||
String getTokenAddress(CryptoCurrency asset) => (asset as Erc20Token).contractAddress;
|
||||
}
|
||||
|
||||
+2
-2
@@ -163,11 +163,11 @@ Future<void> initializeAppConfigs() async {
|
||||
transactionDescriptions: transactionDescriptions,
|
||||
secureStorage: secureStorage,
|
||||
anonpayInvoiceInfo: anonpayInvoiceInfo,
|
||||
initialMigrationVersion: 26);
|
||||
initialMigrationVersion: 27);
|
||||
}
|
||||
|
||||
Future<void> initialSetup(
|
||||
{required SharedPreferences sharedPreferences,
|
||||
{required SharedPreferences sharedPreferences,
|
||||
required Box<Node> nodes,
|
||||
required Box<Node> powNodes,
|
||||
required Box<WalletInfo> walletInfoSource,
|
||||
|
||||
@@ -119,12 +119,12 @@ class CWPolygon extends Polygon {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> addErc20Token(WalletBase wallet, Erc20Token token) async =>
|
||||
await (wallet as PolygonWallet).addErc20Token(token);
|
||||
Future<void> addErc20Token(WalletBase wallet, CryptoCurrency token) async =>
|
||||
await (wallet as PolygonWallet).addErc20Token(token as Erc20Token);
|
||||
|
||||
@override
|
||||
Future<void> deleteErc20Token(WalletBase wallet, Erc20Token token) async =>
|
||||
await (wallet as PolygonWallet).deleteErc20Token(token);
|
||||
Future<void> deleteErc20Token(WalletBase wallet, CryptoCurrency token) async =>
|
||||
await (wallet as PolygonWallet).deleteErc20Token(token as Erc20Token);
|
||||
|
||||
@override
|
||||
Future<Erc20Token?> getErc20Token(WalletBase wallet, String contractAddress) async {
|
||||
@@ -153,4 +153,6 @@ class CWPolygon extends Polygon {
|
||||
Web3Client? getWeb3Client(WalletBase wallet) {
|
||||
return (wallet as PolygonWallet).getWeb3Client();
|
||||
}
|
||||
|
||||
String getTokenAddress(CryptoCurrency asset) => (asset as Erc20Token).contractAddress;
|
||||
}
|
||||
|
||||
@@ -4,9 +4,11 @@ import 'package:cake_wallet/entities/fiat_api_mode.dart';
|
||||
import 'package:cake_wallet/entities/update_haven_rate.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cake_wallet/polygon/polygon.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
@@ -35,7 +37,7 @@ Future<void> startFiatRateUpdate(
|
||||
torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly);
|
||||
}
|
||||
|
||||
Iterable<Erc20Token>? currencies;
|
||||
Iterable<CryptoCurrency>? currencies;
|
||||
if (appStore.wallet!.type == WalletType.ethereum) {
|
||||
currencies =
|
||||
ethereum!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
|
||||
@@ -46,6 +48,12 @@ Future<void> startFiatRateUpdate(
|
||||
polygon!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
|
||||
}
|
||||
|
||||
if (appStore.wallet!.type == WalletType.solana) {
|
||||
currencies =
|
||||
solana!.getSPLTokenCurrencies(appStore.wallet!).where((element) => element.enabled);
|
||||
}
|
||||
|
||||
|
||||
if (currencies != null) {
|
||||
for (final currency in currencies) {
|
||||
() async {
|
||||
|
||||
@@ -3,6 +3,8 @@ import 'package:cake_wallet/entities/fiat_api_mode.dart';
|
||||
import 'package:cake_wallet/entities/update_haven_rate.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cake_wallet/polygon/polygon.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
import 'package:cw_core/transaction_history.dart';
|
||||
import 'package:cw_core/balance.dart';
|
||||
@@ -109,7 +111,7 @@ void startCurrentWalletChangeReaction(
|
||||
fiat: settingsStore.fiatCurrency,
|
||||
torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly);
|
||||
|
||||
Iterable<Erc20Token>? currencies;
|
||||
Iterable<CryptoCurrency>? currencies;
|
||||
if (wallet.type == WalletType.ethereum) {
|
||||
currencies =
|
||||
ethereum!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
|
||||
@@ -118,7 +120,11 @@ void startCurrentWalletChangeReaction(
|
||||
currencies =
|
||||
polygon!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.solana) {
|
||||
currencies =
|
||||
solana!.getSPLTokenCurrencies(appStore.wallet!).where((element) => element.enabled);
|
||||
}
|
||||
|
||||
if (currencies != null) {
|
||||
for (final currency in currencies) {
|
||||
() async {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:cake_wallet/core/wallet_connect/evm_chain_id.dart';
|
||||
import 'package:cake_wallet/core/wallet_connect/chain_service/eth/evm_chain_id.dart';
|
||||
import 'package:cake_wallet/core/wallet_connect/chain_service/solana/solana_chain_id.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
bool isEVMCompatibleChain(WalletType walletType) {
|
||||
@@ -11,12 +12,24 @@ bool isEVMCompatibleChain(WalletType walletType) {
|
||||
}
|
||||
}
|
||||
|
||||
bool isWalletConnectCompatibleChain(WalletType walletType) {
|
||||
switch (walletType) {
|
||||
case WalletType.polygon:
|
||||
case WalletType.ethereum:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
String getChainNameSpaceAndIdBasedOnWalletType(WalletType walletType) {
|
||||
switch (walletType) {
|
||||
case WalletType.ethereum:
|
||||
return EVMChainId.ethereum.chain();
|
||||
case WalletType.polygon:
|
||||
return EVMChainId.polygon.chain();
|
||||
case WalletType.solana:
|
||||
return SolanaChainId.mainnet.chain();
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
@@ -40,6 +53,8 @@ String getChainNameBasedOnWalletType(WalletType walletType) {
|
||||
return 'eth';
|
||||
case WalletType.polygon:
|
||||
return 'polygon';
|
||||
case WalletType.solana:
|
||||
return 'solana';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
@@ -51,6 +66,8 @@ String getTokenNameBasedOnWalletType(WalletType walletType) {
|
||||
return 'ETH';
|
||||
case WalletType.polygon:
|
||||
return 'MATIC';
|
||||
case WalletType.solana:
|
||||
return 'SOL';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
part of 'solana.dart';
|
||||
|
||||
class CWSolana extends Solana {
|
||||
@override
|
||||
List<String> getSolanaWordList(String language) => SolanaMnemonics.englishWordlist;
|
||||
|
||||
WalletService createSolanaWalletService(Box<WalletInfo> walletInfoSource) =>
|
||||
SolanaWalletService(walletInfoSource);
|
||||
|
||||
@override
|
||||
WalletCredentials createSolanaNewWalletCredentials({
|
||||
required String name,
|
||||
WalletInfo? walletInfo,
|
||||
}) =>
|
||||
SolanaNewWalletCredentials(name: name, walletInfo: walletInfo);
|
||||
|
||||
@override
|
||||
WalletCredentials createSolanaRestoreWalletFromSeedCredentials({
|
||||
required String name,
|
||||
required String mnemonic,
|
||||
required String password,
|
||||
}) =>
|
||||
SolanaRestoreWalletFromSeedCredentials(name: name, password: password, mnemonic: mnemonic);
|
||||
|
||||
@override
|
||||
WalletCredentials createSolanaRestoreWalletFromPrivateKey({
|
||||
required String name,
|
||||
required String privateKey,
|
||||
required String password,
|
||||
}) =>
|
||||
SolanaRestoreWalletFromPrivateKey(name: name, password: password, privateKey: privateKey);
|
||||
|
||||
@override
|
||||
String getAddress(WalletBase wallet) => (wallet as SolanaWallet).walletAddresses.address;
|
||||
|
||||
@override
|
||||
String getPrivateKey(WalletBase wallet) => (wallet as SolanaWallet).privateKey;
|
||||
|
||||
@override
|
||||
String getPublicKey(WalletBase wallet) => (wallet as SolanaWallet).keys.publicKey.toBase58();
|
||||
|
||||
@override
|
||||
Ed25519HDKeyPair? getWalletKeyPair(WalletBase wallet) => (wallet as SolanaWallet).walletKeyPair;
|
||||
|
||||
Object createSolanaTransactionCredentials(
|
||||
List<Output> outputs, {
|
||||
required CryptoCurrency currency,
|
||||
}) =>
|
||||
SolanaTransactionCredentials(
|
||||
outputs
|
||||
.map((out) => OutputInfo(
|
||||
fiatAmount: out.fiatAmount,
|
||||
cryptoAmount: out.cryptoAmount,
|
||||
address: out.address,
|
||||
note: out.note,
|
||||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
formattedCryptoAmount: out.formattedCryptoAmount))
|
||||
.toList(),
|
||||
currency: currency,
|
||||
);
|
||||
|
||||
Object createSolanaTransactionCredentialsRaw(
|
||||
List<OutputInfo> outputs, {
|
||||
required CryptoCurrency currency,
|
||||
}) =>
|
||||
SolanaTransactionCredentials(outputs, currency: currency);
|
||||
|
||||
@override
|
||||
List<SPLToken> getSPLTokenCurrencies(WalletBase wallet) {
|
||||
final solanaWallet = wallet as SolanaWallet;
|
||||
return solanaWallet.splTokenCurrencies;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> addSPLToken(WalletBase wallet, CryptoCurrency token) async =>
|
||||
await (wallet as SolanaWallet).addSPLToken(token as SPLToken);
|
||||
|
||||
@override
|
||||
Future<void> deleteSPLToken(WalletBase wallet, CryptoCurrency token) async =>
|
||||
await (wallet as SolanaWallet).deleteSPLToken(token as SPLToken);
|
||||
|
||||
@override
|
||||
Future<SPLToken?> getSPLToken(WalletBase wallet, String mintAddress) async {
|
||||
final solanaWallet = wallet as SolanaWallet;
|
||||
return await solanaWallet.getSPLToken(mintAddress);
|
||||
}
|
||||
|
||||
@override
|
||||
CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction) {
|
||||
transaction as SolanaTransactionInfo;
|
||||
if (transaction.tokenSymbol == CryptoCurrency.sol.title) {
|
||||
return CryptoCurrency.sol;
|
||||
}
|
||||
|
||||
wallet as SolanaWallet;
|
||||
return wallet.splTokenCurrencies
|
||||
.firstWhere((element) => transaction.tokenSymbol == element.symbol);
|
||||
}
|
||||
|
||||
@override
|
||||
double getTransactionAmountRaw(TransactionInfo transactionInfo) {
|
||||
return (transactionInfo as SolanaTransactionInfo).solAmount.toDouble();
|
||||
}
|
||||
|
||||
@override
|
||||
String getTokenAddress(CryptoCurrency asset) => (asset as SPLToken).mintAddress;
|
||||
|
||||
@override
|
||||
List<int>? getValidationLength(CryptoCurrency type) {
|
||||
if (type is SPLToken) {
|
||||
return [44];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,7 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
|
||||
final bitcoinCashIcon = Image.asset('assets/images/bch_icon.png', height: 24, width: 24);
|
||||
final nanoIcon = Image.asset('assets/images/nano_icon.png', height: 24, width: 24);
|
||||
final bananoIcon = Image.asset('assets/images/nano_icon.png', height: 24, width: 24);
|
||||
final solanaIcon = Image.asset('assets/images/sol_icon.png', height: 24, width: 24);
|
||||
final nonWalletTypeIcon = Image.asset('assets/images/close.png', height: 24, width: 24);
|
||||
|
||||
Image _newWalletImage(BuildContext context) => Image.asset(
|
||||
@@ -153,6 +154,8 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
|
||||
return bananoIcon;
|
||||
case WalletType.polygon:
|
||||
return polygonIcon;
|
||||
case WalletType.solana:
|
||||
return solanaIcon;
|
||||
default:
|
||||
return nonWalletTypeIcon;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/home_settings_view_model.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -16,12 +17,12 @@ class EditTokenPage extends BasePage {
|
||||
EditTokenPage({
|
||||
Key? key,
|
||||
required this.homeSettingsViewModel,
|
||||
this.erc20token,
|
||||
this.token,
|
||||
this.initialContractAddress,
|
||||
}) : assert(erc20token == null || initialContractAddress == null);
|
||||
}) : assert(token == null || initialContractAddress == null);
|
||||
|
||||
final HomeSettingsViewModel homeSettingsViewModel;
|
||||
final Erc20Token? erc20token;
|
||||
final CryptoCurrency? token;
|
||||
final String? initialContractAddress;
|
||||
|
||||
@override
|
||||
@@ -31,7 +32,7 @@ class EditTokenPage extends BasePage {
|
||||
Widget body(BuildContext context) {
|
||||
return EditTokenPageBody(
|
||||
homeSettingsViewModel: homeSettingsViewModel,
|
||||
erc20token: erc20token,
|
||||
token: token,
|
||||
initialContractAddress: initialContractAddress,
|
||||
);
|
||||
}
|
||||
@@ -41,12 +42,12 @@ class EditTokenPageBody extends StatefulWidget {
|
||||
const EditTokenPageBody({
|
||||
Key? key,
|
||||
required this.homeSettingsViewModel,
|
||||
this.erc20token,
|
||||
this.token,
|
||||
this.initialContractAddress,
|
||||
}) : super(key: key);
|
||||
|
||||
final HomeSettingsViewModel homeSettingsViewModel;
|
||||
final Erc20Token? erc20token;
|
||||
final CryptoCurrency? token;
|
||||
final String? initialContractAddress;
|
||||
|
||||
@override
|
||||
@@ -73,11 +74,15 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (widget.erc20token != null) {
|
||||
_contractAddressController.text = widget.erc20token!.contractAddress;
|
||||
_tokenNameController.text = widget.erc20token!.name;
|
||||
_tokenSymbolController.text = widget.erc20token!.symbol;
|
||||
_tokenDecimalController.text = widget.erc20token!.decimal.toString();
|
||||
String? address;
|
||||
|
||||
if (widget.token != null) {
|
||||
address = widget.homeSettingsViewModel.getTokenAddressBasedOnWallet(widget.token!);
|
||||
|
||||
_contractAddressController.text = address ?? '';
|
||||
_tokenNameController.text = widget.token!.name;
|
||||
_tokenSymbolController.text = widget.token!.title;
|
||||
_tokenDecimalController.text = widget.token!.decimals.toString();
|
||||
}
|
||||
|
||||
if (widget.initialContractAddress != null) {
|
||||
@@ -91,7 +96,7 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
|
||||
}
|
||||
|
||||
final contractAddress = _contractAddressController.text;
|
||||
if (contractAddress.isNotEmpty && contractAddress != widget.erc20token?.contractAddress) {
|
||||
if (contractAddress.isNotEmpty && contractAddress != address) {
|
||||
setState(() {
|
||||
_showDisclaimer = true;
|
||||
});
|
||||
@@ -139,7 +144,9 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.normal,
|
||||
color: Theme.of(context).extension<TransactionTradeTheme>()!.detailsTitlesColor,
|
||||
color: Theme.of(context)
|
||||
.extension<TransactionTradeTheme>()!
|
||||
.detailsTitlesColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -172,12 +179,12 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
onPressed: () async {
|
||||
if (widget.erc20token != null) {
|
||||
await widget.homeSettingsViewModel.deleteErc20Token(widget.erc20token!);
|
||||
if (widget.token != null) {
|
||||
await widget.homeSettingsViewModel.deleteToken(widget.token!);
|
||||
}
|
||||
Navigator.pop(context);
|
||||
},
|
||||
text: widget.erc20token != null ? S.of(context).delete : S.of(context).cancel,
|
||||
text: widget.token != null ? S.of(context).delete : S.of(context).cancel,
|
||||
color: Colors.red,
|
||||
textColor: Colors.white,
|
||||
),
|
||||
@@ -188,7 +195,7 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
|
||||
onPressed: () async {
|
||||
if (_formKey.currentState!.validate() &&
|
||||
(!_showDisclaimer || _disclaimerChecked)) {
|
||||
await widget.homeSettingsViewModel.addErc20Token(Erc20Token(
|
||||
await widget.homeSettingsViewModel.addToken(Erc20Token(
|
||||
name: _tokenNameController.text,
|
||||
symbol: _tokenSymbolController.text,
|
||||
contractAddress: _contractAddressController.text,
|
||||
@@ -214,14 +221,13 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
|
||||
|
||||
void _getTokenInfo() async {
|
||||
if (_contractAddressController.text.isNotEmpty) {
|
||||
final token =
|
||||
await widget.homeSettingsViewModel.getErc20Token(_contractAddressController.text);
|
||||
final token = await widget.homeSettingsViewModel.getToken(_contractAddressController.text);
|
||||
|
||||
if (token != null) {
|
||||
if (_tokenNameController.text.isEmpty) _tokenNameController.text = token.name;
|
||||
if (_tokenSymbolController.text.isEmpty) _tokenSymbolController.text = token.symbol;
|
||||
if (_tokenSymbolController.text.isEmpty) _tokenSymbolController.text = token.title;
|
||||
if (_tokenDecimalController.text.isEmpty)
|
||||
_tokenDecimalController.text = token.decimal.toString();
|
||||
_tokenDecimalController.text = token.decimals.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:cake_wallet/entities/sort_balance_types.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/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
|
||||
import 'package:cake_wallet/themes/extensions/address_theme.dart';
|
||||
@@ -117,7 +118,7 @@ class HomeSettingsPage extends BasePage {
|
||||
|
||||
return SettingsSwitcherCell(
|
||||
title: "${token.name} "
|
||||
"(${token.symbol})",
|
||||
"(${token.title})",
|
||||
value: token.enabled,
|
||||
onValueChange: (_, bool value) {
|
||||
_homeSettingsViewModel.changeTokenAvailability(token, value);
|
||||
@@ -128,20 +129,16 @@ class HomeSettingsPage extends BasePage {
|
||||
'token': token,
|
||||
});
|
||||
},
|
||||
leading: token.iconPath != null
|
||||
? Container(
|
||||
child: Image.asset(
|
||||
token.iconPath!,
|
||||
height: 30.0,
|
||||
width: 30.0,
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
leading: CakeImageWidget(
|
||||
imageUrl: token.iconPath,
|
||||
height: 40,
|
||||
width: 40,
|
||||
displayOnError: Container(
|
||||
height: 30.0,
|
||||
width: 30.0,
|
||||
child: Center(
|
||||
child: Text(
|
||||
token.symbol.substring(0, min(token.symbol.length, 2)),
|
||||
token.title.substring(0, min(token.title.length, 2)),
|
||||
style: TextStyle(fontSize: 11),
|
||||
),
|
||||
),
|
||||
@@ -149,7 +146,8 @@ class HomeSettingsPage extends BasePage {
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.grey.shade400,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).cardColor,
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/pages/nft_listing_page.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/home_screen_account_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange_trade/information_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/introducing_card.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
@@ -333,15 +334,11 @@ class BalanceRowWidget extends StatelessWidget {
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
currency.iconPath != null
|
||||
? Container(
|
||||
child: Image.asset(
|
||||
currency.iconPath!,
|
||||
height: 40.0,
|
||||
width: 40.0,
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
CakeImageWidget(
|
||||
imageUrl: currency.iconPath,
|
||||
height: 40,
|
||||
width: 40,
|
||||
displayOnError: Container(
|
||||
height: 30.0,
|
||||
width: 30.0,
|
||||
child: Center(
|
||||
@@ -355,6 +352,7 @@ class BalanceRowWidget extends StatelessWidget {
|
||||
color: Colors.grey.shade400,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
currency.title,
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'package:cake_wallet/entities/wallet_nft_response.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/menu_widget.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/nft_image_tile_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/gradient_background.dart';
|
||||
import 'package:cake_wallet/themes/extensions/balance_page_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
|
||||
@@ -94,7 +94,7 @@ class NFTDetailsPage extends BasePage {
|
||||
.syncedBackgroundColor,
|
||||
|
||||
),
|
||||
child: NFTImageWidget(
|
||||
child: CakeImageWidget(
|
||||
imageUrl: nftAsset.normalizedMetadata?.imageUrl,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -18,23 +18,23 @@ class MenuWidget extends StatefulWidget {
|
||||
|
||||
class MenuWidgetState extends State<MenuWidget> {
|
||||
MenuWidgetState()
|
||||
: this.menuWidth = 0,
|
||||
this.screenWidth = 0,
|
||||
this.screenHeight = 0,
|
||||
this.headerHeight = 120,
|
||||
this.tileHeight = 60,
|
||||
this.fromTopEdge = 50,
|
||||
this.fromBottomEdge = 25,
|
||||
this.moneroIcon = Image.asset('assets/images/monero_menu.png'),
|
||||
this.bitcoinIcon = Image.asset('assets/images/bitcoin_menu.png'),
|
||||
this.litecoinIcon = Image.asset('assets/images/litecoin_menu.png'),
|
||||
this.havenIcon = Image.asset('assets/images/haven_menu.png'),
|
||||
this.ethereumIcon = Image.asset('assets/images/eth_icon.png'),
|
||||
this.nanoIcon = Image.asset('assets/images/nano_icon.png'),
|
||||
this.bananoIcon = Image.asset('assets/images/nano_icon.png'),
|
||||
: this.menuWidth = 0,
|
||||
this.screenWidth = 0,
|
||||
this.screenHeight = 0,
|
||||
this.headerHeight = 120,
|
||||
this.tileHeight = 60,
|
||||
this.fromTopEdge = 50,
|
||||
this.fromBottomEdge = 25,
|
||||
this.moneroIcon = Image.asset('assets/images/monero_menu.png'),
|
||||
this.bitcoinIcon = Image.asset('assets/images/bitcoin_menu.png'),
|
||||
this.litecoinIcon = Image.asset('assets/images/litecoin_menu.png'),
|
||||
this.havenIcon = Image.asset('assets/images/haven_menu.png'),
|
||||
this.ethereumIcon = Image.asset('assets/images/eth_icon.png'),
|
||||
this.nanoIcon = Image.asset('assets/images/nano_icon.png'),
|
||||
this.bananoIcon = Image.asset('assets/images/nano_icon.png'),
|
||||
this.bitcoinCashIcon = Image.asset('assets/images/bch_icon.png'),
|
||||
this.polygonIcon = Image.asset('assets/images/matic_icon.png');
|
||||
|
||||
this.polygonIcon = Image.asset('assets/images/matic_icon.png'),
|
||||
this.solanaIcon = Image.asset('assets/images/sol_icon.png');
|
||||
|
||||
final largeScreen = 731;
|
||||
|
||||
@@ -56,7 +56,7 @@ class MenuWidgetState extends State<MenuWidget> {
|
||||
Image nanoIcon;
|
||||
Image bananoIcon;
|
||||
Image polygonIcon;
|
||||
|
||||
Image solanaIcon;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -224,6 +224,8 @@ class MenuWidgetState extends State<MenuWidget> {
|
||||
return bananoIcon;
|
||||
case WalletType.polygon:
|
||||
return polygonIcon;
|
||||
case WalletType.solana:
|
||||
return solanaIcon;
|
||||
default:
|
||||
throw Exception('No icon for ${type.toString()}');
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import 'package:cake_wallet/entities/wallet_nft_response.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/nft_image_tile_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/themes/extensions/balance_page_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class NFTTileWidget extends StatelessWidget {
|
||||
@@ -38,7 +37,7 @@ class NFTTileWidget extends StatelessWidget {
|
||||
),
|
||||
color: Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor,
|
||||
),
|
||||
child: NFTImageWidget(
|
||||
child: CakeImageWidget(
|
||||
imageUrl: nftAsset.normalizedMetadata?.imageUrl,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -14,6 +14,7 @@ import 'package:cake_wallet/utils/payment_request.dart';
|
||||
import 'package:cake_wallet/utils/request_review_handler.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/view_model/send/output.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
@@ -439,10 +440,17 @@ class SendPage extends BasePage {
|
||||
}
|
||||
|
||||
if (state is TransactionCommitted) {
|
||||
String alertContent;
|
||||
if (sendViewModel.walletType == WalletType.solana) {
|
||||
alertContent =
|
||||
'${S.of(_dialogContext).send_success(sendViewModel.selectedCryptoCurrency.toString())}. ${S.of(_dialogContext).waitFewSecondForTxUpdate}';
|
||||
} else {
|
||||
alertContent = S.of(_dialogContext).send_success(
|
||||
sendViewModel.selectedCryptoCurrency.toString());
|
||||
}
|
||||
return AlertWithOneAction(
|
||||
alertTitle: '',
|
||||
alertContent: S.of(_dialogContext).send_success(
|
||||
sendViewModel.selectedCryptoCurrency.toString()),
|
||||
alertContent: alertContent,
|
||||
buttonText: S.of(_dialogContext).ok,
|
||||
buttonAction: () {
|
||||
Navigator.of(_dialogContext).pop();
|
||||
|
||||
@@ -321,7 +321,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||
? sendViewModel.allAmountValidator
|
||||
: sendViewModel.amountValidator,
|
||||
),
|
||||
if (!sendViewModel.isBatchSending)
|
||||
if (!sendViewModel.isBatchSending && sendViewModel.shouldDisplaySendALL)
|
||||
Positioned(
|
||||
top: 2,
|
||||
right: 0,
|
||||
|
||||
@@ -107,7 +107,7 @@ class ConnectionSyncPage extends BasePage {
|
||||
);
|
||||
},
|
||||
),
|
||||
if (isEVMCompatibleChain(dashboardViewModel.wallet.type)) ...[
|
||||
if (isWalletConnectCompatibleChain(dashboardViewModel.wallet.type)) ...[
|
||||
WalletConnectTile(
|
||||
onTap: () => Navigator.of(context).pushNamed(Routes.walletConnectConnectionsListing),
|
||||
),
|
||||
|
||||
@@ -26,7 +26,7 @@ class OtherSettingsPage extends BasePage {
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Column(
|
||||
children: [
|
||||
if (!_otherSettingsViewModel.changeRepresentativeEnabled)
|
||||
if (_otherSettingsViewModel.displayTransactionPriority)
|
||||
SettingsPickerCell(
|
||||
title: S.current.settings_fee_priority,
|
||||
items: priorityForWalletType(_otherSettingsViewModel.walletType),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -26,12 +27,11 @@ class PairingItemWidget extends StatelessWidget {
|
||||
'$year-${month.toString().padLeft(2, '0')}-${day.toString().padLeft(2, '0')}';
|
||||
|
||||
return ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundImage: (metadata.icons.isNotEmpty
|
||||
? NetworkImage(metadata.icons[0])
|
||||
: const AssetImage(
|
||||
'assets/images/default_icon.png',
|
||||
)) as ImageProvider<Object>,
|
||||
leading: CakeImageWidget(
|
||||
imageUrl: metadata.icons.isNotEmpty ? metadata.icons[0]: null,
|
||||
displayOnError: CircleAvatar(
|
||||
backgroundImage: AssetImage('assets/images/default_icon.png'),
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
metadata.name,
|
||||
|
||||
@@ -103,6 +103,7 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||
final bitcoinCashIcon = Image.asset('assets/images/bch_icon.png', height: 24, width: 24);
|
||||
final nanoIcon = Image.asset('assets/images/nano_icon.png', height: 24, width: 24);
|
||||
final polygonIcon = Image.asset('assets/images/matic_icon.png', height: 24, width: 24);
|
||||
final solanaIcon = Image.asset('assets/images/sol_icon.png', height: 24, width: 24);
|
||||
final scrollController = ScrollController();
|
||||
final double tileHeight = 60;
|
||||
Flushbar<void>? _progressBar;
|
||||
@@ -313,6 +314,8 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||
return nanoIcon;
|
||||
case WalletType.polygon:
|
||||
return polygonIcon;
|
||||
case WalletType.solana:
|
||||
return solanaIcon;
|
||||
default:
|
||||
return nonWalletTypeIcon;
|
||||
}
|
||||
|
||||
+26
-6
@@ -2,25 +2,45 @@ import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
class NFTImageWidget extends StatelessWidget {
|
||||
const NFTImageWidget({
|
||||
class CakeImageWidget extends StatelessWidget {
|
||||
CakeImageWidget({
|
||||
required this.imageUrl,
|
||||
});
|
||||
Widget? displayOnError,
|
||||
this.height,
|
||||
this.width,
|
||||
}) : _displayOnError = displayOnError ?? Icon(Icons.error);
|
||||
|
||||
final String? imageUrl;
|
||||
final double? height;
|
||||
final double? width;
|
||||
final Widget? _displayOnError;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
try {
|
||||
if (imageUrl == null) return Icon(Icons.error);
|
||||
if (imageUrl == null) return _displayOnError!;
|
||||
|
||||
if (imageUrl!.contains('assets/images')) {
|
||||
return Image.asset(
|
||||
imageUrl!,
|
||||
height: height,
|
||||
width: width,
|
||||
);
|
||||
}
|
||||
|
||||
if (imageUrl!.contains('.svg')) {
|
||||
return SvgPicture.network(imageUrl!);
|
||||
return SvgPicture.network(
|
||||
imageUrl!,
|
||||
height: height,
|
||||
width: width,
|
||||
);
|
||||
}
|
||||
|
||||
return Image.network(
|
||||
imageUrl!,
|
||||
fit: BoxFit.cover,
|
||||
height: height,
|
||||
width: width,
|
||||
loadingBuilder: (BuildContext _, Widget child, ImageChunkEvent? loadingProgress) {
|
||||
if (loadingProgress == null) {
|
||||
return child;
|
||||
@@ -31,7 +51,7 @@ class NFTImageWidget extends StatelessWidget {
|
||||
errorBuilder: (_, __, ___) => Icon(Icons.error),
|
||||
);
|
||||
} catch (_) {
|
||||
return Icon(Icons.error);
|
||||
return _displayOnError!;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ abstract class AppStoreBase with Store {
|
||||
this.wallet = wallet;
|
||||
this.wallet!.setExceptionHandler(ExceptionHandler.onError);
|
||||
|
||||
if (isEVMCompatibleChain(wallet.type)) {
|
||||
if (isWalletConnectCompatibleChain(wallet.type)) {
|
||||
await getIt.get<Web3WalletService>().onDispose();
|
||||
getIt.get<Web3WalletService>().create();
|
||||
await getIt.get<Web3WalletService>().init();
|
||||
|
||||
@@ -838,6 +838,7 @@ abstract class SettingsStoreBase with Store {
|
||||
final polygonNodeId = sharedPreferences.getInt(PreferencesKey.currentPolygonNodeIdKey);
|
||||
final nanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
|
||||
final nanoPowNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoPowNodeIdKey);
|
||||
final solanaNodeId = sharedPreferences.getInt(PreferencesKey.currentSolanaNodeIdKey);
|
||||
final moneroNode = nodeSource.get(nodeId);
|
||||
final bitcoinElectrumServer = nodeSource.get(bitcoinElectrumServerId);
|
||||
final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId);
|
||||
@@ -847,6 +848,7 @@ abstract class SettingsStoreBase with Store {
|
||||
final bitcoinCashElectrumServer = nodeSource.get(bitcoinCashElectrumServerId);
|
||||
final nanoNode = nodeSource.get(nanoNodeId);
|
||||
final nanoPowNode = powNodeSource.get(nanoPowNodeId);
|
||||
final solanaNode = nodeSource.get(solanaNodeId);
|
||||
final packageInfo = await PackageInfo.fromPlatform();
|
||||
final deviceName = await _getDeviceName() ?? '';
|
||||
final shouldShowYatPopup = sharedPreferences.getBool(PreferencesKey.shouldShowYatPopup) ?? true;
|
||||
@@ -903,6 +905,10 @@ abstract class SettingsStoreBase with Store {
|
||||
powNodes[WalletType.nano] = nanoPowNode;
|
||||
}
|
||||
|
||||
if (solanaNode != null) {
|
||||
nodes[WalletType.solana] = solanaNode;
|
||||
}
|
||||
|
||||
final savedSyncMode = SyncMode.all.firstWhere((element) {
|
||||
return element.type.index == (sharedPreferences.getInt(PreferencesKey.syncModeKey) ?? 0);
|
||||
});
|
||||
@@ -1190,6 +1196,7 @@ abstract class SettingsStoreBase with Store {
|
||||
final ethereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
|
||||
final polygonNodeId = sharedPreferences.getInt(PreferencesKey.currentPolygonNodeIdKey);
|
||||
final nanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
|
||||
final solanaNodeId = sharedPreferences.getInt(PreferencesKey.currentSolanaNodeIdKey);
|
||||
final moneroNode = nodeSource.get(nodeId);
|
||||
final bitcoinElectrumServer = nodeSource.get(bitcoinElectrumServerId);
|
||||
final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId);
|
||||
@@ -1198,7 +1205,7 @@ abstract class SettingsStoreBase with Store {
|
||||
final polygonNode = nodeSource.get(polygonNodeId);
|
||||
final bitcoinCashNode = nodeSource.get(bitcoinCashElectrumServerId);
|
||||
final nanoNode = nodeSource.get(nanoNodeId);
|
||||
|
||||
final solanaNode = nodeSource.get(solanaNodeId);
|
||||
if (moneroNode != null) {
|
||||
nodes[WalletType.monero] = moneroNode;
|
||||
}
|
||||
@@ -1231,6 +1238,10 @@ abstract class SettingsStoreBase with Store {
|
||||
nodes[WalletType.nano] = nanoNode;
|
||||
}
|
||||
|
||||
if (solanaNode != null) {
|
||||
nodes[WalletType.solana] = solanaNode;
|
||||
}
|
||||
|
||||
// MIGRATED:
|
||||
|
||||
useTOTP2FA = await SecureKey.getBool(
|
||||
@@ -1358,6 +1369,9 @@ abstract class SettingsStoreBase with Store {
|
||||
case WalletType.polygon:
|
||||
await _sharedPreferences.setInt(PreferencesKey.currentPolygonNodeIdKey, node.key as int);
|
||||
break;
|
||||
case WalletType.solana:
|
||||
await _sharedPreferences.setInt(PreferencesKey.currentSolanaNodeIdKey, node.key as int);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,10 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
|
||||
final SettingsStore _settingsStore;
|
||||
|
||||
bool get hasSeedPhraseLengthOption =>
|
||||
type == WalletType.bitcoinCash || type == WalletType.ethereum;
|
||||
type == WalletType.bitcoinCash ||
|
||||
type == WalletType.ethereum ||
|
||||
type == WalletType.polygon ||
|
||||
type == WalletType.solana;
|
||||
|
||||
bool get hasSeedTypeOption => type == WalletType.monero;
|
||||
|
||||
|
||||
@@ -18,15 +18,15 @@ import 'package:mobx/mobx.dart';
|
||||
part 'balance_view_model.g.dart';
|
||||
|
||||
class BalanceRecord {
|
||||
const BalanceRecord({
|
||||
required this.availableBalance,
|
||||
required this.additionalBalance,
|
||||
required this.frozenBalance,
|
||||
required this.fiatAvailableBalance,
|
||||
required this.fiatAdditionalBalance,
|
||||
required this.fiatFrozenBalance,
|
||||
required this.asset,
|
||||
required this.formattedAssetTitle});
|
||||
const BalanceRecord(
|
||||
{required this.availableBalance,
|
||||
required this.additionalBalance,
|
||||
required this.frozenBalance,
|
||||
required this.fiatAvailableBalance,
|
||||
required this.fiatAdditionalBalance,
|
||||
required this.fiatFrozenBalance,
|
||||
required this.asset,
|
||||
required this.formattedAssetTitle});
|
||||
final String fiatAdditionalBalance;
|
||||
final String fiatAvailableBalance;
|
||||
final String fiatFrozenBalance;
|
||||
@@ -41,12 +41,10 @@ class BalanceViewModel = BalanceViewModelBase with _$BalanceViewModel;
|
||||
|
||||
abstract class BalanceViewModelBase with Store {
|
||||
BalanceViewModelBase(
|
||||
{required this.appStore,
|
||||
required this.settingsStore,
|
||||
required this.fiatConvertationStore})
|
||||
: isReversing = false,
|
||||
isShowCard = appStore.wallet!.walletInfo.isShowIntroCakePayCard,
|
||||
wallet = appStore.wallet! {
|
||||
{required this.appStore, required this.settingsStore, required this.fiatConvertationStore})
|
||||
: isReversing = false,
|
||||
isShowCard = appStore.wallet!.walletInfo.isShowIntroCakePayCard,
|
||||
wallet = appStore.wallet! {
|
||||
reaction((_) => appStore.wallet, _onWalletChange);
|
||||
}
|
||||
|
||||
@@ -60,8 +58,7 @@ abstract class BalanceViewModelBase with Store {
|
||||
bool isReversing;
|
||||
|
||||
@observable
|
||||
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>
|
||||
wallet;
|
||||
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet;
|
||||
|
||||
@computed
|
||||
double get price {
|
||||
@@ -82,7 +79,8 @@ abstract class BalanceViewModelBase with Store {
|
||||
bool get isFiatDisabled => settingsStore.fiatApiMode == FiatApiMode.disabled;
|
||||
|
||||
@computed
|
||||
bool get isHomeScreenSettingsEnabled => isEVMCompatibleChain(wallet.type);
|
||||
bool get isHomeScreenSettingsEnabled =>
|
||||
isEVMCompatibleChain(wallet.type) || wallet.type == WalletType.solana;
|
||||
|
||||
@computed
|
||||
bool get hasAccounts => wallet.type == WalletType.monero;
|
||||
@@ -97,7 +95,7 @@ abstract class BalanceViewModelBase with Store {
|
||||
String get asset {
|
||||
final typeFormatted = walletTypeToString(appStore.wallet!.type);
|
||||
|
||||
switch(wallet.type) {
|
||||
switch (wallet.type) {
|
||||
case WalletType.haven:
|
||||
return '$typeFormatted Assets';
|
||||
default:
|
||||
@@ -120,13 +118,14 @@ abstract class BalanceViewModelBase with Store {
|
||||
|
||||
@computed
|
||||
String get availableBalanceLabel {
|
||||
switch(wallet.type) {
|
||||
switch (wallet.type) {
|
||||
case WalletType.monero:
|
||||
case WalletType.haven:
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
case WalletType.solana:
|
||||
return S.current.xmr_available_balance;
|
||||
default:
|
||||
return S.current.confirmed;
|
||||
@@ -135,11 +134,12 @@ abstract class BalanceViewModelBase with Store {
|
||||
|
||||
@computed
|
||||
String get additionalBalanceLabel {
|
||||
switch(wallet.type) {
|
||||
switch (wallet.type) {
|
||||
case WalletType.monero:
|
||||
case WalletType.haven:
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.solana:
|
||||
return S.current.xmr_full_balance;
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
@@ -228,15 +228,17 @@ abstract class BalanceViewModelBase with Store {
|
||||
Map<CryptoCurrency, BalanceRecord> get balances {
|
||||
return wallet.balance.map((key, value) {
|
||||
if (displayMode == BalanceDisplayMode.hiddenBalance) {
|
||||
return MapEntry(key, BalanceRecord(
|
||||
availableBalance: '---',
|
||||
additionalBalance: '---',
|
||||
frozenBalance: '---',
|
||||
fiatAdditionalBalance: isFiatDisabled ? '' : '---',
|
||||
fiatAvailableBalance: isFiatDisabled ? '' : '---',
|
||||
fiatFrozenBalance: isFiatDisabled ? '' : '---',
|
||||
asset: key,
|
||||
formattedAssetTitle: _formatterAsset(key)));
|
||||
return MapEntry(
|
||||
key,
|
||||
BalanceRecord(
|
||||
availableBalance: '---',
|
||||
additionalBalance: '---',
|
||||
frozenBalance: '---',
|
||||
fiatAdditionalBalance: isFiatDisabled ? '' : '---',
|
||||
fiatAvailableBalance: isFiatDisabled ? '' : '---',
|
||||
fiatFrozenBalance: isFiatDisabled ? '' : '---',
|
||||
asset: key,
|
||||
formattedAssetTitle: _formatterAsset(key)));
|
||||
}
|
||||
final fiatCurrency = settingsStore.fiatCurrency;
|
||||
final price = fiatConvertationStore.prices[key] ?? 0;
|
||||
@@ -245,25 +247,23 @@ abstract class BalanceViewModelBase with Store {
|
||||
// throw Exception('Price is null for: $key');
|
||||
// }
|
||||
|
||||
final additionalFiatBalance = isFiatDisabled ? '' : (fiatCurrency.toString()
|
||||
+ ' '
|
||||
+ _getFiatBalance(
|
||||
price: price,
|
||||
cryptoAmount: value.formattedAdditionalBalance));
|
||||
final additionalFiatBalance = isFiatDisabled
|
||||
? ''
|
||||
: (fiatCurrency.toString() +
|
||||
' ' +
|
||||
_getFiatBalance(price: price, cryptoAmount: value.formattedAdditionalBalance));
|
||||
|
||||
final availableFiatBalance = isFiatDisabled ? '' : (fiatCurrency.toString()
|
||||
+ ' '
|
||||
+ _getFiatBalance(
|
||||
price: price,
|
||||
cryptoAmount: value.formattedAvailableBalance));
|
||||
|
||||
|
||||
final frozenFiatBalance = isFiatDisabled ? '' : (fiatCurrency.toString()
|
||||
+ ' '
|
||||
+ _getFiatBalance(
|
||||
price: price,
|
||||
cryptoAmount: getFormattedFrozenBalance(value)));
|
||||
final availableFiatBalance = isFiatDisabled
|
||||
? ''
|
||||
: (fiatCurrency.toString() +
|
||||
' ' +
|
||||
_getFiatBalance(price: price, cryptoAmount: value.formattedAvailableBalance));
|
||||
|
||||
final frozenFiatBalance = isFiatDisabled
|
||||
? ''
|
||||
: (fiatCurrency.toString() +
|
||||
' ' +
|
||||
_getFiatBalance(price: price, cryptoAmount: getFormattedFrozenBalance(value)));
|
||||
|
||||
return MapEntry(
|
||||
key,
|
||||
@@ -276,12 +276,22 @@ abstract class BalanceViewModelBase with Store {
|
||||
fiatFrozenBalance: frozenFiatBalance,
|
||||
asset: key,
|
||||
formattedAssetTitle: _formatterAsset(key)));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@computed
|
||||
bool get hasAdditionalBalance => !isEVMCompatibleChain(wallet.type);
|
||||
|
||||
bool get hasAdditionalBalance => _hasAdditionBalanceForWalletType(wallet.type);
|
||||
|
||||
bool _hasAdditionBalanceForWalletType(WalletType type) {
|
||||
switch (type) {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.solana:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@computed
|
||||
List<BalanceRecord> get formattedBalances {
|
||||
@@ -358,9 +368,7 @@ abstract class BalanceViewModelBase with Store {
|
||||
|
||||
@action
|
||||
void _onWalletChange(
|
||||
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>,
|
||||
TransactionInfo>?
|
||||
wallet) {
|
||||
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>? wallet) {
|
||||
if (wallet == null) {
|
||||
return;
|
||||
}
|
||||
@@ -371,7 +379,7 @@ abstract class BalanceViewModelBase with Store {
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> disableIntroCakePayCard () async {
|
||||
Future<void> disableIntroCakePayCard() async {
|
||||
const cardDisplayStatus = false;
|
||||
wallet.walletInfo.showIntroCakePayCard = cardDisplayStatus;
|
||||
await wallet.walletInfo.save();
|
||||
@@ -401,6 +409,6 @@ abstract class BalanceViewModelBase with Store {
|
||||
}
|
||||
}
|
||||
|
||||
String getFormattedFrozenBalance(Balance walletBalance) => walletBalance.formattedUnAvailableBalance;
|
||||
String getFormattedFrozenBalance(Balance walletBalance) =>
|
||||
walletBalance.formattedUnAvailableBalance;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:cake_wallet/entities/fiat_api_mode.dart';
|
||||
import 'package:cake_wallet/entities/sort_balance_types.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cake_wallet/polygon/polygon.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
@@ -16,14 +17,14 @@ class HomeSettingsViewModel = HomeSettingsViewModelBase with _$HomeSettingsViewM
|
||||
|
||||
abstract class HomeSettingsViewModelBase with Store {
|
||||
HomeSettingsViewModelBase(this._settingsStore, this._balanceViewModel)
|
||||
: tokens = ObservableSet<Erc20Token>() {
|
||||
: tokens = ObservableSet<CryptoCurrency>() {
|
||||
_updateTokensList();
|
||||
}
|
||||
|
||||
final SettingsStore _settingsStore;
|
||||
final BalanceViewModel _balanceViewModel;
|
||||
|
||||
final ObservableSet<Erc20Token> tokens;
|
||||
final ObservableSet<CryptoCurrency> tokens;
|
||||
|
||||
@observable
|
||||
String searchText = '';
|
||||
@@ -43,7 +44,7 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
@action
|
||||
void setPinNativeToken(bool value) => _settingsStore.pinNativeTokenAtTop = value;
|
||||
|
||||
Future<void> addErc20Token(Erc20Token token) async {
|
||||
Future<void> addToken(CryptoCurrency token) async {
|
||||
if (_balanceViewModel.wallet.type == WalletType.ethereum) {
|
||||
await ethereum!.addErc20Token(_balanceViewModel.wallet, token);
|
||||
}
|
||||
@@ -52,23 +53,31 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
await polygon!.addErc20Token(_balanceViewModel.wallet, token);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.solana) {
|
||||
await solana!.addSPLToken(_balanceViewModel.wallet, token);
|
||||
}
|
||||
|
||||
_updateTokensList();
|
||||
_updateFiatPrices(token);
|
||||
}
|
||||
|
||||
Future<void> deleteErc20Token(Erc20Token token) async {
|
||||
Future<void> deleteToken(CryptoCurrency token) async {
|
||||
if (_balanceViewModel.wallet.type == WalletType.ethereum) {
|
||||
await ethereum!.deleteErc20Token(_balanceViewModel.wallet, token);
|
||||
await ethereum!.deleteErc20Token(_balanceViewModel.wallet, token as Erc20Token);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.polygon) {
|
||||
await polygon!.deleteErc20Token(_balanceViewModel.wallet, token);
|
||||
await polygon!.deleteErc20Token(_balanceViewModel.wallet, token as Erc20Token);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.solana) {
|
||||
await solana!.deleteSPLToken(_balanceViewModel.wallet, token);
|
||||
}
|
||||
|
||||
_updateTokensList();
|
||||
}
|
||||
|
||||
Future<Erc20Token?> getErc20Token(String contractAddress) async {
|
||||
Future<CryptoCurrency?> getToken(String contractAddress) async {
|
||||
if (_balanceViewModel.wallet.type == WalletType.ethereum) {
|
||||
return await ethereum!.getErc20Token(_balanceViewModel.wallet, contractAddress);
|
||||
}
|
||||
@@ -77,12 +86,16 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
return await polygon!.getErc20Token(_balanceViewModel.wallet, contractAddress);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.solana) {
|
||||
return await solana!.getSPLToken(_balanceViewModel.wallet, contractAddress);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
CryptoCurrency get nativeToken => _balanceViewModel.wallet.currency;
|
||||
|
||||
void _updateFiatPrices(Erc20Token token) async {
|
||||
void _updateFiatPrices(CryptoCurrency token) async {
|
||||
try {
|
||||
_balanceViewModel.fiatConvertationStore.prices[token] =
|
||||
await FiatConversionService.fetchPrice(
|
||||
@@ -92,20 +105,27 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
void changeTokenAvailability(Erc20Token token, bool value) async {
|
||||
void changeTokenAvailability(CryptoCurrency token, bool value) async {
|
||||
token.enabled = value;
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.ethereum) {
|
||||
ethereum!.addErc20Token(_balanceViewModel.wallet, token);
|
||||
ethereum!.addErc20Token(_balanceViewModel.wallet, token as Erc20Token);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.polygon) {
|
||||
polygon!.addErc20Token(_balanceViewModel.wallet, token);
|
||||
polygon!.addErc20Token(_balanceViewModel.wallet, token as Erc20Token);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.solana) {
|
||||
solana!.addSPLToken(_balanceViewModel.wallet, token);
|
||||
}
|
||||
|
||||
_refreshTokensList();
|
||||
}
|
||||
|
||||
@action
|
||||
void _updateTokensList() {
|
||||
int _sortFunc(Erc20Token e1, Erc20Token e2) {
|
||||
int _sortFunc(CryptoCurrency e1, CryptoCurrency e2) {
|
||||
int index1 = _balanceViewModel.formattedBalances.indexWhere((element) => element.asset == e1);
|
||||
int index2 = _balanceViewModel.formattedBalances.indexWhere((element) => element.asset == e2);
|
||||
|
||||
@@ -138,6 +158,14 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
.toList()
|
||||
..sort(_sortFunc));
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.solana) {
|
||||
tokens.addAll(solana!
|
||||
.getSPLTokenCurrencies(_balanceViewModel.wallet)
|
||||
.where((element) => _matchesSearchText(element))
|
||||
.toList()
|
||||
..sort(_sortFunc));
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
@@ -153,10 +181,32 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
_updateTokensList();
|
||||
}
|
||||
|
||||
bool _matchesSearchText(Erc20Token asset) {
|
||||
bool _matchesSearchText(CryptoCurrency asset) {
|
||||
final address = getTokenAddressBasedOnWallet(asset);
|
||||
|
||||
// The homes settings would only be displayed for either of Ethereum, Polygon or Solana Wallets.
|
||||
if (address == null) return false;
|
||||
|
||||
return searchText.isEmpty ||
|
||||
asset.fullName!.toLowerCase().contains(searchText.toLowerCase()) ||
|
||||
asset.title.toLowerCase().contains(searchText.toLowerCase()) ||
|
||||
asset.contractAddress == searchText;
|
||||
address == searchText;
|
||||
}
|
||||
|
||||
String? getTokenAddressBasedOnWallet(CryptoCurrency asset) {
|
||||
if (_balanceViewModel.wallet.type == WalletType.solana) {
|
||||
return solana!.getTokenAddress(asset);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.ethereum) {
|
||||
return ethereum!.getTokenAddress(asset);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.polygon) {
|
||||
return polygon!.getTokenAddress(asset);
|
||||
}
|
||||
|
||||
// We return null if it's neither Polygin, Ethereum or Solana wallet (which is actually impossible because we only display home settings for either of these three wallets).
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/nano/nano.dart';
|
||||
import 'package:cake_wallet/polygon/polygon.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
@@ -105,6 +106,14 @@ class TransactionListItem extends ActionListItem with Keyable {
|
||||
nano!.getTransactionAmountRaw(transaction).toString(), nanoUtil!.rawPerNano)),
|
||||
price: price);
|
||||
break;
|
||||
case WalletType.solana:
|
||||
final asset = solana!.assetOfTransaction(balanceViewModel.wallet, transaction);
|
||||
final price = balanceViewModel.fiatConvertationStore.prices[asset];
|
||||
amount = calculateFiatAmountRaw(
|
||||
cryptoAmount: solana!.getTransactionAmountRaw(transaction),
|
||||
price: price,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -625,6 +625,10 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
depositCurrency = CryptoCurrency.maticpoly;
|
||||
receiveCurrency = CryptoCurrency.xmr;
|
||||
break;
|
||||
case WalletType.solana:
|
||||
depositCurrency = CryptoCurrency.sol;
|
||||
receiveCurrency = CryptoCurrency.xmr;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -75,6 +75,9 @@ abstract class NodeListViewModelBase with Store {
|
||||
case WalletType.polygon:
|
||||
node = getPolygonDefaultNode(nodes: _nodeSource)!;
|
||||
break;
|
||||
case WalletType.solana:
|
||||
node = getSolanaDefaultNode(nodes: _nodeSource)!;
|
||||
break;
|
||||
default:
|
||||
throw Exception('Unexpected wallet type: ${_appStore.wallet!.type}');
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.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/view_model/restore/restore_mode.dart';
|
||||
import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
@@ -75,6 +76,9 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store
|
||||
case WalletType.polygon:
|
||||
return polygon!.createPolygonRestoreWalletFromPrivateKey(
|
||||
name: name, password: password, privateKey: restoreWallet.privateKey!);
|
||||
case WalletType.solana:
|
||||
return solana!.createSolanaRestoreWalletFromPrivateKey(
|
||||
name: name, password: password, privateKey: restoreWallet.privateKey!);
|
||||
default:
|
||||
throw Exception('Unexpected type: ${restoreWallet.type.toString()}');
|
||||
}
|
||||
@@ -102,6 +106,9 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store
|
||||
case WalletType.polygon:
|
||||
return polygon!.createPolygonRestoreWalletFromSeedCredentials(
|
||||
name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password);
|
||||
case WalletType.solana:
|
||||
return solana!.createSolanaRestoreWalletFromSeedCredentials(
|
||||
name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password);
|
||||
default:
|
||||
throw Exception('Unexpected type: ${type.toString()}');
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ class WalletRestoreFromQRCode {
|
||||
'bitcoincash': WalletType.bitcoinCash,
|
||||
'bitcoincash-wallet': WalletType.bitcoinCash,
|
||||
'bitcoincash_wallet': WalletType.bitcoinCash,
|
||||
'solana-wallet': WalletType.solana,
|
||||
};
|
||||
|
||||
static bool _containsAssetSpecifier(String code) => _extractWalletType(code) != null;
|
||||
@@ -175,6 +176,14 @@ class WalletRestoreFromQRCode {
|
||||
return WalletRestoreMode.seed;
|
||||
}
|
||||
|
||||
if (type == WalletType.solana && credentials.containsKey('private_key')) {
|
||||
final privateKey = credentials['private_key'] as String;
|
||||
if (privateKey.isEmpty) {
|
||||
throw Exception('Unexpected restore mode: private_key');
|
||||
}
|
||||
return WalletRestoreMode.keys;
|
||||
}
|
||||
|
||||
throw Exception('Unexpected restore mode: restore params are invalid');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,9 +148,8 @@ abstract class OutputBase with Store {
|
||||
@computed
|
||||
String get estimatedFeeFiatAmount {
|
||||
try {
|
||||
final currency = isEVMCompatibleChain(_wallet.type)
|
||||
? _wallet.currency
|
||||
: cryptoCurrencyHandler();
|
||||
final currency =
|
||||
isEVMCompatibleChain(_wallet.type) ? _wallet.currency : cryptoCurrencyHandler();
|
||||
final fiat = calculateFiatAmountRaw(
|
||||
price: _fiatConversationStore.prices[currency]!, cryptoAmount: estimatedFee);
|
||||
return fiat;
|
||||
@@ -220,7 +219,6 @@ abstract class OutputBase with Store {
|
||||
final crypto = double.parse(fiatAmount.replaceAll(',', '.')) /
|
||||
_fiatConversationStore.prices[cryptoCurrencyHandler()]!;
|
||||
final cryptoAmountTmp = _cryptoNumberFormat.format(crypto);
|
||||
|
||||
if (cryptoAmount != cryptoAmountTmp) {
|
||||
cryptoAmount = cryptoAmountTmp;
|
||||
}
|
||||
@@ -252,6 +250,9 @@ abstract class OutputBase with Store {
|
||||
case WalletType.polygon:
|
||||
maximumFractionDigits = 12;
|
||||
break;
|
||||
case WalletType.solana:
|
||||
maximumFractionDigits = 12;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,8 @@ abstract class SendTemplateViewModelBase with Store {
|
||||
bool get hasMultiRecipient =>
|
||||
_wallet.type != WalletType.haven &&
|
||||
_wallet.type != WalletType.ethereum &&
|
||||
_wallet.type != WalletType.polygon;
|
||||
_wallet.type != WalletType.polygon &&
|
||||
_wallet.type != WalletType.solana;
|
||||
|
||||
@computed
|
||||
CryptoCurrency get cryptoCurrency => _wallet.currency;
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:cake_wallet/entities/contact_record.dart';
|
||||
import 'package:cake_wallet/entities/wallet_contact.dart';
|
||||
import 'package:cake_wallet/polygon/polygon.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
|
||||
@@ -44,7 +45,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
void onWalletChange(wallet) {
|
||||
currencies = wallet.balance.keys.toList();
|
||||
selectedCryptoCurrency = wallet.currency;
|
||||
hasMultipleTokens = isEVMCompatibleChain(wallet.type);
|
||||
hasMultipleTokens = isEVMCompatibleChain(wallet.type) || wallet.type == WalletType.solana;
|
||||
}
|
||||
|
||||
SendViewModelBase(
|
||||
@@ -57,7 +58,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
) : state = InitialExecutionState(),
|
||||
currencies = appStore.wallet!.balance.keys.toList(),
|
||||
selectedCryptoCurrency = appStore.wallet!.currency,
|
||||
hasMultipleTokens = isEVMCompatibleChain(appStore.wallet!.type),
|
||||
hasMultipleTokens = isEVMCompatibleChain(appStore.wallet!.type) ||
|
||||
appStore.wallet!.type == WalletType.solana,
|
||||
outputs = ObservableList<Output>(),
|
||||
_settingsStore = appStore.settingsStore,
|
||||
fiatFromSettings = appStore.settingsStore.fiatCurrency,
|
||||
@@ -100,6 +102,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
@computed
|
||||
bool get isBatchSending => outputs.length > 1;
|
||||
|
||||
bool get shouldDisplaySendALL => walletType != WalletType.solana;
|
||||
|
||||
@computed
|
||||
String get pendingTransactionFiatAmount {
|
||||
if (pendingTransaction == null) {
|
||||
@@ -297,6 +301,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
pendingTransaction = await wallet.createTransaction(_credentials());
|
||||
state = ExecutedSuccessfullyState();
|
||||
} catch (e) {
|
||||
print('Failed with ${e.toString()}');
|
||||
state = FailureState(e.toString());
|
||||
}
|
||||
}
|
||||
@@ -351,7 +356,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
Object _credentials() {
|
||||
final priority = _settingsStore.priority[wallet.type];
|
||||
|
||||
if (priority == null && wallet.type != WalletType.nano) {
|
||||
if (priority == null && wallet.type != WalletType.nano && wallet.type != WalletType.solana) {
|
||||
throw Exception('Priority is null for wallet type: ${wallet.type}');
|
||||
}
|
||||
|
||||
@@ -377,6 +382,9 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
case WalletType.polygon:
|
||||
return polygon!.createPolygonTransactionCredentials(outputs,
|
||||
priority: priority!, currency: selectedCryptoCurrency);
|
||||
case WalletType.solana:
|
||||
return solana!
|
||||
.createSolanaTransactionCredentials(outputs, currency: selectedCryptoCurrency);
|
||||
default:
|
||||
throw Exception('Unexpected wallet type: ${wallet.type}');
|
||||
}
|
||||
|
||||
@@ -14,15 +14,14 @@ import 'package:package_info/package_info.dart';
|
||||
|
||||
part 'other_settings_view_model.g.dart';
|
||||
|
||||
class OtherSettingsViewModel = OtherSettingsViewModelBase
|
||||
with _$OtherSettingsViewModel;
|
||||
class OtherSettingsViewModel = OtherSettingsViewModelBase with _$OtherSettingsViewModel;
|
||||
|
||||
abstract class OtherSettingsViewModelBase with Store {
|
||||
OtherSettingsViewModelBase(this._settingsStore, this._wallet)
|
||||
: walletType = _wallet.type,
|
||||
currentVersion = '' {
|
||||
PackageInfo.fromPlatform().then(
|
||||
(PackageInfo packageInfo) => currentVersion = packageInfo.version);
|
||||
PackageInfo.fromPlatform()
|
||||
.then((PackageInfo packageInfo) => currentVersion = packageInfo.version);
|
||||
|
||||
final priority = _settingsStore.priority[_wallet.type];
|
||||
final priorities = priorityForWalletType(_wallet.type);
|
||||
@@ -33,8 +32,7 @@ abstract class OtherSettingsViewModelBase with Store {
|
||||
}
|
||||
|
||||
final WalletType walletType;
|
||||
final WalletBase<Balance, TransactionHistoryBase<TransactionInfo>,
|
||||
TransactionInfo> _wallet;
|
||||
final WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> _wallet;
|
||||
|
||||
@observable
|
||||
String currentVersion;
|
||||
@@ -57,12 +55,14 @@ abstract class OtherSettingsViewModelBase with Store {
|
||||
_wallet.type == WalletType.nano || _wallet.type == WalletType.banano;
|
||||
|
||||
@computed
|
||||
bool get isEnabledBuyAction =>
|
||||
!_settingsStore.disableBuy && _wallet.type != WalletType.haven;
|
||||
bool get displayTransactionPriority =>
|
||||
!(changeRepresentativeEnabled || _wallet.type == WalletType.solana);
|
||||
|
||||
@computed
|
||||
bool get isEnabledSellAction =>
|
||||
!_settingsStore.disableSell && _wallet.type != WalletType.haven;
|
||||
bool get isEnabledBuyAction => !_settingsStore.disableBuy && _wallet.type != WalletType.haven;
|
||||
|
||||
@computed
|
||||
bool get isEnabledSellAction => !_settingsStore.disableSell && _wallet.type != WalletType.haven;
|
||||
|
||||
List<ProviderType> get availableBuyProvidersTypes {
|
||||
return ProvidersHelper.getAvailableBuyProviderTypes(walletType);
|
||||
@@ -72,12 +72,10 @@ abstract class OtherSettingsViewModelBase with Store {
|
||||
ProvidersHelper.getAvailableSellProviderTypes(walletType);
|
||||
|
||||
ProviderType get buyProviderType =>
|
||||
_settingsStore.defaultBuyProviders[walletType] ??
|
||||
ProviderType.askEachTime;
|
||||
_settingsStore.defaultBuyProviders[walletType] ?? ProviderType.askEachTime;
|
||||
|
||||
ProviderType get sellProviderType =>
|
||||
_settingsStore.defaultSellProviders[walletType] ??
|
||||
ProviderType.askEachTime;
|
||||
_settingsStore.defaultSellProviders[walletType] ?? ProviderType.askEachTime;
|
||||
|
||||
String getDisplayPriority(dynamic priority) {
|
||||
final _priority = priority as TransactionPriority;
|
||||
@@ -114,7 +112,6 @@ abstract class OtherSettingsViewModelBase with Store {
|
||||
_settingsStore.defaultBuyProviders[walletType] = buyProviderType;
|
||||
|
||||
@action
|
||||
ProviderType onSellProviderTypeSelected(
|
||||
ProviderType sellProviderType) =>
|
||||
ProviderType onSellProviderTypeSelected(ProviderType sellProviderType) =>
|
||||
_settingsStore.defaultSellProviders[walletType] = sellProviderType;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,9 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
case WalletType.polygon:
|
||||
_addPolygonListItems(tx, dateFormat);
|
||||
break;
|
||||
case WalletType.solana:
|
||||
_addSolanaListItems(tx, dateFormat);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -131,6 +134,8 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
return 'https://bananolooker.com/block/${txId}';
|
||||
case WalletType.polygon:
|
||||
return 'https://polygonscan.com/tx/${txId}';
|
||||
case WalletType.solana:
|
||||
return 'https://solscan.io/tx/${txId}';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
@@ -155,6 +160,8 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
return S.current.view_transaction_on + 'bananolooker.com';
|
||||
case WalletType.polygon:
|
||||
return S.current.view_transaction_on + 'polygonscan.com';
|
||||
case WalletType.solana:
|
||||
return S.current.view_transaction_on + 'solscan.io';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
@@ -281,4 +288,21 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
|
||||
items.addAll(_items);
|
||||
}
|
||||
|
||||
void _addSolanaListItems(TransactionInfo tx, DateFormat dateFormat) {
|
||||
final _items = [
|
||||
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id),
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_date, value: dateFormat.format(tx.date)),
|
||||
StandartListItem(title: S.current.transaction_details_amount, value: tx.amountFormatted()),
|
||||
if (tx.feeFormatted()?.isNotEmpty ?? false)
|
||||
StandartListItem(title: S.current.transaction_details_fee, value: tx.feeFormatted()!),
|
||||
if (showRecipientAddress && tx.to != null)
|
||||
StandartListItem(title: S.current.transaction_details_recipient_address, value: tx.to!),
|
||||
if (tx.from != null)
|
||||
StandartListItem(title: S.current.transaction_details_source_address, value: tx.from!),
|
||||
];
|
||||
|
||||
items.addAll(_items);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ 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/polygon/polygon.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
@@ -159,6 +160,21 @@ class PolygonURI extends PaymentURI {
|
||||
}
|
||||
}
|
||||
|
||||
class SolanaURI extends PaymentURI {
|
||||
SolanaURI({required String amount, required String address})
|
||||
: super(amount: amount, address: address);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
var base = 'solana:' + address;
|
||||
if (amount.isNotEmpty) {
|
||||
base += '?amount=${amount.replaceAll(',', '.')}';
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewModel with Store {
|
||||
WalletAddressListViewModelBase({
|
||||
required AppStore appStore,
|
||||
@@ -257,6 +273,10 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
return PolygonURI(amount: amount, address: address.address);
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.solana) {
|
||||
return SolanaURI(amount: amount, address: address.address);
|
||||
}
|
||||
|
||||
throw Exception('Unexpected type: ${type.toString()}');
|
||||
}
|
||||
|
||||
@@ -326,6 +346,12 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.solana) {
|
||||
final primaryAddress = solana!.getAddress(wallet);
|
||||
|
||||
addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
|
||||
}
|
||||
|
||||
if (searchText.isNotEmpty) {
|
||||
return ObservableList.of(addressList.where((item) {
|
||||
if (item is WalletAddressListItem) {
|
||||
|
||||
@@ -110,7 +110,8 @@ abstract class WalletKeysViewModelBase with Store {
|
||||
]);
|
||||
}
|
||||
|
||||
if (isEVMCompatibleChain(_appStore.wallet!.type)) {
|
||||
if (isEVMCompatibleChain(_appStore.wallet!.type) ||
|
||||
_appStore.wallet!.type == WalletType.solana) {
|
||||
items.addAll([
|
||||
if (_appStore.wallet!.privateKey != null)
|
||||
StandartListItem(title: S.current.private_key, value: _appStore.wallet!.privateKey!),
|
||||
@@ -165,6 +166,8 @@ abstract class WalletKeysViewModelBase with Store {
|
||||
return 'banano-wallet';
|
||||
case WalletType.polygon:
|
||||
return 'polygon-wallet';
|
||||
case WalletType.solana:
|
||||
return 'solana-wallet';
|
||||
default:
|
||||
throw Exception('Unexpected wallet type: ${_appStore.wallet!.toString()}');
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
@@ -36,19 +37,21 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
|
||||
bool get hasLanguageSelector => type == WalletType.monero || type == WalletType.haven;
|
||||
|
||||
int get seedPhraseWordsLength {
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
if(advancedPrivacySettingsViewModel.isPolySeed) {
|
||||
return 16;
|
||||
}
|
||||
return 25;
|
||||
case WalletType.ethereum:
|
||||
case WalletType.bitcoinCash:
|
||||
return advancedPrivacySettingsViewModel.seedPhraseLength.value;
|
||||
default:
|
||||
return 24;
|
||||
}
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
if (advancedPrivacySettingsViewModel.isPolySeed) {
|
||||
return 16;
|
||||
}
|
||||
return 25;
|
||||
case WalletType.solana:
|
||||
case WalletType.polygon:
|
||||
case WalletType.ethereum:
|
||||
case WalletType.bitcoinCash:
|
||||
return advancedPrivacySettingsViewModel.seedPhraseLength.value;
|
||||
default:
|
||||
return 24;
|
||||
}
|
||||
}
|
||||
|
||||
bool get hasSeedType => type == WalletType.monero;
|
||||
|
||||
@@ -64,8 +67,8 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
|
||||
case WalletType.litecoin:
|
||||
return bitcoin!.createBitcoinNewWalletCredentials(name: name);
|
||||
case WalletType.haven:
|
||||
return haven!.createHavenNewWalletCredentials(
|
||||
name: name, language: options!.first as String);
|
||||
return haven!
|
||||
.createHavenNewWalletCredentials(name: name, language: options!.first as String);
|
||||
case WalletType.ethereum:
|
||||
return ethereum!.createEthereumNewWalletCredentials(name: name);
|
||||
case WalletType.bitcoinCash:
|
||||
@@ -74,6 +77,8 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
|
||||
return nano!.createNanoNewWalletCredentials(name: name);
|
||||
case WalletType.polygon:
|
||||
return polygon!.createPolygonNewWalletCredentials(name: name);
|
||||
case WalletType.solana:
|
||||
return solana!.createSolanaNewWalletCredentials(name: name);
|
||||
default:
|
||||
throw Exception('Unexpected type: ${type.toString()}');
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:cake_wallet/nano/nano.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
||||
import 'package:cake_wallet/polygon/polygon.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
@@ -28,11 +29,11 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||
{required WalletType type})
|
||||
: hasSeedLanguageSelector = type == WalletType.monero || type == WalletType.haven,
|
||||
hasBlockchainHeightLanguageSelector = type == WalletType.monero || type == WalletType.haven,
|
||||
hasRestoreFromPrivateKey =
|
||||
type == WalletType.ethereum ||
|
||||
hasRestoreFromPrivateKey = type == WalletType.ethereum ||
|
||||
type == WalletType.polygon ||
|
||||
type == WalletType.nano ||
|
||||
type == WalletType.banano,
|
||||
type == WalletType.banano ||
|
||||
type == WalletType.solana,
|
||||
isButtonEnabled = false,
|
||||
mode = WalletRestoreMode.seed,
|
||||
super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: true) {
|
||||
@@ -45,6 +46,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||
break;
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
case WalletType.solana:
|
||||
availableModes = [WalletRestoreMode.seed, WalletRestoreMode.keys];
|
||||
break;
|
||||
default:
|
||||
@@ -98,22 +100,21 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||
name: name, height: height, mnemonic: seed, password: password);
|
||||
case WalletType.ethereum:
|
||||
return ethereum!.createEthereumRestoreWalletFromSeedCredentials(
|
||||
name: name,
|
||||
mnemonic: seed,
|
||||
password: password);
|
||||
name: name, mnemonic: seed, password: password);
|
||||
case WalletType.bitcoinCash:
|
||||
return bitcoinCash!.createBitcoinCashRestoreWalletFromSeedCredentials(
|
||||
name: name,
|
||||
mnemonic: seed,
|
||||
password: password);
|
||||
name: name, mnemonic: seed, password: password);
|
||||
case WalletType.nano:
|
||||
return nano!.createNanoRestoreWalletFromSeedCredentials(
|
||||
name: name, mnemonic: seed, password: password, derivationType: derivationType);
|
||||
case WalletType.polygon:
|
||||
return polygon!.createPolygonRestoreWalletFromSeedCredentials(
|
||||
name: name,
|
||||
mnemonic: seed,
|
||||
password: password,
|
||||
derivationType: derivationType);
|
||||
case WalletType.polygon:
|
||||
return polygon!.createPolygonRestoreWalletFromSeedCredentials(
|
||||
);
|
||||
case WalletType.solana:
|
||||
return solana!.createSolanaRestoreWalletFromSeedCredentials(
|
||||
name: name,
|
||||
mnemonic: seed,
|
||||
password: password,
|
||||
@@ -160,16 +161,22 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||
|
||||
case WalletType.nano:
|
||||
return nano!.createNanoRestoreWalletFromKeysCredentials(
|
||||
name: name,
|
||||
password: password,
|
||||
seedKey: options['private_key'] as String,
|
||||
derivationType: options["derivationType"] as DerivationType);
|
||||
name: name,
|
||||
password: password,
|
||||
seedKey: options['private_key'] as String,
|
||||
derivationType: options["derivationType"] as DerivationType);
|
||||
case WalletType.polygon:
|
||||
return polygon!.createPolygonRestoreWalletFromPrivateKey(
|
||||
name: name,
|
||||
password: password,
|
||||
privateKey: options['private_key'] as String,
|
||||
);
|
||||
case WalletType.solana:
|
||||
return solana!.createSolanaRestoreWalletFromPrivateKey(
|
||||
name: name,
|
||||
password: password,
|
||||
privateKey: options['private_key'] as String,
|
||||
);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -187,10 +194,8 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||
|
||||
switch (type) {
|
||||
case WalletType.nano:
|
||||
return nanoUtil!.compareDerivationMethods(
|
||||
mnemonic: mnemonic,
|
||||
privateKey: seedKey,
|
||||
node: node);
|
||||
return nanoUtil!
|
||||
.compareDerivationMethods(mnemonic: mnemonic, privateKey: seedKey, node: node);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user