CW-1035: Solana New Fixes (#2205)

* fix: Error while restoring Solana Wallet with PrivateKey

* fix: Enhance Solana Error Messages, especially for ATA Creation Errors

* - Optimize Solana Transaction History, now more smoother and faster
- fix bug with transactions history not being displayed in real time until next reload
This commit is contained in:
David Adegoke 2025-04-15 01:37:46 +01:00 committed by GitHub
parent 469373b780
commit f574fa5e9b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 127 additions and 82 deletions

View file

@ -254,8 +254,9 @@ class SolanaWalletClient {
final receiver = message.accountKeys[instruction.accounts[1]].address;
String? tokenSymbol = splTokenSymbol;
if (tokenSymbol == null && mintAddress != null) {
final token = await fetchSPLTokenInfo(mintAddress);
final token = await getTokenInfo(mintAddress);
tokenSymbol = token?.symbol;
}
@ -288,9 +289,9 @@ class SolanaWalletClient {
int? splTokenDecimal,
Commitment? commitment,
SolAddress? walletAddress,
required void Function(List<SolanaTransactionModel>) onUpdate,
}) async {
List<SolanaTransactionModel> transactions = [];
try {
final signatures = await _provider!.request(
SolanaRPCGetSignaturesForAddress(
@ -299,10 +300,11 @@ class SolanaWalletClient {
),
);
final List<VersionedTransactionResponse?> transactionDetails = [];
// The maximum concurrent batch size.
const int batchSize = 10;
for (int i = 0; i < signatures.length; i += 20) {
final batch = signatures.skip(i).take(20).toList(); // Get the next 20 signatures
for (int i = 0; i < signatures.length; i += batchSize) {
final batch = signatures.skip(i).take(batchSize).toList();
final batchResponses = await Future.wait(batch.map((signature) async {
try {
@ -314,30 +316,31 @@ class SolanaWalletClient {
),
);
} catch (e) {
printV("Error fetching transaction: $e");
// printV("Error fetching transaction: $e");
return null;
}
}));
transactionDetails.addAll(batchResponses.whereType<VersionedTransactionResponse>());
final versionedBatchResponses = batchResponses.whereType<VersionedTransactionResponse>();
// to avoid reaching the node RPS limit
if (i + 20 < signatures.length) {
final parsedTransactionsFutures = versionedBatchResponses.map((tx) => parseTransaction(
txResponse: tx,
splTokenSymbol: splTokenSymbol,
walletAddress: walletAddress?.address ?? address.address,
));
final parsedTransactions = await Future.wait(parsedTransactionsFutures);
transactions.addAll(parsedTransactions.whereType<SolanaTransactionModel>().toList());
// Calling the callback after each batch is processed, therefore passing the current list of transactions.
onUpdate(List<SolanaTransactionModel>.from(transactions));
if (i + batchSize < signatures.length) {
await Future.delayed(const Duration(milliseconds: 500));
}
}
for (final tx in transactionDetails) {
final parsedTx = await parseTransaction(
txResponse: tx,
splTokenSymbol: splTokenSymbol,
walletAddress: walletAddress?.address ?? address.address,
);
if (parsedTx != null) {
transactions.add(parsedTx);
}
}
return transactions;
} catch (err, s) {
printV('Error fetching transactions: $err \n$s');
@ -350,6 +353,7 @@ class SolanaWalletClient {
required String splTokenSymbol,
required int splTokenDecimal,
required SolanaPrivateKey privateKey,
required void Function(List<SolanaTransactionModel>) onUpdate,
}) async {
ProgramDerivedAddress? associatedTokenAccount;
final ownerWalletAddress = privateKey.publicKey().toAddress();
@ -373,11 +377,26 @@ class SolanaWalletClient {
splTokenSymbol: splTokenSymbol,
splTokenDecimal: splTokenDecimal,
walletAddress: ownerWalletAddress,
onUpdate: onUpdate,
);
return tokenTransactions;
}
final Map<String, SPLToken?> tokenInfoCache = {};
Future<SPLToken?> getTokenInfo(String mintAddress) async {
if (tokenInfoCache.containsKey(mintAddress)) {
return tokenInfoCache[mintAddress];
} else {
final token = await fetchSPLTokenInfo(mintAddress);
if (token != null) {
tokenInfoCache[mintAddress] = token;
}
return token;
}
}
Future<SPLToken?> fetchSPLTokenInfo(String mintAddress) async {
final programAddress =
MetaplexTokenMetaDataProgramUtils.findMetadataPda(mint: SolAddress(mintAddress));
@ -747,9 +766,7 @@ class SolanaWalletClient {
} catch (e) {
associatedRecipientAccount = null;
throw SolanaCreateAssociatedTokenAccountException(
'Error fetching recipient associated token account: ${e.toString()}',
);
throw SolanaCreateAssociatedTokenAccountException(e.toString());
}
if (associatedRecipientAccount == null) {

View file

@ -292,9 +292,14 @@ abstract class SolanaWalletBase
@override
Future<Map<String, SolanaTransactionInfo>> fetchTransactions() async => {};
void updateTransactions(List<SolanaTransactionModel> updatedTx) {
_addTransactionsToTransactionHistory(updatedTx);
}
/// Fetches the native SOL transactions linked to the wallet Public Key
Future<void> _updateNativeSOLTransactions() async {
final transactions = await _client.fetchTransactions(_solanaPublicKey.toAddress());
final transactions =
await _client.fetchTransactions(_solanaPublicKey.toAddress(), onUpdate: updateTransactions);
await _addTransactionsToTransactionHistory(transactions);
}
@ -313,6 +318,7 @@ abstract class SolanaWalletBase
splTokenSymbol: token.symbol,
splTokenDecimal: token.decimal,
privateKey: _solanaPrivateKey,
onUpdate: updateTransactions,
);
// splTokenTransactions.addAll(tokenTxs);