fix: Already Known Error (#2606)
* fix: Already made error and update transactions two seconds after successful send for evm, tron and sol * feat: Add updateTransactionsHistory method on WalletBase to handle transaction update immediately after sending * Update lib/view_model/send/send_view_model.dart [skip ci] --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
David Adegoke committed
Oct 25, 2025 at 12:34 UTC
b0b053a19dbf59efeeb654cb9e1da9cf474b2889
5 files changed
+80
-44
cw_core/lib/wallet_base.dart
+2
-1
@@ -102,6 +102,7 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
102
String get password;
103
104
Future<void>? updateBalance();
105
+ Future<void> updateTransactionsHistory() async {}
106
107
void setExceptionHandler(void Function(FlutterErrorDetails) onError) => null;
108
@@ -119,7 +120,7 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
120
/// Returns true if the connection is alive, false otherwise.
121
/// Default implementation returns true (no-op for wallets without socket connections).
122
Future<bool> checkSocketHealth() async => true;
122
-
123
+
124
/// This is used to check if the current node is healthy by making a lightweight RPC call
125
/// Each wallet implementation should override this to make a single, efficient call
126
/// Returns true if the node is healthy, false otherwise
cw_evm/lib/evm_chain_wallet.dart
+3
@@ -894,6 +894,9 @@ abstract class EVMChainWalletBase
894
895
@override
896
Future<void>? updateBalance() async => await _updateBalance();
897
+ @override
898
+ Future<void> updateTransactionsHistory() async => await _updateTransactions();
899
+
900
901
List<Erc20Token> get erc20Currencies => evmChainErc20TokensBox.values.toList();
902
cw_solana/lib/solana_wallet.dart
+8
@@ -292,6 +292,14 @@ abstract class SolanaWalletBase
292
@override
293
Future<Map<String, SolanaTransactionInfo>> fetchTransactions() async => {};
294
295
+ @override
296
+ Future<void> updateTransactionsHistory() async {
297
+ await Future.wait([
298
+ _updateNativeSOLTransactions(),
299
+ _updateSPLTokenTransactions(),
300
+ ]);
301
+ }
302
+
303
void updateTransactions(List<SolanaTransactionModel> updatedTx) {
304
_addTransactionsToTransactionHistory(updatedTx);
305
}
cw_tron/lib/tron_wallet.dart
+15
-5
@@ -180,9 +180,11 @@ abstract class TronWalletBase
180
for (var token in initialTronTokens) {
181
if (!tronTokensBox.containsKey(token.contractAddress)) {
182
tronTokensBox.put(token.contractAddress, token);
183
- } else { // update existing token
183
+ } else {
184
+ // update existing token
185
final existingToken = tronTokensBox.get(token.contractAddress);
185
- tronTokensBox.put(token.contractAddress, TronToken.copyWith(token, enabled: existingToken!.enabled));
186
+ tronTokensBox.put(
187
+ token.contractAddress, TronToken.copyWith(token, enabled: existingToken!.enabled));
188
}
189
}
190
}
@@ -281,7 +283,7 @@ abstract class TronWalletBase
283
Future<void> startSync() async {
284
try {
285
syncStatus = AttemptingSyncStatus();
284
-
286
+
287
// Verify node health before attempting to sync
288
final isHealthy = await checkNodeHealth();
289
if (!isHealthy) {
@@ -359,6 +361,14 @@ abstract class TronWalletBase
361
return pendingTransaction;
362
}
363
364
+ @override
365
+ Future<void> updateTransactionsHistory() async {
366
+ await Future.wait([
367
+ fetchTransactions(),
368
+ fetchTrc20ExcludedTransactions(),
369
+ ]);
370
+ }
371
+
372
@override
373
Future<Map<String, TronTransactionInfo>> fetchTransactions() async {
374
final address = _tronAddress;
@@ -522,11 +532,11 @@ abstract class TronWalletBase
532
try {
533
// Check native balance
534
await _client.getBalance(_tronPublicKey.toAddress(), throwOnError: true);
525
-
535
+
536
// Check USDT token balance
537
const usdtContractAddress = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t";
538
await _client.fetchTronTokenBalances(_tronAddress, usdtContractAddress, throwOnError: true);
529
-
539
+
540
return true;
541
} catch (e) {
542
return false;
lib/view_model/send/send_view_model.dart
+52
-38
@@ -139,8 +139,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
139
140
bool get isMwebEnabled => balanceViewModel.mwebEnabled;
141
142
- bool get isEVMWallet => walletType == WalletType.ethereum || walletType == WalletType.polygon ||
143
- walletType == WalletType.base;
142
+ bool get isEVMWallet => isEVMCompatibleChain(walletType);
143
144
@action
145
void setShowAddressBookPopup(bool value) {
@@ -514,7 +513,6 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
513
});
514
}
515
517
-
516
// Swaps.xyz (EVM) path
517
518
if (isEVMWallet && trade != null && provider is SwapsXyzExchangeProvider) {
@@ -524,7 +522,6 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
522
BigInt.tryParse((trade.routerValue ?? '0').toString()) ?? BigInt.zero;
523
524
if (routerTo?.isNotEmpty == true && routerData?.isNotEmpty == true) {
527
-
525
// detect prepared ERC-20 transfer(...) (alt-vm deposit pattern)
526
String _selector(String s) =>
527
(s.startsWith('0x') && s.length >= 10) ? s.substring(0, 10) : '';
@@ -538,11 +535,13 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
535
// Optionally prebuild approval (SKIP for prepared transfer)
536
final tokenContract = trade.sourceTokenAddress ?? '';
537
final requiredAmount = BigInt.tryParse(
541
- (trade.sourceTokenAmountRaw ?? '0').replaceAll('n', ''),
542
- ) ?? BigInt.zero;
538
+ (trade.sourceTokenAmountRaw ?? '0').replaceAll('n', ''),
539
+ ) ??
540
+ BigInt.zero;
541
542
// Only do approval when NOT a prepared transfer, and only if the API hinted we might need it
545
- final requiresTokenApproval = (trade.requiresTokenApproval ?? false) && !_isPreparedTransfer;
543
+ final requiresTokenApproval =
544
+ (trade.requiresTokenApproval ?? false) && !_isPreparedTransfer;
545
546
if (requiresTokenApproval && tokenContract.isNotEmpty && requiredAmount > BigInt.zero) {
547
if (walletType == WalletType.ethereum) {
@@ -656,7 +655,6 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
655
}
656
}
657
659
-
658
// Regular flow
659
660
pendingTransaction = await wallet.createTransaction(_credentials(provider));
@@ -772,21 +770,6 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
770
return; // skip the regular flow below
771
}
772
775
- // Regular flow (non-Swaps)
776
- if (pendingTransaction!.shouldCommitUR()) {
777
- final urstr = await pendingTransaction!.commitUR();
778
- final result = await Navigator.of(context).pushNamed(
779
- Routes.urqrAnimatedPage,
780
- arguments: urstr,
781
- );
782
- if (result == null) {
783
- state = FailureState("Canceled by user");
784
- return;
785
- }
786
- } else {
787
- await pendingTransaction!.commit();
788
- }
789
-
773
String address = outputs.fold('', (acc, value) {
774
return value.isParsedAddress
775
? '$acc${value.address}\n${value.extractedAddress}\n\n'
@@ -818,6 +801,17 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
801
nano!.updateTransactions(wallet);
802
}
803
804
+ // Immediate transaction update for EVM chains, Solana, and Tron
805
+ if (isEVMWallet || walletType == WalletType.solana || walletType == WalletType.tron) {
806
+ Future.delayed(Duration(seconds: 4), () async {
807
+ try {
808
+ await wallet.updateTransactionsHistory();
809
+ } catch (e) {
810
+ printV('Failed to update transactions after send: $e');
811
+ }
812
+ });
813
+ }
814
+
815
if (pendingTransaction!.id.isNotEmpty) {
816
TransactionInfo? tx;
817
if (walletType == WalletType.monero) {
@@ -1124,7 +1118,6 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
1118
required BigInt requiredAmount,
1119
int? sourceTokenDecimals,
1120
}) async {
1127
-
1121
// Only EVM chains support ERC20 approvals
1122
if (!isEVMWallet) return null;
1123
@@ -1138,15 +1131,24 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
1131
bool needsApproval = false;
1132
if (walletType == WalletType.ethereum) {
1133
needsApproval = await ethereum!.isApprovalRequired(
1141
- wallet, tokenContract, spender, requiredAmount,
1134
+ wallet,
1135
+ tokenContract,
1136
+ spender,
1137
+ requiredAmount,
1138
);
1139
} else if (walletType == WalletType.polygon) {
1140
needsApproval = await polygon!.isApprovalRequired(
1145
- wallet, tokenContract, spender, requiredAmount,
1141
+ wallet,
1142
+ tokenContract,
1143
+ spender,
1144
+ requiredAmount,
1145
);
1146
} else if (walletType == WalletType.base) {
1147
needsApproval = await base!.isApprovalRequired(
1149
- wallet, tokenContract, spender, requiredAmount,
1148
+ wallet,
1149
+ tokenContract,
1150
+ spender,
1151
+ requiredAmount,
1152
);
1153
}
1154
@@ -1154,29 +1156,41 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
1156
1157
final erc20Token = wallet.balance.keys.whereType<Erc20Token>().firstWhere(
1158
(t) => t.contractAddress.toLowerCase() == tokenLc,
1157
- orElse: () => Erc20Token(
1158
- name: '',
1159
- symbol: '',
1160
- contractAddress: tokenContract,
1161
- decimal: sourceTokenDecimals ?? 18,
1162
- enabled: true,
1163
- ),
1164
- );
1159
+ orElse: () => Erc20Token(
1160
+ name: '',
1161
+ symbol: '',
1162
+ contractAddress: tokenContract,
1163
+ decimal: sourceTokenDecimals ?? 18,
1164
+ enabled: true,
1165
+ ),
1166
+ );
1167
1168
if (walletType == WalletType.ethereum) {
1169
final priority = _settingsStore.priority[WalletType.ethereum]!;
1170
return await ethereum!.createTokenApproval(
1169
- wallet, requiredAmount, spender, erc20Token, priority,
1171
+ wallet,
1172
+ requiredAmount,
1173
+ spender,
1174
+ erc20Token,
1175
+ priority,
1176
);
1177
} else if (walletType == WalletType.polygon) {
1178
final priority = _settingsStore.priority[WalletType.polygon]!;
1179
return await polygon!.createTokenApproval(
1174
- wallet, requiredAmount, spender, erc20Token, priority,
1180
+ wallet,
1181
+ requiredAmount,
1182
+ spender,
1183
+ erc20Token,
1184
+ priority,
1185
);
1186
} else if (walletType == WalletType.base) {
1187
final priority = _settingsStore.priority[WalletType.base]!;
1188
return await base!.createTokenApproval(
1179
- wallet, requiredAmount, spender, erc20Token, priority,
1189
+ wallet,
1190
+ requiredAmount,
1191
+ spender,
1192
+ erc20Token,
1193
+ priority,
1194
);
1195
}
1196