mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-03-29 18:49:01 +00:00
Solana Wallet New Implementation (#2011)
* Feat: Implement Solana wallet using on_chain * v4.23.0 release candidate (#1974) * 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 (#1979) 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 (#1992) * 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 (#2004) * 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:
parent
1c29be7993
commit
1b5be705f6
19 changed files with 716 additions and 561 deletions
cw_core
cw_solana
lib
default_spl_tokens.dartpending_solana_transaction.dartsolana_client.dartsolana_transaction_info.dartsolana_wallet.dartsolana_wallet_service.dartspl_token.dart
pubspec.yamlcw_tron
ios
lib
core/wallet_connect
solana
src/screens/wallet_keys
view_model
tool
26
cw_core/lib/solana_rpc_http_service.dart
Normal file
26
cw_core/lib/solana_rpc_http_service.dart
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)',
|
||||
|
|
|
@ -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
|
@ -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';
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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!)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -326,7 +326,7 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Widget _buildBottomActionPanel({
|
||||
required String titleForClipboard,
|
||||
required String dataToCopy,
|
||||
|
|
|
@ -78,6 +78,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
|
|||
WalletType.ethereum,
|
||||
WalletType.polygon,
|
||||
WalletType.tron,
|
||||
WalletType.solana,
|
||||
WalletType.monero,
|
||||
WalletType.wownero,
|
||||
WalletType.zano,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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, {
|
||||
|
|
Loading…
Reference in a new issue