Files
hash-wallet/cw_polygon/lib/polygon_wallet.dart
T
cyanandGitHub 76876181e5 feat: initial hive -> SQLite (#2502)
* feat: initial hive -> SQLite

Includes migration script and initial work done to
remove dependency on hive generators.

Also contains some refactoring to make sync code
async.

Features new dev screen that lists all SQLite tables

Aspects that need testing:
- backups (from this version, and from previous versions as well)
- wallet loading / wallet list
- almost everything else
- renaming
- deletion
- sorting in wallet list
- wallet groups
- wallet seed/keys
- wallet switching
- wallet creation / restore - all methods
- wallet address page
- hardware wallet connection (and prompts from mall screens)
- contacts
- advanced settings in wallet creation
- derivation paths
- some minor wownero fixes
- base, hardware wallets

* fix: build errors

* fix: Bad state: No element in backup restore

* address comments from review

* fix: HardwareWalletType
2025-10-31 22:37:15 +01:00

171 lines
5.8 KiB
Dart

import 'dart:convert';
import 'package:cw_core/cake_hive.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/erc20_token.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_keys_file.dart';
import 'package:cw_evm/evm_chain_transaction_history.dart';
import 'package:cw_evm/evm_chain_transaction_info.dart';
import 'package:cw_evm/evm_chain_transaction_model.dart';
import 'package:cw_evm/evm_chain_wallet.dart';
import 'package:cw_evm/evm_erc20_balance.dart';
import 'package:cw_polygon/default_polygon_erc20_tokens.dart';
import 'package:cw_polygon/polygon_client.dart';
import 'package:cw_polygon/polygon_transaction_history.dart';
import 'package:cw_polygon/polygon_transaction_info.dart';
class PolygonWallet extends EVMChainWallet {
PolygonWallet({
required super.walletInfo,
required super.derivationInfo,
required super.password,
super.mnemonic,
super.initialBalance,
super.privateKey,
required super.client,
required super.encryptionFileUtils,
super.passphrase,
}) : super(nativeCurrency: CryptoCurrency.maticpoly);
@override
Future<void> initErc20TokensBox() async {
final boxName = "${walletInfo.name.replaceAll(" ", "_")}_ ${Erc20Token.polygonBoxName}";
if (await CakeHive.boxExists(boxName)) {
evmChainErc20TokensBox = await CakeHive.openBox<Erc20Token>(boxName);
} else {
evmChainErc20TokensBox = await CakeHive.openBox<Erc20Token>(boxName.replaceAll(" ", ""));
}
}
@override
void addInitialTokens() {
final initialErc20Tokens = DefaultPolygonErc20Tokens().initialPolygonErc20Tokens;
for (final token in initialErc20Tokens) {
if (!evmChainErc20TokensBox.containsKey(token.contractAddress)) {
evmChainErc20TokensBox.put(token.contractAddress, token);
} else { // update existing token
final existingToken = evmChainErc20TokensBox.get(token.contractAddress);
evmChainErc20TokensBox.put(token.contractAddress, Erc20Token.copyWith(token, enabled: existingToken!.enabled));
}
}
}
@override
List<String> get getDefaultTokenContractAddresses =>
DefaultPolygonErc20Tokens().initialPolygonErc20Tokens.map((e) => e.contractAddress).toList();
@override
Future<bool> checkIfScanProviderIsEnabled() async {
bool isPolygonScanEnabled = (await sharedPrefs.future).getBool("use_polygonscan") ?? true;
return isPolygonScanEnabled;
}
@override
String getTransactionHistoryFileName() => 'polygon_transactions.json';
@override
Erc20Token createNewErc20TokenObject(Erc20Token token, String? iconPath) {
return Erc20Token(
name: token.name,
symbol: token.symbol,
contractAddress: token.contractAddress,
decimal: token.decimal,
enabled: token.enabled,
tag: token.tag ?? 'POL',
iconPath: iconPath,
isPotentialScam: token.isPotentialScam,
);
}
@override
EVMChainTransactionInfo getTransactionInfo(
EVMChainTransactionModel transactionModel, String address) {
final model = PolygonTransactionInfo(
id: transactionModel.hash,
height: transactionModel.blockNumber,
ethAmount: transactionModel.amount,
direction: transactionModel.from == address
? TransactionDirection.outgoing
: TransactionDirection.incoming,
isPending: false,
date: transactionModel.date,
confirmations: transactionModel.confirmations,
ethFee: BigInt.from(transactionModel.gasUsed) * transactionModel.gasPrice,
exponent: transactionModel.tokenDecimal ?? 18,
tokenSymbol: transactionModel.tokenSymbol ?? "MATIC",
to: transactionModel.to,
from: transactionModel.from,
contractAddress: transactionModel.contractAddress,
);
return model;
}
@override
EVMChainTransactionHistory setUpTransactionHistory(
WalletInfo walletInfo, String password, EncryptionFileUtils encryptionFileUtils) {
return PolygonTransactionHistory(
walletInfo: walletInfo,
password: password,
encryptionFileUtils: encryptionFileUtils,
);
}
static Future<PolygonWallet> open({
required String name,
required String password,
required WalletInfo walletInfo,
required EncryptionFileUtils encryptionFileUtils,
}) async {
final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type);
final path = await pathForWallet(name: name, type: walletInfo.type);
Map<String, dynamic>? data;
try {
final jsonSource = await encryptionFileUtils.read(path: path, password: password);
data = json.decode(jsonSource) as Map<String, dynamic>;
} catch (e) {
if (!hasKeysFile) rethrow;
}
final balance = EVMChainERC20Balance.fromJSON(data?['balance'] as String?) ??
EVMChainERC20Balance(BigInt.zero);
final WalletKeysData keysData;
// Migrate wallet from the old scheme to then new .keys file scheme
if (!hasKeysFile) {
final mnemonic = data!['mnemonic'] as String?;
final privateKey = data['private_key'] as String?;
final passphrase = data['passphrase'] as String?;
keysData = WalletKeysData(mnemonic: mnemonic, privateKey: privateKey, passphrase: passphrase);
} else {
keysData = await WalletKeysFile.readKeysFile(
name,
walletInfo.type,
password,
encryptionFileUtils,
);
}
final derivationInfo = await walletInfo.getDerivationInfo();
return PolygonWallet(
walletInfo: walletInfo,
derivationInfo: derivationInfo,
password: password,
mnemonic: keysData.mnemonic,
privateKey: keysData.privateKey,
passphrase: keysData.passphrase,
initialBalance: balance,
client: PolygonClient(),
encryptionFileUtils: encryptionFileUtils,
);
}
}