mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-17 05:41:29 +02:00
94 files changed, -5926 net lines. No stubs, no generators, no dead enum
values, no orphan switch branches. Removed for principled reasons (we
do not ship these chains) and to make the codebase smaller and friendlier
to contribute to.
What's gone:
- lib/{solana,tron,zano,decred,zcash,haven}/ directories
- lib/exchange/provider/jupiter_exchange_provider.dart (Solana-only)
- lib/entities/haven_seed_store.dart
- WalletType.{solana,tron,zano,decred,zcash,haven} enum values
(HiveField IDs are pinned: Wownero stays at 12, all surviving types
keep their IDs, so existing user-saved wallets deserialize unchanged)
- All ~70 lib/ files that imported the removed chain stubs or branched
on the removed WalletType values
- backupSeeds(Box<HavenSeedStore>) abstract method + Wownero implementation
- Jupiter exchange branches in send/exchange view models
- TronGrid usage settings, Zano alias lookups, Zcash transparent address
handling, Decred default-spv-nodes branches, Haven seed migration
What stays (intentional):
- CryptoCurrency.{xhv,sol,trx,dcr,zano,zec} enum entries in cw_core —
they're still used as exchange/swap currency identifiers in
address_validator.dart and elsewhere. The wallet types are gone but
the *currencies* are still tradeable through Trocador.
- cw_core/spl_token.dart and cw_core/tron_token.dart Hive adapters in
main.dart — the tokens still register as Hive types for backwards-
compat with any old box data.
- lib/buy/{moonpay,robinhood,onramper,dfx,kryptonim,meld}/ provider
source files (just stripped of solana/tron/etc references; providers
themselves remain dormant per provider_types.dart).
Pinned-ID note: WalletType enum now has gaps in @HiveField IDs (4, 10,
11, 13, 14, 18 removed). This is deliberate — re-using a freed ID later
would mis-deserialize old hive boxes. Don't renumber.
Bundled config / CI hasn't changed; configure.dart was already free of
these chains in commit 4eb7db0b7.
304 lines
9.4 KiB
Dart
304 lines
9.4 KiB
Dart
import 'package:hash_wallet/reactions/wallet_connect.dart';
|
|
import 'package:hash_wallet/evm/evm.dart';
|
|
import 'package:cw_core/cake_hive.dart';
|
|
import 'package:cw_core/crypto_currency.dart';
|
|
import 'package:cw_core/currency_for_wallet_type.dart';
|
|
import 'package:cw_core/erc20_token.dart';
|
|
import 'package:cw_core/spl_token.dart';
|
|
import 'package:cw_core/tron_token.dart';
|
|
import 'package:cw_core/wallet_base.dart';
|
|
import 'package:cw_core/wallet_info.dart';
|
|
import 'package:cw_core/wallet_type.dart';
|
|
import 'package:hive/hive.dart';
|
|
|
|
class TokenUtilities {
|
|
static Future<List<Erc20Token>> loadAllUniqueEvmTokens() async {
|
|
final allWi = await WalletInfo.getAll();
|
|
final evmWallets = allWi.where(
|
|
(w) => isEVMCompatibleChain(w.type),
|
|
);
|
|
|
|
final seen = <String>{};
|
|
final unique = <Erc20Token>[];
|
|
|
|
for (final wallet in evmWallets) {
|
|
final chain = getTokenNameBasedOnWalletType(wallet.type);
|
|
final box = await _openEvmTokensBoxFor(wallet);
|
|
|
|
for (final t in box.values.where((t) => t.enabled)) {
|
|
final key = '$chain|${t.contractAddress.toLowerCase()}';
|
|
if (seen.add(key)) {
|
|
unique.add(t);
|
|
}
|
|
}
|
|
}
|
|
|
|
return unique;
|
|
}
|
|
|
|
static Future<List<SPLToken>> loadAllUniqueSolTokens() async {
|
|
return <SPLToken>[];
|
|
}
|
|
|
|
static Future<List<TronToken>> loadAllUniqueTronTokens() async {
|
|
return <TronToken>[];
|
|
}
|
|
|
|
static List<Erc20Token> loadDefaultEvmTokensForSwap() {
|
|
if (evm == null) return [];
|
|
|
|
final tokens = <Erc20Token>[];
|
|
final seen = <String>{};
|
|
|
|
for (final chain in evm!.getAllChains()) {
|
|
for (final token in evm!.getDefaultTokensByChainId(chain.chainId)) {
|
|
final key = '${chain.chainId}|${token.contractAddress.toLowerCase()}';
|
|
if (seen.add(key)) tokens.add(token);
|
|
}
|
|
}
|
|
|
|
return tokens;
|
|
}
|
|
|
|
static List<SPLToken> loadDefaultSolTokensForSwap() => <SPLToken>[];
|
|
|
|
static List<TronToken> loadDefaultTronTokensForSwap() => <TronToken>[];
|
|
|
|
static Future<List<Erc20Token>> loadEvmTokensForSwap() async {
|
|
final defaultTokens = loadDefaultEvmTokensForSwap();
|
|
final userTokens = await loadAllUniqueEvmTokens();
|
|
|
|
final seen = <String>{};
|
|
final result = <Erc20Token>[];
|
|
|
|
for (final t in [...defaultTokens, ...userTokens]) {
|
|
final key = '${t.tag ?? 'ETH'}|${t.contractAddress.toLowerCase()}';
|
|
if (seen.add(key)) result.add(t);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static Future<List<SPLToken>> loadSolTokensForSwap() async {
|
|
final defaultTokens = loadDefaultSolTokensForSwap();
|
|
final userTokens = await loadAllUniqueSolTokens();
|
|
|
|
final seen = <String>{};
|
|
final result = <SPLToken>[];
|
|
|
|
for (final t in [...defaultTokens, ...userTokens]) {
|
|
final key = t.mintAddress.toLowerCase();
|
|
if (seen.add(key)) result.add(t);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static Future<List<TronToken>> loadTronTokensForSwap() async {
|
|
final defaultTokens = loadDefaultTronTokensForSwap();
|
|
final userTokens = await loadAllUniqueTronTokens();
|
|
|
|
final seen = <String>{};
|
|
final result = <TronToken>[];
|
|
|
|
for (final t in [...defaultTokens, ...userTokens]) {
|
|
final key = t.contractAddress.toLowerCase();
|
|
if (seen.add(key)) result.add(t);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// Finds a token by address across wallets depending on [walletType]
|
|
/// - EVM chains: match by contractAddress
|
|
static Future<CryptoCurrency?> findTokenByAddress({
|
|
required WalletType walletType,
|
|
required String address,
|
|
}) async {
|
|
if(address.isEmpty) return null;
|
|
final lower = address.toLowerCase();
|
|
switch (walletType) {
|
|
case WalletType.ethereum:
|
|
case WalletType.polygon:
|
|
case WalletType.base:
|
|
case WalletType.arbitrum:
|
|
case WalletType.bsc:
|
|
final tokens = await loadAllUniqueEvmTokens();
|
|
for (final t in tokens) {
|
|
if (t.contractAddress.toLowerCase() == lower) return t;
|
|
}
|
|
return null;
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
static Future<Box<Erc20Token>> _openEvmTokensBoxFor(WalletInfo walletInfo) async {
|
|
final walletKey = walletInfo.name.replaceAll(' ', '_');
|
|
final boxName = _getErc20TokensBoxName(walletKey, walletInfo.type);
|
|
|
|
if (CakeHive.isBoxOpen(boxName)) {
|
|
return CakeHive.box<Erc20Token>(boxName);
|
|
}
|
|
return CakeHive.openBox<Erc20Token>(boxName);
|
|
}
|
|
|
|
static String _getErc20TokensBoxName(String walletKey, WalletType walletType) {
|
|
return switch (walletType) {
|
|
WalletType.ethereum => '${walletKey}_${Erc20Token.ethereumBoxName}',
|
|
WalletType.polygon => '${walletKey}_${Erc20Token.polygonBoxName}',
|
|
WalletType.base => '${walletKey}_${Erc20Token.baseBoxName}',
|
|
WalletType.arbitrum => '${walletKey}_${Erc20Token.arbitrumBoxName}',
|
|
WalletType.bsc => '${walletKey}_${Erc20Token.bscBoxName}',
|
|
_ => '${walletKey}_${Erc20Token.ethereumBoxName}',
|
|
};
|
|
}
|
|
|
|
static Erc20Token? findErc20Token(CryptoCurrency currency, WalletBase wallet) {
|
|
if (currency is Erc20Token) return currency;
|
|
|
|
// More of a fallback for us
|
|
for (final balanceCurrency in wallet.balance.keys) {
|
|
if (balanceCurrency is Erc20Token && _matchesCurrency(balanceCurrency, currency)) {
|
|
return balanceCurrency;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
static Erc20Token? findErc20TokenForSwap(CryptoCurrency currency) {
|
|
if (currency is Erc20Token) return currency;
|
|
|
|
for (final token in loadDefaultEvmTokensForSwap()) {
|
|
if (_matchesCurrency(token, currency)) return token;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
static bool isNativeToken(CryptoCurrency currency) {
|
|
final title = currency.title.toLowerCase();
|
|
final tag = currency.tag?.toLowerCase();
|
|
|
|
return title == 'eth' ||
|
|
title == 'ethereum' ||
|
|
title == 'matic' ||
|
|
title == 'polygon' ||
|
|
title == 'base' ||
|
|
title == 'arbitrum' ||
|
|
title == 'bnb' ||
|
|
title == 'bsc' ||
|
|
title == 'avax' ||
|
|
title == 'avalanche' ||
|
|
tag == 'polygon' ||
|
|
tag == 'bsc' ||
|
|
tag == 'avalanche';
|
|
}
|
|
|
|
static int getChainId(CryptoCurrency currency) {
|
|
final tag = currency.tag?.toUpperCase();
|
|
final title = currency.title.toLowerCase();
|
|
|
|
// Only check EVM registry for currencies that might be EVM-related
|
|
final isPotentialEVM = isNativeToken(currency) ||
|
|
(tag != null &&
|
|
(tag == 'ETH' || tag == 'POL' || tag == 'BASE' || tag == 'ARB' || tag == 'BSC'));
|
|
|
|
if (isPotentialEVM) {
|
|
// Try by tag first if available (e.g., 'POL', 'BASE', 'ARB')
|
|
if (tag != null) {
|
|
final chainId = evm?.getChainIdByTag(tag);
|
|
if (chainId != null) return chainId;
|
|
}
|
|
|
|
// Try by title (case-insensitive)
|
|
final titleChainId = evm?.getChainIdByTitle(title);
|
|
if (titleChainId != null) return titleChainId;
|
|
}
|
|
|
|
// Fallback to hardcoded values for chains not in registry yet
|
|
// Avalanche C-Chain
|
|
if (title == 'avalanche' || title == 'avax' || tag == 'AVALANCHE') {
|
|
return 43114;
|
|
}
|
|
|
|
// Optimism
|
|
if (title == 'optimism' || title == 'op' || tag == 'OPTIMISM') {
|
|
return 10;
|
|
}
|
|
|
|
// Fantom Opera
|
|
if (title == 'fantom' || title == 'ftm' || tag == 'FANTOM') {
|
|
return 250;
|
|
}
|
|
|
|
// Default to Ethereum mainnet
|
|
return 1;
|
|
}
|
|
|
|
static Future<List<CryptoCurrency>> getAvailableTokensForNetwork(
|
|
WalletType network,
|
|
) async {
|
|
final baseCurrency = walletTypeToCryptoCurrency(network);
|
|
final allTokens = <CryptoCurrency>[baseCurrency];
|
|
final addedAddresses = <String>{};
|
|
|
|
// Handle EVM networks
|
|
if (isEVMCompatibleChain(network)) {
|
|
// First, collect all user tokens
|
|
final userTokens = await _getUserTokensForNetwork(baseCurrency);
|
|
for (final token in userTokens) {
|
|
if (token is Erc20Token) {
|
|
final address = token.contractAddress.toLowerCase();
|
|
if (addedAddresses.add(address)) {
|
|
allTokens.add(token);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Then add tokens from CryptoCurrency.all that don't duplicate user tokens
|
|
for (final currency in CryptoCurrency.all) {
|
|
// Match by tag for POL/BASE, match by title==tag for ETH
|
|
final matches = (baseCurrency.tag == null && baseCurrency.title == currency.tag) ||
|
|
(baseCurrency.tag != null &&
|
|
currency.tag?.toLowerCase() == baseCurrency.tag?.toLowerCase());
|
|
|
|
if (matches) {
|
|
if (currency is Erc20Token) {
|
|
final address = currency.contractAddress.toLowerCase();
|
|
if (addedAddresses.add(address)) {
|
|
allTokens.add(currency);
|
|
}
|
|
} else if (!allTokens.any((t) => _matchesCurrency(t, currency))) {
|
|
allTokens.add(currency);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return allTokens;
|
|
}
|
|
|
|
static bool _matchesCurrency(CryptoCurrency a, CryptoCurrency b) {
|
|
return a.title.toUpperCase() == b.title.toUpperCase() &&
|
|
(a.tag?.toUpperCase() == b.tag?.toUpperCase());
|
|
}
|
|
|
|
static Future<List<CryptoCurrency>> _getUserTokensForNetwork(CryptoCurrency baseCurrency) async {
|
|
final walletType = cryptoCurrencyOrTokenToWalletType(baseCurrency);
|
|
if (walletType == null) return [];
|
|
|
|
if (isEVMCompatibleChain(walletType)) {
|
|
final tokens = await TokenUtilities.loadAllUniqueEvmTokens();
|
|
|
|
return tokens.where((token) {
|
|
if (baseCurrency.tag == null) return token.tag == baseCurrency.title;
|
|
|
|
return token.tag?.toLowerCase() == baseCurrency.tag?.toLowerCase();
|
|
}).toList();
|
|
}
|
|
|
|
return [];
|
|
}
|
|
}
|