mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-17 05:41:29 +02:00
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>
This commit is contained in:
co-authored by
Omar Hatem
parent
6388ae8af8
commit
b0b053a19d
@@ -102,6 +102,7 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
|
||||
String get password;
|
||||
|
||||
Future<void>? updateBalance();
|
||||
Future<void> updateTransactionsHistory() async {}
|
||||
|
||||
void setExceptionHandler(void Function(FlutterErrorDetails) onError) => null;
|
||||
|
||||
@@ -119,7 +120,7 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
|
||||
/// Returns true if the connection is alive, false otherwise.
|
||||
/// Default implementation returns true (no-op for wallets without socket connections).
|
||||
Future<bool> checkSocketHealth() async => true;
|
||||
|
||||
|
||||
/// This is used to check if the current node is healthy by making a lightweight RPC call
|
||||
/// Each wallet implementation should override this to make a single, efficient call
|
||||
/// Returns true if the node is healthy, false otherwise
|
||||
|
||||
@@ -894,6 +894,9 @@ abstract class EVMChainWalletBase
|
||||
|
||||
@override
|
||||
Future<void>? updateBalance() async => await _updateBalance();
|
||||
@override
|
||||
Future<void> updateTransactionsHistory() async => await _updateTransactions();
|
||||
|
||||
|
||||
List<Erc20Token> get erc20Currencies => evmChainErc20TokensBox.values.toList();
|
||||
|
||||
|
||||
@@ -292,6 +292,14 @@ abstract class SolanaWalletBase
|
||||
@override
|
||||
Future<Map<String, SolanaTransactionInfo>> fetchTransactions() async => {};
|
||||
|
||||
@override
|
||||
Future<void> updateTransactionsHistory() async {
|
||||
await Future.wait([
|
||||
_updateNativeSOLTransactions(),
|
||||
_updateSPLTokenTransactions(),
|
||||
]);
|
||||
}
|
||||
|
||||
void updateTransactions(List<SolanaTransactionModel> updatedTx) {
|
||||
_addTransactionsToTransactionHistory(updatedTx);
|
||||
}
|
||||
|
||||
@@ -180,9 +180,11 @@ abstract class TronWalletBase
|
||||
for (var token in initialTronTokens) {
|
||||
if (!tronTokensBox.containsKey(token.contractAddress)) {
|
||||
tronTokensBox.put(token.contractAddress, token);
|
||||
} else { // update existing token
|
||||
} else {
|
||||
// update existing token
|
||||
final existingToken = tronTokensBox.get(token.contractAddress);
|
||||
tronTokensBox.put(token.contractAddress, TronToken.copyWith(token, enabled: existingToken!.enabled));
|
||||
tronTokensBox.put(
|
||||
token.contractAddress, TronToken.copyWith(token, enabled: existingToken!.enabled));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -281,7 +283,7 @@ abstract class TronWalletBase
|
||||
Future<void> startSync() async {
|
||||
try {
|
||||
syncStatus = AttemptingSyncStatus();
|
||||
|
||||
|
||||
// Verify node health before attempting to sync
|
||||
final isHealthy = await checkNodeHealth();
|
||||
if (!isHealthy) {
|
||||
@@ -359,6 +361,14 @@ abstract class TronWalletBase
|
||||
return pendingTransaction;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateTransactionsHistory() async {
|
||||
await Future.wait([
|
||||
fetchTransactions(),
|
||||
fetchTrc20ExcludedTransactions(),
|
||||
]);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, TronTransactionInfo>> fetchTransactions() async {
|
||||
final address = _tronAddress;
|
||||
@@ -522,11 +532,11 @@ abstract class TronWalletBase
|
||||
try {
|
||||
// Check native balance
|
||||
await _client.getBalance(_tronPublicKey.toAddress(), throwOnError: true);
|
||||
|
||||
|
||||
// Check USDT token balance
|
||||
const usdtContractAddress = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t";
|
||||
await _client.fetchTronTokenBalances(_tronAddress, usdtContractAddress, throwOnError: true);
|
||||
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
|
||||
@@ -139,8 +139,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
|
||||
bool get isMwebEnabled => balanceViewModel.mwebEnabled;
|
||||
|
||||
bool get isEVMWallet => walletType == WalletType.ethereum || walletType == WalletType.polygon ||
|
||||
walletType == WalletType.base;
|
||||
bool get isEVMWallet => isEVMCompatibleChain(walletType);
|
||||
|
||||
@action
|
||||
void setShowAddressBookPopup(bool value) {
|
||||
@@ -514,7 +513,6 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Swaps.xyz (EVM) path
|
||||
|
||||
if (isEVMWallet && trade != null && provider is SwapsXyzExchangeProvider) {
|
||||
@@ -524,7 +522,6 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
BigInt.tryParse((trade.routerValue ?? '0').toString()) ?? BigInt.zero;
|
||||
|
||||
if (routerTo?.isNotEmpty == true && routerData?.isNotEmpty == true) {
|
||||
|
||||
// detect prepared ERC-20 transfer(...) (alt-vm deposit pattern)
|
||||
String _selector(String s) =>
|
||||
(s.startsWith('0x') && s.length >= 10) ? s.substring(0, 10) : '';
|
||||
@@ -538,11 +535,13 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
// Optionally prebuild approval (SKIP for prepared transfer)
|
||||
final tokenContract = trade.sourceTokenAddress ?? '';
|
||||
final requiredAmount = BigInt.tryParse(
|
||||
(trade.sourceTokenAmountRaw ?? '0').replaceAll('n', ''),
|
||||
) ?? BigInt.zero;
|
||||
(trade.sourceTokenAmountRaw ?? '0').replaceAll('n', ''),
|
||||
) ??
|
||||
BigInt.zero;
|
||||
|
||||
// Only do approval when NOT a prepared transfer, and only if the API hinted we might need it
|
||||
final requiresTokenApproval = (trade.requiresTokenApproval ?? false) && !_isPreparedTransfer;
|
||||
final requiresTokenApproval =
|
||||
(trade.requiresTokenApproval ?? false) && !_isPreparedTransfer;
|
||||
|
||||
if (requiresTokenApproval && tokenContract.isNotEmpty && requiredAmount > BigInt.zero) {
|
||||
if (walletType == WalletType.ethereum) {
|
||||
@@ -656,7 +655,6 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Regular flow
|
||||
|
||||
pendingTransaction = await wallet.createTransaction(_credentials(provider));
|
||||
@@ -772,21 +770,6 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
return; // skip the regular flow below
|
||||
}
|
||||
|
||||
// Regular flow (non-Swaps)
|
||||
if (pendingTransaction!.shouldCommitUR()) {
|
||||
final urstr = await pendingTransaction!.commitUR();
|
||||
final result = await Navigator.of(context).pushNamed(
|
||||
Routes.urqrAnimatedPage,
|
||||
arguments: urstr,
|
||||
);
|
||||
if (result == null) {
|
||||
state = FailureState("Canceled by user");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
await pendingTransaction!.commit();
|
||||
}
|
||||
|
||||
String address = outputs.fold('', (acc, value) {
|
||||
return value.isParsedAddress
|
||||
? '$acc${value.address}\n${value.extractedAddress}\n\n'
|
||||
@@ -818,6 +801,17 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
nano!.updateTransactions(wallet);
|
||||
}
|
||||
|
||||
// Immediate transaction update for EVM chains, Solana, and Tron
|
||||
if (isEVMWallet || walletType == WalletType.solana || walletType == WalletType.tron) {
|
||||
Future.delayed(Duration(seconds: 4), () async {
|
||||
try {
|
||||
await wallet.updateTransactionsHistory();
|
||||
} catch (e) {
|
||||
printV('Failed to update transactions after send: $e');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (pendingTransaction!.id.isNotEmpty) {
|
||||
TransactionInfo? tx;
|
||||
if (walletType == WalletType.monero) {
|
||||
@@ -1124,7 +1118,6 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
required BigInt requiredAmount,
|
||||
int? sourceTokenDecimals,
|
||||
}) async {
|
||||
|
||||
// Only EVM chains support ERC20 approvals
|
||||
if (!isEVMWallet) return null;
|
||||
|
||||
@@ -1138,15 +1131,24 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
bool needsApproval = false;
|
||||
if (walletType == WalletType.ethereum) {
|
||||
needsApproval = await ethereum!.isApprovalRequired(
|
||||
wallet, tokenContract, spender, requiredAmount,
|
||||
wallet,
|
||||
tokenContract,
|
||||
spender,
|
||||
requiredAmount,
|
||||
);
|
||||
} else if (walletType == WalletType.polygon) {
|
||||
needsApproval = await polygon!.isApprovalRequired(
|
||||
wallet, tokenContract, spender, requiredAmount,
|
||||
wallet,
|
||||
tokenContract,
|
||||
spender,
|
||||
requiredAmount,
|
||||
);
|
||||
} else if (walletType == WalletType.base) {
|
||||
needsApproval = await base!.isApprovalRequired(
|
||||
wallet, tokenContract, spender, requiredAmount,
|
||||
wallet,
|
||||
tokenContract,
|
||||
spender,
|
||||
requiredAmount,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1154,29 +1156,41 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
|
||||
final erc20Token = wallet.balance.keys.whereType<Erc20Token>().firstWhere(
|
||||
(t) => t.contractAddress.toLowerCase() == tokenLc,
|
||||
orElse: () => Erc20Token(
|
||||
name: '',
|
||||
symbol: '',
|
||||
contractAddress: tokenContract,
|
||||
decimal: sourceTokenDecimals ?? 18,
|
||||
enabled: true,
|
||||
),
|
||||
);
|
||||
orElse: () => Erc20Token(
|
||||
name: '',
|
||||
symbol: '',
|
||||
contractAddress: tokenContract,
|
||||
decimal: sourceTokenDecimals ?? 18,
|
||||
enabled: true,
|
||||
),
|
||||
);
|
||||
|
||||
if (walletType == WalletType.ethereum) {
|
||||
final priority = _settingsStore.priority[WalletType.ethereum]!;
|
||||
return await ethereum!.createTokenApproval(
|
||||
wallet, requiredAmount, spender, erc20Token, priority,
|
||||
wallet,
|
||||
requiredAmount,
|
||||
spender,
|
||||
erc20Token,
|
||||
priority,
|
||||
);
|
||||
} else if (walletType == WalletType.polygon) {
|
||||
final priority = _settingsStore.priority[WalletType.polygon]!;
|
||||
return await polygon!.createTokenApproval(
|
||||
wallet, requiredAmount, spender, erc20Token, priority,
|
||||
wallet,
|
||||
requiredAmount,
|
||||
spender,
|
||||
erc20Token,
|
||||
priority,
|
||||
);
|
||||
} else if (walletType == WalletType.base) {
|
||||
final priority = _settingsStore.priority[WalletType.base]!;
|
||||
return await base!.createTokenApproval(
|
||||
wallet, requiredAmount, spender, erc20Token, priority,
|
||||
wallet,
|
||||
requiredAmount,
|
||||
spender,
|
||||
erc20Token,
|
||||
priority,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user