Solana Wallet New Implementation ()

* Feat: Implement Solana wallet using on_chain

* v4.23.0 release candidate ()

* v4.23.0 release candidate

* - Fix restoring zano from QR
- Fix Zano confirmations count
- Fix birdpay
- Fix balance display

* Fix Zano assets showing amount before they are added

* - handle fetching token data while the API is busy
- potential fix for duplicate transactions

* fix receive confirmations, maybe

* revert onChangeWallet cleanup

* Fix confirmations not updating

* improve zano wallet opening, fix CI commands and messages on slack ()

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>

* Cache wallet when creating/restoring as well

* - hardcode Trocador Maximum limit for Zano temporarily
- Configure Cake Zano node to use SSL

* reformatting [skip ci]

* revert to non-ssl

* update build numbers [skip ci]

* disable zano for desktop [skip ci]

---------

Co-authored-by: cyan <cyjan@mrcyjanek.net>

* CW-711 passphrase for XMR/WOWcreation ()

* add monero passphrase
add wownero passphrase
add passphrase to seed screen

* obscure passphrase by default
disable passphrase create for zano

* Update lib/view_model/wallet_keys_view_model.dart [skip ci]

* Update lib/src/screens/wallet_keys/wallet_keys_page.dart [skip ci]

* Update lib/view_model/advanced_privacy_settings_view_model.dart

* dynamic passphrase icon

* fix polyseed not being encrypted by passphrase

---------

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>

* show Zano keys properly in the keys tab ()

* fix: Switch private key hex encoding

* Modified existing implementation to use older version of packages

* fix: Fetch direct transaction history amounts instead of decimals, and add Create Account Instructions to Transaction History List

* fix: Remove Create Account entries in Transaction History and disable activating token accounts of selected tokens

* feat: Add passphrase support to Solana

* fix: Issues with transaction amount and dissappearing transaction history items (very annoying bug)

* fix: Issue with flipping transactions and incorrect transaction status

* PR Review fixes

---------

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
Co-authored-by: cyan <cyjan@mrcyjanek.net>
This commit is contained in:
David Adegoke 2025-03-14 15:42:17 +01:00 committed by GitHub
parent 1c29be7993
commit 1b5be705f6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 716 additions and 561 deletions

View file

@ -0,0 +1,26 @@
import 'dart:convert';
import 'package:http/http.dart';
import 'package:on_chain/solana/solana.dart';
class SolanaRPCHTTPService implements SolanaJSONRPCService {
SolanaRPCHTTPService(
{required this.url, Client? client, this.defaultRequestTimeout = const Duration(seconds: 30)})
: client = client ?? Client();
@override
final String url;
final Client client;
final Duration defaultRequestTimeout;
@override
Future<Map<String, dynamic>> call(SolanaRequestDetails params, [Duration? timeout]) async {
final response = await client.post(
Uri.parse(url),
body: params.toRequestBody(),
headers: {
'Content-Type': 'application/json',
},
).timeout(timeout ?? defaultRequestTimeout);
final data = json.decode(response.body) as Map<String, dynamic>;
return data;
}
}

View file

@ -27,6 +27,10 @@ dependencies:
version: 1.0.0
socks5_proxy: ^1.0.4
unorm_dart: ^0.3.0
on_chain:
git:
url: https://github.com/cake-tech/on_chain.git
ref: cake-update-v2
# tor:
# git:
# url: https://github.com/cake-tech/tor.git

View file

