dev
dart 128 lines 4.38 KB
Raw
1 import 'dart:convert';
2 import 'dart:core';
3 import 'dart:developer';
4 import 'package:cw_core/encryption_file_utils.dart';
5 import 'package:cw_core/pathForWallet.dart';
6 import 'package:cw_core/wallet_info.dart';
7 import 'package:cw_evm/evm_chain_transaction_info.dart';
8 import 'package:cw_evm/utils/evm_chain_utils.dart';
9 import 'package:mobx/mobx.dart';
10 import 'package:cw_core/transaction_history.dart';
11
12 part 'evm_chain_transaction_history.g.dart';
13
14 class EVMChainTransactionHistory = EVMChainTransactionHistoryBase with _$EVMChainTransactionHistory;
15
16 abstract class EVMChainTransactionHistoryBase
17 extends TransactionHistoryBase<EVMChainTransactionInfo> with Store {
18 EVMChainTransactionHistoryBase({
19 required this.walletInfo,
20 required String password,
21 required this.encryptionFileUtils,
22 required this.getCurrentChainId,
23 }) : _password = password {
24 transactions = ObservableMap<String, EVMChainTransactionInfo>();
25 }
26
27 String _password;
28
29 final WalletInfo walletInfo;
30 final EncryptionFileUtils encryptionFileUtils;
31
32 /// Function to get the current chain ID (allows transaction history to use correct file)
33 final int Function() getCurrentChainId;
34
35 /// Get transaction history file name based on current chain ID
36 String getTransactionHistoryFileName() {
37 return EVMChainUtils.getTransactionHistoryFileName(getCurrentChainId());
38 }
39
40 EVMChainTransactionInfo getTransactionInfo(Map<String, dynamic> val) {
41 return EVMChainTransactionInfo.fromJson(val, getCurrentChainId());
42 }
43
44 Future<void> init() async {
45 clear();
46 await _load();
47 }
48
49 @override
50 Future<void> save() async {
51 final transactionsHistoryFileNameForWallet = getTransactionHistoryFileName();
52 try {
53 final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
54 String path = '$dirPath/$transactionsHistoryFileNameForWallet';
55
56 // Filter transactions by current chainId before saving
57 // This ensures we only save transactions for the current chain, preventing
58 // transactions from other chains from being saved to the wrong file
59 final currentChainId = getCurrentChainId();
60 final filteredTransactions = <String, EVMChainTransactionInfo>{};
61 for (final entry in transactions.entries) {
62 if (entry.value.chainId == currentChainId) {
63 filteredTransactions[entry.key] = entry.value;
64 }
65 }
66
67 final data = json.encode({'transactions': filteredTransactions});
68 await encryptionFileUtils.write(path: path, password: _password, data: data);
69 } catch (e, s) {
70 log('Error while saving ${walletInfo.type.name} transaction history: ${e.toString()}');
71 log(s.toString());
72 }
73 }
74
75 @override
76 void addOne(EVMChainTransactionInfo transaction) {
77 if (transaction.chainId == getCurrentChainId()) {
78 transactions[transaction.id] = transaction;
79 }
80 }
81
82 @override
83 void addMany(Map<String, EVMChainTransactionInfo> transactionsToAdd) {
84 final currentChainId = getCurrentChainId();
85
86 // First, remove any transactions that don't match the current chainId
87 // This prevents transactions from other chains from persisting in the map
88 transactions.removeWhere((key, value) => value.chainId != currentChainId);
89
90 // Then add/update transactions for the current chain
91 for (final entry in transactionsToAdd.entries) {
92 if (entry.value.chainId == currentChainId) {
93 transactions[entry.key] = entry.value;
94 }
95 }
96 }
97
98 Future<Map<String, dynamic>> _read() async {
99 final transactionsHistoryFileNameForWallet = getTransactionHistoryFileName();
100 final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
101 String path = '$dirPath/$transactionsHistoryFileNameForWallet';
102 final content = await encryptionFileUtils.read(path: path, password: _password);
103 if (content.isEmpty) {
104 return {};
105 }
106 return json.decode(content) as Map<String, dynamic>;
107 }
108
109 Future<void> _load() async {
110 try {
111 final content = await _read();
112 final txs = content['transactions'] as Map<String, dynamic>? ?? {};
113
114 for (var entry in txs.entries) {
115 final val = entry.value;
116
117 if (val is Map<String, dynamic>) {
118 final tx = getTransactionInfo(val);
119 _update(tx);
120 }
121 }
122 } catch (e) {
123 log(e.toString());
124 }
125 }
126
127 void _update(EVMChainTransactionInfo transaction) => transactions[transaction.id] = transaction;
128 }