mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-28 06:28:41 +01:00
fix: dismissed transaction that finishes completing async (#3687)
can overwrite new one GHSA-5p55-jf6f-c7wx
This commit is contained in:
@@ -336,6 +336,12 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
@observable
|
||||
PendingTransaction? pendingTransaction;
|
||||
|
||||
// Bumped when a preparation is dismissed or replaced.
|
||||
//
|
||||
// Finished call sets pendingTransaction only if it matches this value to
|
||||
// its own value.
|
||||
int _sendPreparationId = 0;
|
||||
|
||||
String? get pendingTransactionAdditionalCostNotice {
|
||||
final additionalCost = pendingTransaction?.additionalCost;
|
||||
|
||||
@@ -596,6 +602,9 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
|
||||
@action
|
||||
Future<void> dismissTransaction() async {
|
||||
_sendPreparationId++;
|
||||
_ledgerTxStateTimer?.cancel();
|
||||
_ledgerTxStateTimer = null;
|
||||
state = InitialExecutionState();
|
||||
if (ocpRequest != null) {
|
||||
clearOutputs();
|
||||
@@ -688,19 +697,36 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
Future<PendingTransaction?> createTransaction({ExchangeProvider? provider, Trade? trade}) async {
|
||||
_currentTrade = trade;
|
||||
_currentProvider = provider;
|
||||
final preparationId = ++_sendPreparationId;
|
||||
_ledgerTxStateTimer?.cancel();
|
||||
_ledgerTxStateTimer = null;
|
||||
pendingTransaction = null;
|
||||
Timer? preparationTimer;
|
||||
|
||||
bool replaced() {
|
||||
if (preparationId == _sendPreparationId) {
|
||||
return false;
|
||||
}
|
||||
preparationTimer?.cancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!(state is IsExecutingState)) state = IsExecutingState();
|
||||
|
||||
if (wallet.isHardwareWallet) {
|
||||
if (walletType == WalletType.monero) {
|
||||
_ledgerTxStateTimer = Timer.periodic(Duration(seconds: 1), (timer) {
|
||||
preparationTimer = Timer.periodic(Duration(seconds: 1), (timer) {
|
||||
if (preparationId != _sendPreparationId) {
|
||||
timer.cancel();
|
||||
return;
|
||||
}
|
||||
if (monero!.getLastLedgerCommand() == "INS_CLSAG") {
|
||||
timer.cancel();
|
||||
state = IsDeviceSigningResponseState();
|
||||
}
|
||||
});
|
||||
_ledgerTxStateTimer = preparationTimer;
|
||||
} else {
|
||||
state = IsAwaitingDeviceResponseState();
|
||||
}
|
||||
@@ -728,7 +754,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
|
||||
// Direct Transfer (Simple routing, no approval needed)
|
||||
if (selector == transferSig) {
|
||||
pendingTransaction = await evm!.createRawCallDataTransaction(
|
||||
final prepared = await evm!.createRawCallDataTransaction(
|
||||
wallet,
|
||||
routerTo,
|
||||
routerData,
|
||||
@@ -738,6 +764,10 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
? _settingsStore.useBlinkProtection
|
||||
: false,
|
||||
);
|
||||
if (replaced()) {
|
||||
return null;
|
||||
}
|
||||
pendingTransaction = prepared;
|
||||
state = ExecutedSuccessfullyState();
|
||||
return pendingTransaction;
|
||||
}
|
||||
@@ -784,6 +814,9 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
requiredAmount: BigInt.zero, // Wait until it equals 0
|
||||
waitForExactMatch: true,
|
||||
);
|
||||
if (replaced()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!resetConfirmed) {
|
||||
state = FailureState('Failed to reset USDT allowance. Please try again.');
|
||||
@@ -803,6 +836,9 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
sourceTokenDecimals: trade.sourceTokenDecimals,
|
||||
priority: priority);
|
||||
|
||||
if (replaced()) {
|
||||
return null;
|
||||
}
|
||||
if (approvalTx == null) {
|
||||
state = FailureState('Failed to build approval transaction');
|
||||
return null;
|
||||
@@ -821,6 +857,9 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
spender: routerTo,
|
||||
requiredAmount: requiredAmount,
|
||||
);
|
||||
if (replaced()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isApproved) {
|
||||
state = FailureState(
|
||||
@@ -831,6 +870,9 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
'[Swaps.xyz sending flow] Approval transaction confirmed on-chain. Proceeding with swap execution.');
|
||||
} catch (e, s) {
|
||||
printV('[Swaps.xyz sending flow] Approval transaction error: $e\n$s');
|
||||
if (replaced()) {
|
||||
return null;
|
||||
}
|
||||
state = FailureState(translateErrorMessage(e, wallet.type, wallet.currency));
|
||||
return null;
|
||||
}
|
||||
@@ -838,7 +880,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
|
||||
// Construct Final Swap Transaction
|
||||
printV('[Swaps.xyz sending flow] Building swap transaction');
|
||||
pendingTransaction = await evm!.createRawCallDataTransaction(
|
||||
final prepared = await evm!.createRawCallDataTransaction(
|
||||
wallet,
|
||||
routerTo,
|
||||
routerData,
|
||||
@@ -850,6 +892,10 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
? _settingsStore.useBlinkProtection
|
||||
: false,
|
||||
);
|
||||
if (replaced()) {
|
||||
return null;
|
||||
}
|
||||
pendingTransaction = prepared;
|
||||
|
||||
state = ExecutedSuccessfullyState();
|
||||
return pendingTransaction;
|
||||
@@ -859,6 +905,9 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
return null;
|
||||
} catch (e, s) {
|
||||
printV('Swaps.xyz transaction error: $e\n$s');
|
||||
if (replaced()) {
|
||||
return null;
|
||||
}
|
||||
state = FailureState(
|
||||
'Failed to create Swaps.xyz transaction - ${translateErrorMessage(e, wallet.type, wallet.currency)}');
|
||||
return null;
|
||||
@@ -886,7 +935,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
strictParsing: false,
|
||||
) ?? Money.zero(fromCurrency);
|
||||
|
||||
pendingTransaction = await solana!.signAndPrepareJupiterSwapTransaction(
|
||||
final prepared = await solana!.signAndPrepareJupiterSwapTransaction(
|
||||
wallet,
|
||||
swapTransactionBase64!,
|
||||
requestId!,
|
||||
@@ -894,6 +943,10 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
amount,
|
||||
Money.tryParse(fee.toString(), CryptoCurrency.sol) ?? Money.zero(CryptoCurrency.sol),
|
||||
);
|
||||
if (replaced()) {
|
||||
return null;
|
||||
}
|
||||
pendingTransaction = prepared;
|
||||
|
||||
state = ExecutedSuccessfullyState();
|
||||
return pendingTransaction;
|
||||
@@ -916,7 +969,11 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
}
|
||||
}
|
||||
|
||||
pendingTransaction = await wallet.createTransaction(_credentials(provider));
|
||||
final prepared = await wallet.createTransaction(_credentials(provider));
|
||||
if (replaced()) {
|
||||
return null;
|
||||
}
|
||||
pendingTransaction = prepared;
|
||||
|
||||
final txAmountDouble = double.tryParse(pendingTransaction?.amountFormatted ?? '0') ?? 0.0;
|
||||
final bool isTradeTx = trade != null && provider != null;
|
||||
@@ -964,6 +1021,9 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
state = ExecutedSuccessfullyState();
|
||||
return pendingTransaction;
|
||||
} catch (e) {
|
||||
if (replaced()) {
|
||||
return null;
|
||||
}
|
||||
_ledgerTxStateTimer?.cancel();
|
||||
// if (e is LedgerException) {
|
||||
// final errorCode = e.errorCode.toRadixString(16);
|
||||
|
||||
Reference in New Issue
Block a user