@ -26,7 +26,7 @@ class DefaultSPLTokens {
decimal: 5,
mint: 'Bonk',
iconPath: 'assets/images/bonk_icon.png',
enabled: true,
enabled: false,
),
SPLToken(
name: 'Raydium',
@ -35,7 +35,7 @@ class DefaultSPLTokens {
decimal: 6,
mint: 'ray',
iconPath: 'assets/images/ray_icon.png',
enabled: true,
enabled: false,
),
SPLToken(
name: 'Wrapped Ethereum (Sollet)',

View file

@ -1,9 +1,8 @@
import 'package:cw_core/pending_transaction.dart';
import 'package:solana/encoder.dart';
class PendingSolanaTransaction with PendingTransaction {
final double amount;
final SignedTx signedTransaction;
final String serializedTransaction;
final String destinationAddress;
final Function sendTransaction;
final double fee;
@ -11,7 +10,7 @@ class PendingSolanaTransaction with PendingTransaction {
PendingSolanaTransaction({
required this.fee,
required this.amount,
required this.signedTransaction,
required this.serializedTransaction,
required this.destinationAddress,
required this.sendTransaction,
});
@ -36,7 +35,7 @@ class PendingSolanaTransaction with PendingTransaction {
String get feeFormatted => fee.toString();
@override
String get hex => signedTransaction.encode();
String get hex => serializedTransaction;
@override
String get id => '';

File diff suppressed because it is too large Load diff

View file

@ -34,7 +34,9 @@ class SolanaTransactionInfo extends TransactionInfo {
@override
String amountFormatted() {
String stringBalance = solAmount.toString();
if (stringBalance.toString().length >= 12) {
stringBalance = stringBalance.substring(0, 12);
}
return '$stringBalance $tokenSymbol';
}

View file

@ -30,9 +30,9 @@ import 'package:hex/hex.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:solana/base58.dart';
import 'package:solana/metaplex.dart' as metaplex;
import 'package:solana/solana.dart';
import 'package:on_chain/solana/solana.dart' hide Store;
import 'package:bip39/bip39.dart' as bip39;
import 'package:blockchain_utils/blockchain_utils.dart';
part 'solana_wallet.g.dart';
@ -77,14 +77,6 @@ abstract class SolanaWalletBase
final String? _hexPrivateKey;
final EncryptionFileUtils encryptionFileUtils;
// The Solana WalletPair
Ed25519HDKeyPair? _walletKeyPair;
Ed25519HDKeyPair? get walletKeyPair => _walletKeyPair;
// To access the privateKey bytes.
Ed25519HDKeyPairData? _keyPairData;
late final SolanaWalletClient _client;
@observable
@ -108,29 +100,23 @@ abstract class SolanaWalletBase
final Completer<SharedPreferences> _sharedPrefs = Completer();
@override
Ed25519HDKeyPairData get keys {
if (_keyPairData == null) {
return Ed25519HDKeyPairData([], publicKey: const Ed25519HDPublicKey([]));
}
Object get keys => throw UnimplementedError("keys");
return _keyPairData!;
}
late final SolanaPrivateKey _solanaPrivateKey;
late final SolanaPublicKey _solanaPublicKey;
SolanaPublicKey get solanaPublicKey => _solanaPublicKey;
SolanaPrivateKey get solanaPrivateKey => _solanaPrivateKey;
String get solanaAddress => _solanaPublicKey.toAddress().address;
@override
String? get seed => _mnemonic;
@override
String get privateKey {
final privateKeyBytes = _keyPairData!.bytes;
final publicKeyBytes = _keyPairData!.publicKey.bytes;
final encodedBytes = privateKeyBytes + publicKeyBytes;
final privateKey = base58encode(encodedBytes);
return privateKey;
}
String get privateKey => _solanaPrivateKey.seedHex();
@override
WalletKeysData get walletKeysData => WalletKeysData(mnemonic: _mnemonic, privateKey: privateKey);
@ -140,35 +126,47 @@ abstract class SolanaWalletBase
splTokensBox = await CakeHive.openBox<SPLToken>(boxName);
// Create WalletPair using either the mnemonic or the privateKey
_walletKeyPair = await getWalletPair(
// Create the privatekey using either the mnemonic or the privateKey
_solanaPrivateKey = await getPrivateKey(
mnemonic: _mnemonic,
privateKey: _hexPrivateKey,
passphrase: passphrase,
);
// Extract the keyPairData containing both the privateKey bytes and the publicKey hex.
_keyPairData = await _walletKeyPair!.extract();
// Extract the public key and wallet address
_solanaPublicKey = _solanaPrivateKey.publicKey();
walletInfo.address = _walletKeyPair!.address;
walletInfo.address = _solanaPublicKey.toAddress().address;
await walletAddresses.init();
await transactionHistory.init();
await save();
}
Future<Wallet> getWalletPair({String? mnemonic, String? privateKey}) async {
Future<SolanaPrivateKey> getPrivateKey({
String? mnemonic,
String? privateKey,
String? passphrase,
}) async {
assert(mnemonic != null || privateKey != null);
if (mnemonic != null) {
return Wallet.fromMnemonic(mnemonic, account: 0, change: 0);
final seed = bip39.mnemonicToSeed(mnemonic, passphrase: passphrase ?? '');
// Derive a Solana private key from the seed
final bip44 = Bip44.fromSeed(seed, Bip44Coins.solana);
final childKey = bip44.deriveDefaultPath.change(Bip44Changes.chainExt);
return SolanaPrivateKey.fromSeed(childKey.privateKey.raw);
}
try {
final privateKeyBytes = base58decode(privateKey!);
return await Wallet.fromPrivateKeyBytes(privateKey: privateKeyBytes.take(32).toList());
final keypairBytes = Base58Decoder.decode(privateKey!);
return SolanaPrivateKey.fromSeed(keypairBytes);
} catch (_) {
final privateKeyBytes = HEX.decode(privateKey!);
return await Wallet.fromPrivateKeyBytes(privateKey: privateKeyBytes);
return SolanaPrivateKey.fromBytes(privateKeyBytes);
}
}
@ -206,7 +204,8 @@ abstract class SolanaWalletBase
Future<void> _getEstimatedFees() async {
try {
estimatedFee = await _client.getEstimatedFee(_walletKeyPair!);
estimatedFee = await _client.getEstimatedFee(_solanaPublicKey, Commitment.confirmed);
printV(estimatedFee.toString());
} catch (e) {
estimatedFee = 0.0;
}
@ -274,7 +273,7 @@ abstract class SolanaWalletBase
tokenMint: tokenMint,
tokenTitle: transactionCurrency.title,
inputAmount: totalAmount,
ownerKeypair: _walletKeyPair!,
ownerPrivateKey: _solanaPrivateKey,
tokenDecimals: transactionCurrency.decimals,
destinationAddress: solCredentials.outputs.first.isParsedAddress
? solCredentials.outputs.first.extractedAddress!
@ -291,9 +290,7 @@ abstract class SolanaWalletBase
/// Fetches the native SOL transactions linked to the wallet Public Key
Future<void> _updateNativeSOLTransactions() async {
final address = Ed25519HDPublicKey.fromBase58(_walletKeyPair!.address);
final transactions = await _client.fetchTransactions(address);
final transactions = await _client.fetchTransactions(_solanaPublicKey.toAddress());
await _addTransactionsToTransactionHistory(transactions);
}
@ -308,10 +305,10 @@ abstract class SolanaWalletBase
for (var token in tokenKeys) {
if (token is SPLToken) {
final tokenTxs = await _client.getSPLTokenTransfers(
token.mintAddress,
token.symbol,
token.decimal,
_walletKeyPair!,
mintAddress: token.mintAddress,
splTokenSymbol: token.symbol,
splTokenDecimal: token.decimal,
privateKey: _solanaPrivateKey,
);
// splTokenTransactions.addAll(tokenTxs);
@ -387,6 +384,7 @@ abstract class SolanaWalletBase
'mnemonic': _mnemonic,
'private_key': _hexPrivateKey,
'balance': balance[currency]!.toJSON(),
'passphrase': passphrase,
});
static Future<SolanaWallet> open({
@ -414,8 +412,9 @@ abstract class SolanaWalletBase
if (!hasKeysFile) {
final mnemonic = data!['mnemonic'] as String?;
final privateKey = data['private_key'] as String?;
final passphrase = data['passphrase'] as String?;
keysData = WalletKeysData(mnemonic: mnemonic, privateKey: privateKey);
keysData = WalletKeysData(mnemonic: mnemonic, privateKey: privateKey, passphrase: passphrase);
} else {
keysData = await WalletKeysFile.readKeysFile(
name,
@ -428,6 +427,7 @@ abstract class SolanaWalletBase
return SolanaWallet(
walletInfo: walletInfo,
password: password,
passphrase: keysData.passphrase,
mnemonic: keysData.mnemonic,
privateKey: keysData.privateKey,
initialBalance: balance,
@ -442,7 +442,7 @@ abstract class SolanaWalletBase
}
Future<SolanaBalance> _fetchSOLBalance() async {
final balance = await _client.getBalance(_walletKeyPair!.address);
final balance = await _client.getBalance(solanaAddress);
return SolanaBalance(balance);
}
@ -451,10 +451,9 @@ abstract class SolanaWalletBase
for (var token in splTokensBox.values) {
if (token.enabled) {
try {
final tokenBalance =
await _client.getSplTokenBalance(token.mintAddress, _walletKeyPair!.address) ??
balance[token] ??
SolanaBalance(0.0);
final tokenBalance = await _client.getSplTokenBalance(token.mintAddress, solanaAddress) ??
balance[token] ??
SolanaBalance(0.0);
balance[token] = tokenBalance;
} catch (e) {
printV('Error fetching spl token (${token.symbol}) balance ${e.toString()}');
@ -482,10 +481,9 @@ abstract class SolanaWalletBase
await splTokensBox.put(token.mintAddress, token);
if (token.enabled) {
final tokenBalance =
await _client.getSplTokenBalance(token.mintAddress, _walletKeyPair!.address) ??
balance[token] ??
SolanaBalance(0.0);
final tokenBalance = await _client.getSplTokenBalance(token.mintAddress, solanaAddress) ??
balance[token] ??
SolanaBalance(0.0);
balance[token] = tokenBalance;
} else {
@ -507,37 +505,10 @@ abstract class SolanaWalletBase
}
Future<SPLToken?> getSPLToken(String mintAddress) async {
// Convert SPL token mint address to public key
final Ed25519HDPublicKey mintPublicKey;
try {
mintPublicKey = Ed25519HDPublicKey.fromBase58(mintAddress);
} catch (_) {
return null;
}
// Fetch token's metadata account
try {
final token = await solanaClient!.rpcClient.getMetadata(mint: mintPublicKey);
if (token == null) {
return null;
}
String? iconPath;
try {
iconPath = await _client.getIconImageFromTokenUri(token.uri);
} catch (_) {}
String filteredTokenSymbol = token.symbol.replaceFirst(RegExp('^\\\$'), '');
return SPLToken.fromMetadata(
name: token.name,
mint: token.mint,
symbol: filteredTokenSymbol,
mintAddress: mintAddress,
iconPath: iconPath,
);
} catch (e) {
return await _client.fetchSPLTokenInfo(mintAddress);
} catch (e, s) {
printV('Error fetching token: ${e.toString()}, ${s.toString()}');
return null;
}
}
@ -582,7 +553,7 @@ abstract class SolanaWalletBase
final messageBytes = utf8.encode(message);
// Sign the message bytes with the wallet's private key
final signature = (await _walletKeyPair!.sign(messageBytes)).toString();
final signature = (_solanaPrivateKey.sign(messageBytes)).toString();
return HEX.encode(utf8.encode(signature)).toUpperCase();
}
@ -596,7 +567,7 @@ abstract class SolanaWalletBase
final base58EncodedPublicKeyString = match.group(2)!;
final sigBytes = bytesString.split(', ').map(int.parse).toList();
List<int> pubKeyBytes = base58decode(base58EncodedPublicKeyString);
List<int> pubKeyBytes = SolAddrDecoder().decodeAddr(base58EncodedPublicKeyString);
return [sigBytes, pubKeyBytes];
} else {
@ -619,19 +590,18 @@ abstract class SolanaWalletBase
}
// make sure the address derived from the public key provided matches the one we expect
final pub = Ed25519HDPublicKey(pubKeyBytes);
if (address != pub.toBase58()) {
final pub = SolanaPublicKey.fromBytes(pubKeyBytes);
if (address != pub.toAddress().address) {
return false;
}
return await verifySignature(
return pub.verify(
message: messageBytes,
signature: sigBytes,
publicKey: Ed25519HDPublicKey(pubKeyBytes),
);
}
SolanaClient? get solanaClient => _client.getSolanaClient;
SolanaRPC? get solanaProvider => _client.getSolanaProvider;
@override
String get password => _password;

View file

@ -33,6 +33,7 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
walletInfo: credentials.walletInfo!,
mnemonic: mnemonic,
password: credentials.password!,
passphrase: credentials.passphrase,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
@ -118,6 +119,7 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
password: credentials.password!,
mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!,
passphrase: credentials.passphrase,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);

View file

@ -1,7 +1,6 @@
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/hive_type_ids.dart';
import 'package:hive/hive.dart';
import 'package:solana/metaplex.dart';
part 'spl_token.g.dart';
@ -55,7 +54,7 @@ class SPLToken extends CryptoCurrency with HiveObjectMixin {
required String mint,
required String symbol,
required String mintAddress,
String? iconPath
String? iconPath,
}) {
return SPLToken(
name: name,
@ -117,31 +116,3 @@ class SPLToken extends CryptoCurrency with HiveObjectMixin {
@override
int get hashCode => mintAddress.hashCode;
}
class NFT extends SPLToken {
final ImageInfo? imageInfo;
NFT(
String mint,
String name,
String symbol,
String mintAddress,
int decimal,
String iconPath,
this.imageInfo,
) : super(
name: name,
symbol: symbol,
mintAddress: mintAddress,
decimal: decimal,
mint: mint,
iconPath: iconPath,
);
}
class ImageInfo {
final String uri;
final OffChainMetadata? data;
const ImageInfo(this.uri, this.data);
}

View file

@ -11,7 +11,6 @@ environment:
dependencies:
flutter:
sdk: flutter
solana: ^0.31.0+1
cw_core:
path: ../cw_core
http: ^1.1.0
@ -21,6 +20,14 @@ dependencies:
shared_preferences: ^2.0.15
bip32: ^2.0.0
hex: ^0.2.0
on_chain:
git:
url: https://github.com/cake-tech/on_chain.git
ref: cake-update-v2
blockchain_utils:
git:
url: https://github.com/cake-tech/blockchain_utils
ref: cake-update-v2
dev_dependencies:
flutter_test:

View file

@ -17,7 +17,7 @@ dependencies:
path: ../cw_evm
on_chain:
git:
url: https://github.com/cake-tech/On_chain
url: https://github.com/cake-tech/on_chain.git
ref: cake-update-v2
blockchain_utils:
git:

View file

@ -3,38 +3,8 @@ PODS:
- Flutter
- ReachabilitySwift
- CryptoSwift (1.8.3)
- cw_haven (0.0.1):
- cw_haven/Boost (= 0.0.1)
- cw_haven/Haven (= 0.0.1)
- cw_haven/OpenSSL (= 0.0.1)
- cw_haven/Sodium (= 0.0.1)
- cw_shared_external
- Flutter
- cw_haven/Boost (0.0.1):
- cw_shared_external
- Flutter
- cw_haven/Haven (0.0.1):
- cw_shared_external
- Flutter
- cw_haven/OpenSSL (0.0.1):
- cw_shared_external
- Flutter
- cw_haven/Sodium (0.0.1):
- cw_shared_external
- Flutter
- cw_mweb (0.0.1):
- Flutter
- cw_shared_external (0.0.1):
- cw_shared_external/Boost (= 0.0.1)
- cw_shared_external/OpenSSL (= 0.0.1)
- cw_shared_external/Sodium (= 0.0.1)
- Flutter
- cw_shared_external/Boost (0.0.1):
- Flutter
- cw_shared_external/OpenSSL (0.0.1):
- Flutter
- cw_shared_external/Sodium (0.0.1):
- Flutter
- device_display_brightness (0.0.1):
- Flutter
- device_info_plus (0.0.1):
@ -136,9 +106,7 @@ PODS:
DEPENDENCIES:
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- CryptoSwift
- cw_haven (from `.symlinks/plugins/cw_haven/ios`)
- cw_mweb (from `.symlinks/plugins/cw_mweb/ios`)
- cw_shared_external (from `.symlinks/plugins/cw_shared_external/ios`)
- device_display_brightness (from `.symlinks/plugins/device_display_brightness/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- devicelocale (from `.symlinks/plugins/devicelocale/ios`)
@ -179,12 +147,8 @@ SPEC REPOS:
EXTERNAL SOURCES:
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
cw_haven:
:path: ".symlinks/plugins/cw_haven/ios"
cw_mweb:
:path: ".symlinks/plugins/cw_mweb/ios"
cw_shared_external:
:path: ".symlinks/plugins/cw_shared_external/ios"
device_display_brightness:
:path: ".symlinks/plugins/device_display_brightness/ios"
device_info_plus:
@ -239,9 +203,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d
CryptoSwift: 967f37cea5a3294d9cce358f78861652155be483
cw_haven: b3e54e1fbe7b8e6fda57a93206bc38f8e89b898a
cw_mweb: 22cd01dfb8ad2d39b15332006f22046aaa8352a3
cw_shared_external: 2972d872b8917603478117c9957dfca611845a92
device_display_brightness: 1510e72c567a1f6ce6ffe393dcd9afd1426034f7
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
devicelocale: 35ba84dc7f45f527c3001535d8c8d104edd5d926

View file

@ -1,5 +1,7 @@
import 'dart:convert';
import 'dart:developer';
import 'package:blockchain_utils/blockchain_utils.dart';
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';
@ -8,9 +10,9 @@ import 'package:cake_wallet/src/screens/wallet_connect/widgets/message_display_w
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:cw_core/solana_rpc_http_service.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:solana/base58.dart';
import 'package:solana/solana.dart';
import 'package:on_chain/solana/solana.dart';
import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart';
import '../chain_service.dart';
import '../../wallet_connect_key_service.dart';
@ -27,25 +29,19 @@ class SolanaChainServiceImpl implements ChainService {
final SolanaChainId reference;
final SolanaClient solanaClient;
final SolanaRPC solanaProvider;
final Ed25519HDKeyPair? ownerKeyPair;
final SolanaPrivateKey? ownerPrivateKey;
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: 5),
) {
required this.ownerPrivateKey,
required String formattedRPCUrl,
SolanaRPC? solanaProvider,
}) : solanaProvider = solanaProvider ?? SolanaRPC(SolanaRPCHTTPService(url: formattedRPCUrl)) {
for (final String event in getEvents()) {
wallet.registerEventEmitter(chainId: getChainId(), event: event);
}
@ -110,26 +106,20 @@ class SolanaChainServiceImpl implements ChainService {
}
try {
final message =
await solanaClient.rpcClient.getMessageFromEncodedTx(solanaSignTx.transaction);
// Convert transaction string to bytes
List<int> transactionBytes = base64Decode(solanaSignTx.transaction);
final sign = await ownerKeyPair?.signMessage(
message: message,
recentBlockhash: solanaSignTx.recentBlockhash ?? '',
final message = SolanaTransactionUtils.deserializeMessageLegacy(transactionBytes);
final sign = ownerPrivateKey!.sign(message.serialize());
final signature = solanaProvider.request(
SolanaRPCSendTransaction(
encodedTransaction: Base58Encoder.encode(sign),
commitment: Commitment.confirmed,
),
);
if (sign == null) {
return '';
}
String signature = await solanaClient.sendAndConfirmTransaction(
message: message,
signers: [ownerKeyPair!],
commitment: Commitment.confirmed,
);
printV(signature);
bottomSheetService.queueBottomSheet(
isModalDismissible: true,
widget: BottomSheetMessageDisplayWidget(
@ -161,10 +151,10 @@ class SolanaChainServiceImpl implements ChainService {
if (authError != null) {
return authError;
}
Signature? sign;
List<int>? sign;
try {
sign = await ownerKeyPair?.sign(base58decode(solanaSignMessage.message));
sign = ownerPrivateKey!.sign(Base58Decoder.decode(solanaSignMessage.message));
} catch (e) {
printV(e);
}
@ -173,7 +163,7 @@ class SolanaChainServiceImpl implements ChainService {
return '';
}
String signature = sign.toBase58();
final signature = Base58Encoder.encode(sign);
return signature;
}

View file

@ -22,6 +22,7 @@ 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:on_chain/solana/solana.dart' hide Store;
import 'package:shared_preferences/shared_preferences.dart';
import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart';
@ -140,29 +141,28 @@ abstract class Web3WalletServiceBase with Store {
for (final cId in SolanaChainId.values) {
final node = appStore.settingsStore.getCurrentNode(appStore.wallet!.type);
Uri rpcUri = node.uri;
String webSocketUrl = 'wss://${node.uriRaw}';
String formattedUrl;
String protocolUsed = node.isSSL ? "https" : "http";
if (node.uriRaw == 'rpc.ankr.com') {
String ankrApiKey = secrets.ankrApiKey;
rpcUri = Uri.https(node.uriRaw, '/solana/$ankrApiKey');
webSocketUrl = 'wss://${node.uriRaw}/solana/ws/$ankrApiKey';
formattedUrl = '$protocolUsed://${node.uriRaw}/$ankrApiKey';
} else if (node.uriRaw == 'solana-mainnet.core.chainstack.com') {
String chainStackApiKey = secrets.chainStackApiKey;
rpcUri = Uri.https(node.uriRaw, '/$chainStackApiKey');
webSocketUrl = 'wss://${node.uriRaw}/$chainStackApiKey';
formattedUrl = '$protocolUsed://${node.uriRaw}/$chainStackApiKey';
} else {
formattedUrl = '$protocolUsed://${node.uriRaw}';
}
SolanaChainServiceImpl(
reference: cId,
rpcUrl: rpcUri,
webSocketUrl: webSocketUrl,
formattedRPCUrl: formattedUrl,
wcKeyService: walletKeyService,
bottomSheetService: _bottomSheetHandler,
wallet: _web3Wallet,
ownerKeyPair: solana!.getWalletKeyPair(appStore.wallet!),
ownerPrivateKey: SolanaPrivateKey.fromSeedHex(solana!.getPrivateKey(appStore.wallet!)),
);
}
}

View file

@ -52,11 +52,8 @@ class CWSolana extends Solana {
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;
String getPublicKey(WalletBase wallet) =>
(wallet as SolanaWallet).solanaPublicKey.toAddress().address;
Object createSolanaTransactionCredentials(
List<Output> outputs, {
required CryptoCurrency currency,

View file

@ -326,7 +326,7 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
),
);
}
Widget _buildBottomActionPanel({
required String titleForClipboard,
required String dataToCopy,

View file

@ -78,6 +78,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
WalletType.ethereum,
WalletType.polygon,
WalletType.tron,
WalletType.solana,
WalletType.monero,
WalletType.wownero,
WalletType.zano,

View file

@ -106,12 +106,19 @@ dependencies:
flutter_svg: ^2.0.9
polyseed: ^0.0.6
nostr_tools: ^1.0.9
solana: ^0.31.0+1
ledger_flutter_plus:
git:
url: https://github.com/vespr-wallet/ledger-flutter-plus
ref: c2e341d8038f1108690ad6f80f7b4b7156aacc76
hashlib: ^1.19.2
on_chain:
git:
url: https://github.com/cake-tech/on_chain.git
ref: cake-update-v2
blockchain_utils:
git:
url: https://github.com/cake-tech/blockchain_utils
ref: cake-update-v2
dev_dependencies:
flutter_test:

View file

@ -1261,7 +1261,6 @@ import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:hive/hive.dart';
import 'package:solana/solana.dart';
""";
const solanaCWHeaders = """
@ -1289,7 +1288,6 @@ abstract class Solana {
String getAddress(WalletBase wallet);
String getPrivateKey(WalletBase wallet);
String getPublicKey(WalletBase wallet);
Ed25519HDKeyPair? getWalletKeyPair(WalletBase wallet);
Object createSolanaTransactionCredentials(
List<Output> outputs, {