mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
* feat: add `Money` class for precise currency handling and amount parsing - Introduced `Money` class for handling money values with fixed-point precision. - Added parsing and comparison methods to `CryptoCurrency` and `FiatCurrency` interfaces. - Implemented `smartAmountSanitizer` for format normalization. Includes extensive unit tests for validation. * refactor: optimize Money class with new factories and string formatting improvements * refactor: unify balance handling with `Money` refactor * refactor: update `ElectrumBalance` and transaction logic to use `Money` for currency precision * refactor: standardize balance handling with `Money` across `Monero`, `Wownero`, and `Zano` wallets * refactor: migrate EVM balance and transaction handling to `Money` for consistent currency precision * refactor: migrate Decred balance and transaction handling to `Money` for consistent currency precision * refactor: migrate nano balance and transaction handling to `Money` for consistent currency precision * refactor: migrate Solana balance and transaction logic to `Money` for precise currency handling * refactor: migrate Tron balance and transaction handling to `Money` for consistent currency precision * refactor: migrate Zcash balance and transaction handling to `Money` for consistent currency precision * refactor: adjust lib to handle money * refactor: remove legacy Decred amount formatting and migrate to `Money` * refactor: migrate pending transaction amount and fee to `Money` * refactor: migrate dEuro savings and transaction logic to `Money` for improved precision and consistency * refactor: migrate Bitcoin and lightning transaction logic to `Money` for consistent currency precision * refactor: migrate Wownero, EVM, Zano, and Zcash transaction logic to `Money` for consistent and precise currency handling * refactor: standardize balance handling across wallets, migrate to `Money.zero` and refine `Balance` class for consistency * refactor: migrate Solana transaction and wallet logic to `Money` for consistent and precise currency handling * refactor: improve fiat balance and formatting for consistency on dashboard * fix: fiat input mode switching and standardize crypto amount display logic * fix: cw_evm generator * refactor: migrate exchange amount handling to `Money` for improved precision and consistency * test: remove redundant and commented-out tests, tidy test cases, and apply minor formatting adjustments * fix using Money wrapper in BridgeViewModel and USDT0Service * refactor: streamline `AmountConverter` and `LightningWallet`, add `cw_core` tests to GitHub workflow, and remove unused/deprecated logic * refactor: enhance `changeReceiveAmount` with `isCanonical` support, improve null handling, and refine amount parsing logic * minor fix * try/catch the lightning log file error * feat: add copy-to-clipboard functionality for transaction amounts, fix typo in trailing widget method name (#3219) * Cw 1234 disallow sp and mweb addresses as refund for swaps (#3187) * disallow SP and MWEB for swaps * Simplify address selection in addressForExchange Refactor addressForExchange method to simplify address selection logic. * validate refund and receive address types * localize address validation messages * minor ui fix [skip ci] * minor fix [skip ci] * Revert "localize address validation messages" * fix translations --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * refactor: replace manual amount sanitization with `sanitized()` extension across the codebase, add unit tests for `AmountSanitizer` * fix: correct `sanitized()` test expectations for decimal handling in `amount_sanitizer_test.dart` * refactor: fee display logic and append currency symbol in `send_confirm_sheet.dart` * refactor: simplify `ZcashBalance` class by replacing custom fields with inherited properties, update references accordingly * refactor: replace hardcoded USDT contract address with `DefaultTronTokens` lookup, improve maintainability * refactor: replace `currency` with `inputAmount.currency` in Solana transaction signing methods to avoid inconsistency * fix: handle null `fee` in `electrum_transaction_info.dart` to prevent potential runtime errors * refactor: replace raw fee and amount handling with `Money` object for improved consistency and maintainability * fix: limit fiat balance display to 2 decimal places in `balance_view_model.dart` * refactor: replace raw `amount` and `fee` types with `Money` in Solana transaction methods for consistency and precision * refactor: replace raw `amount` and `fee` handling with `Money` and improve balance calculation consistency across view models * refactor: replace raw balance strings with `Money` and simplify `UnconfirmedBalanceModal` widget logic * refactor: replace `print` with `printV` for better logging, add equality and hashCode overrides to `ERC20Currency` for object comparison * feat: prepare deuro savings migration * feat: add V1 savings migration support and improve formatting consistency in DEuro view model * feat: add V1 savings withdrawal support and update associated UI components and logic * refactor: replace `Image.asset` with `CakeImageWidget`, update balance string handling, and streamline address validation logic * refactor: use `Money.tryParse` with fallback to `Money.zero` in `NanoBalance.fromRawString` for safer balance parsing * refactor: replace `ERC20Currency` with `Erc20Token` for consistency and remove unused `evm_erc20_currency.dart` file * refactor: add computed `hasDepositAmount` for deposit null-checks, optimize balance handling in `zano_wallet`, update Breez SDK to v0.14.0, and implement equality/hashCode for `Currency` * refactor: rename `amountSats` to `amount` in `PrepareLnurlPayRequest` for consistency * revert: downgrade Breez SDK to v0.11.0 in `pubspec.yaml` and update relevant references in `pubspec.lock` * fix: use `cryptoAmount.copyWith` for currency consistency and replace `Image.asset` with `CakeImageWidget` in swap address modal * fix: ensure currency consistency by using `cryptoAmount.copyWith` in Solana and EVM wallet transactions * refactor: simplify CSV row generation logic by removing `_splitAmountCurrency` and streamlining amount/fee handling * fix: LateInitializationError Field 'sdk' has not been initialized * fix: use `Money.tryParse` for safer parsing of deposit and receive amounts * chore: remove debug print statement * refactor: update transaction processing to consistently use `Money` for amounts and fees * refactor: improve currency and amount handling, simplify logic, and enhance code readability in Send and Swap pages * refactor: integrate `DecimalInputFormatter` for consistent decimal input across Send and Swap pages, simplify input handling logic * fix: "fiatMode" per output on sendPage * refactor: make transaction_details_height applicable more readable * fix: use correct base unit for deposit * refactor: use Money in estimateFakeSendAllTxAmount method * fix decred frozen balance minor fixes * fix decred frozen balance minor fixes * fix: automatically switch to lightning if lnurl or invoice detected * fix: getBolt11Amount test cases * fix: standardise crypto amount formatting and display * fix: use base unit for Tron transaction fee parsing * refactor: hardcode fractional digits to 8 in `AmountParsingProxy` display methods * refactor: ensure consistent 8-decimal precision for fee formatting in `PendingTransaction` * fix: use `AmountParsingProxy` for available balance display in exchange view model * fix: improve amount formatting and layout in swap and send confirmation sheets * chore: remove redundant decimal truncation in balance view model and send confirmation sheet * fix: allow for editing text field if it is filled with "ALL" * fix: allow for editing text field if it is filled with "ALL" * fix: update currency detection logic for lightning mode * avoid potential infinite loop [skip ci] * fix: getDisplayCryptoAmount * fix: update estimated fee calculation * chore: simplify fee calculation logic [skip-ci] * fix value display in hidden mode * fix stale state in send confirm sheet * fix decimals for mweb * fix available balance display for sol/trx on swap * restore proper hint text * fix swap receive amount calculation for xno --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> Co-authored-by: Serhii <17529954+serhii-bor@users.noreply.github.com> Co-authored-by: Robert Malikowski <malikowskirobert@gmail.com>
757 lines
22 KiB
Dart
757 lines
22 KiB
Dart
part of 'evm.dart';
|
|
|
|
class CWEVM extends EVM {
|
|
@override
|
|
List<String> getEVMWordList(String language) => EVMChainMnemonics.englishWordlist;
|
|
|
|
@override
|
|
WalletService createEVMWalletService(WalletType walletType, bool isDirect) {
|
|
return EVMChainWalletService(isDirect);
|
|
}
|
|
|
|
@override
|
|
WalletCredentials createEVMNewWalletCredentials({
|
|
required String name,
|
|
WalletInfo? walletInfo,
|
|
String? password,
|
|
String? mnemonic,
|
|
String? passphrase,
|
|
}) {
|
|
return EVMChainNewWalletCredentials(
|
|
name: name,
|
|
walletInfo: walletInfo,
|
|
password: password,
|
|
mnemonic: mnemonic,
|
|
passphrase: passphrase,
|
|
);
|
|
}
|
|
|
|
@override
|
|
WalletCredentials createEVMRestoreWalletFromSeedCredentials({
|
|
required String name,
|
|
required String mnemonic,
|
|
required String password,
|
|
String? passphrase,
|
|
}) {
|
|
return EVMChainRestoreWalletFromSeedCredentials(
|
|
name: name,
|
|
password: password,
|
|
mnemonic: mnemonic,
|
|
passphrase: passphrase,
|
|
);
|
|
}
|
|
|
|
@override
|
|
WalletCredentials createEVMRestoreWalletFromPrivateKey({
|
|
required String name,
|
|
required String privateKey,
|
|
required String password,
|
|
}) {
|
|
return EVMChainRestoreWalletFromPrivateKey(
|
|
name: name,
|
|
password: password,
|
|
privateKey: privateKey,
|
|
);
|
|
}
|
|
|
|
@override
|
|
WalletCredentials createEVMHardwareWalletCredentials({
|
|
required String name,
|
|
required HardwareAccountData hwAccountData,
|
|
WalletInfo? walletInfo,
|
|
}) {
|
|
return EVMChainRestoreWalletFromHardware(
|
|
name: name,
|
|
hwAccountData: hwAccountData,
|
|
walletInfo: walletInfo,
|
|
);
|
|
}
|
|
|
|
@override
|
|
String getAddress(WalletBase wallet) => (wallet as EVMChainWallet).walletAddresses.address;
|
|
|
|
@override
|
|
String getPrivateKey(WalletBase wallet) {
|
|
final privateKeyHolder = (wallet as EVMChainWallet).evmChainPrivateKey;
|
|
if (privateKeyHolder is EthPrivateKey) {
|
|
return bytesToHex(privateKeyHolder.privateKey);
|
|
}
|
|
return "";
|
|
}
|
|
|
|
@override
|
|
String getPublicKey(WalletBase wallet) {
|
|
final privateKeyInUnitInt = (wallet as EVMChainWallet).evmChainPrivateKey;
|
|
return privateKeyInUnitInt.address.hex;
|
|
}
|
|
|
|
@override
|
|
TransactionPriority getDefaultTransactionPriority() => EVMChainTransactionPriority.medium;
|
|
|
|
@override
|
|
TransactionPriority getEVMTransactionPrioritySlow() => EVMChainTransactionPriority.slow;
|
|
|
|
@override
|
|
List<TransactionPriority> getTransactionPriorities() => EVMChainTransactionPriority.all;
|
|
|
|
@override
|
|
TransactionPriority deserializeEVMTransactionPriority(int raw) =>
|
|
EVMChainTransactionPriority.deserialize(raw: raw);
|
|
|
|
@override
|
|
Object createEVMTransactionCredentials(
|
|
List<Output> outputs, {
|
|
required CryptoCurrency currency,
|
|
TransactionPriority? priority,
|
|
int? feeRate,
|
|
bool useBlinkProtection = true,
|
|
}) =>
|
|
EVMChainTransactionCredentials(
|
|
outputs
|
|
.map((out) => OutputInfo(
|
|
fiatAmount: out.fiatAmount,
|
|
cryptoAmount: out.cryptoAmountMoney,
|
|
address: out.address,
|
|
note: out.note,
|
|
sendAll: out.sendAll,
|
|
extractedAddress: out.extractedAddress,
|
|
isParsedAddress: out.isParsedAddress,
|
|
memo: out.memo,
|
|
))
|
|
.toList(),
|
|
priority: priority as EVMChainTransactionPriority?,
|
|
currency: currency,
|
|
feeRate: feeRate,
|
|
useBlinkProtection: useBlinkProtection,
|
|
);
|
|
|
|
@override
|
|
Object createEVMTransactionCredentialsRaw(
|
|
List<OutputInfo> outputs, {
|
|
TransactionPriority? priority,
|
|
required CryptoCurrency currency,
|
|
required int feeRate,
|
|
bool useBlinkProtection = true,
|
|
}) {
|
|
return EVMChainTransactionCredentials(
|
|
outputs,
|
|
priority: priority as EVMChainTransactionPriority?,
|
|
currency: currency,
|
|
feeRate: feeRate,
|
|
useBlinkProtection: useBlinkProtection,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TransactionInfo getTransactionInfo({
|
|
required String id,
|
|
required int height,
|
|
required Money amount,
|
|
required Money fee,
|
|
required String tokenSymbol,
|
|
int exponent = 18,
|
|
required TransactionDirection direction,
|
|
required bool isPending,
|
|
required DateTime date,
|
|
required int confirmations,
|
|
String? to,
|
|
String? from,
|
|
String? evmSignatureName,
|
|
String? contractAddress,
|
|
required int chainId,
|
|
}) =>
|
|
EVMChainTransactionInfo(
|
|
id: id,
|
|
height: height,
|
|
amount: amount,
|
|
fee: fee,
|
|
tokenSymbol: tokenSymbol,
|
|
direction: direction,
|
|
isPending: isPending,
|
|
date: date,
|
|
confirmations: confirmations,
|
|
to: to,
|
|
from: from,
|
|
chainId: chainId);
|
|
|
|
@override
|
|
int formatterEVMParseAmount(String amount) => EVMChainFormatter.parseEVMChainAmount(amount);
|
|
|
|
@override
|
|
List<Erc20Token> getERC20Currencies(WalletBase wallet) =>
|
|
(wallet as EVMChainWallet).erc20Currencies;
|
|
|
|
@override
|
|
Future<void> addErc20Token(WalletBase wallet, CryptoCurrency token) =>
|
|
(wallet as EVMChainWallet).addErc20Token(token as Erc20Token);
|
|
|
|
@override
|
|
Future<void> deleteErc20Token(WalletBase wallet, CryptoCurrency token) =>
|
|
(wallet as EVMChainWallet).deleteErc20Token(token as Erc20Token);
|
|
|
|
@override
|
|
Future<void> removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token) =>
|
|
(wallet as EVMChainWallet).removeTokenTransactionsInHistory(token as Erc20Token);
|
|
|
|
@override
|
|
Future<Erc20Token?> getErc20Token(WalletBase wallet, String contractAddress) {
|
|
final evmWallet = wallet as EVMChainWallet;
|
|
final chainName = EVMChainUtils.getDefaultTokenSymbol(evmWallet.selectedChainId).toLowerCase();
|
|
return evmWallet.getErc20Token(contractAddress, chainName);
|
|
}
|
|
|
|
@override
|
|
CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction) {
|
|
transaction as EVMChainTransactionInfo;
|
|
final evmWallet = wallet as EVMChainWallet;
|
|
|
|
final nativeCurrency = evmWallet.currency;
|
|
final nativeCurrencyTitle = nativeCurrency.title;
|
|
final currentChainId = evmWallet.selectedChainId;
|
|
|
|
// If transaction is from a different chain, we will return native currency as fallback
|
|
// This can happen during chain switching when old transactions are still visible
|
|
if (transaction.chainId != currentChainId) {
|
|
return nativeCurrency;
|
|
}
|
|
|
|
if (transaction.tokenSymbol == CryptoCurrency.maticpoly.title ||
|
|
transaction.tokenSymbol == "MATIC") {
|
|
return CryptoCurrency.maticpoly;
|
|
}
|
|
|
|
if (transaction.tokenSymbol == nativeCurrencyTitle) {
|
|
return nativeCurrency;
|
|
}
|
|
|
|
// Otherwise, it's an ERC20 token
|
|
// Also using firstWhereOrNull to handle cases where token isn't found (e.g., during chain switch)
|
|
final erc20Token = evmWallet.erc20Currencies.firstWhereOrNull(
|
|
(element) =>
|
|
transaction.contractAddress?.toLowerCase() == element.contractAddress.toLowerCase(),
|
|
);
|
|
|
|
return erc20Token ?? nativeCurrency;
|
|
}
|
|
|
|
@override
|
|
void updateScanProviderUsageState(WalletBase wallet, bool isEnabled) =>
|
|
(wallet as EVMChainWallet).updateScanProviderUsageState(isEnabled);
|
|
|
|
@override
|
|
Web3Client? getWeb3Client(WalletBase wallet) => (wallet as EVMChainWallet).getWeb3Client();
|
|
|
|
@override
|
|
Future<bool?> getTransactionReceipt(WalletBase wallet, String txHash) async {
|
|
final client = getWeb3Client(wallet);
|
|
if (client == null) return null;
|
|
|
|
try {
|
|
final receipt = await client.getTransactionReceipt(txHash);
|
|
|
|
if (receipt == null) return null;
|
|
|
|
return receipt.status;
|
|
} catch (_) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@override
|
|
String getTokenAddress(CryptoCurrency asset) => (asset as Erc20Token).contractAddress;
|
|
|
|
@override
|
|
Future<bool> isApprovalRequired(
|
|
WalletBase wallet,
|
|
String tokenContract,
|
|
String spender,
|
|
BigInt requiredAmount,
|
|
) =>
|
|
(wallet as EVMChainWallet).isApprovalRequired(tokenContract, spender, requiredAmount);
|
|
|
|
@override
|
|
Future<BigInt?> getAllowance(WalletBase wallet, String tokenContract, String spender) =>
|
|
(wallet as EVMChainWallet).getAllowance(tokenContract, spender);
|
|
|
|
@override
|
|
Future<PendingTransaction> createTokenApproval(
|
|
WalletBase wallet,
|
|
Money amount,
|
|
String spender,
|
|
TransactionPriority? priority, {
|
|
bool useBlinkProtection = true,
|
|
}) {
|
|
final evmWallet = wallet as EVMChainWallet;
|
|
return evmWallet.createApprovalTransaction(
|
|
amount,
|
|
spender,
|
|
priority as EVMChainTransactionPriority?,
|
|
useBlinkProtection: useBlinkProtection,
|
|
);
|
|
}
|
|
|
|
@override
|
|
Future<PendingTransaction> createRawCallDataTransaction(
|
|
WalletBase wallet,
|
|
String to,
|
|
String dataHex,
|
|
Money valueWei,
|
|
TransactionPriority? priority, {
|
|
bool useBlinkProtection = true,
|
|
String? sourceTokenAddress,
|
|
BigInt? sourceTokenAmount,
|
|
}) =>
|
|
(wallet as EVMChainWallet).createCallDataTransaction(
|
|
to,
|
|
dataHex,
|
|
valueWei,
|
|
priority as EVMChainTransactionPriority?,
|
|
sourceTokenAddress,
|
|
sourceTokenAmount,
|
|
useBlinkProtection: useBlinkProtection,
|
|
);
|
|
|
|
@override
|
|
Future<void> setHardwareWalletService(
|
|
WalletBase wallet,
|
|
HardwareWalletService service,
|
|
) async {
|
|
final evmWallet = wallet as EVMChainWallet;
|
|
final privateKey = evmWallet.evmChainPrivateKey;
|
|
final derivationPath = (await wallet.walletInfo.getDerivationInfo()).derivationPath;
|
|
|
|
if (service is EVMChainLedgerService) {
|
|
(privateKey as EvmLedgerCredentials)
|
|
.setLedgerConnection(service.ledgerConnection, derivationPath);
|
|
} else if (service is EVMChainBitboxService) {
|
|
(privateKey as EvmBitboxCredentials).setBitbox(service.manager, derivationPath);
|
|
} else if (service is EVMChainTrezorService) {
|
|
(privateKey as EvmTrezorCredentials).setTrezorConnect(service.connect, derivationPath);
|
|
}
|
|
}
|
|
|
|
@override
|
|
HardwareWalletService getLedgerHardwareWalletService(ledger.LedgerConnection connection) =>
|
|
EVMChainLedgerService(connection);
|
|
|
|
@override
|
|
HardwareWalletService getBitboxHardwareWalletService(bitbox.BitboxManager manager) =>
|
|
EVMChainBitboxService(manager);
|
|
|
|
@override
|
|
HardwareWalletService getTrezorHardwareWalletService(trezor.TrezorConnect connect) =>
|
|
EVMChainTrezorService(connect);
|
|
|
|
@override
|
|
List<Erc20Token> getDefaultTokensByChainId(int chainId) =>
|
|
EVMChainDefaultTokens.getDefaultTokensByChainId(chainId);
|
|
|
|
@override
|
|
List<String> getDefaultTokenContractAddresses(WalletBase wallet) {
|
|
final chainId = getSelectedChainId(wallet);
|
|
if (chainId == null) return [];
|
|
return EVMChainDefaultTokens.getDefaultTokenAddresses(chainId);
|
|
}
|
|
|
|
@override
|
|
List<String> getDefaultTokenSymbols(WalletBase wallet) {
|
|
final chainId = getSelectedChainId(wallet);
|
|
if (chainId == null) return [];
|
|
return EVMChainDefaultTokens.getDefaultTokenSymbols(chainId);
|
|
}
|
|
|
|
@override
|
|
bool isTokenAlreadyAdded(WalletBase wallet, String contractAddress) {
|
|
final evmWallet = wallet as EVMChainWallet;
|
|
return evmWallet.erc20Currencies
|
|
.any((element) => element.contractAddress.toLowerCase() == contractAddress.toLowerCase());
|
|
}
|
|
|
|
@override
|
|
String? getEVMNativeEstimatedFee(WalletBase wallet) =>
|
|
(wallet as EVMChainWallet).nativeTxEstimatedFee;
|
|
|
|
@override
|
|
String? getEVMERC20EstimatedFee(WalletBase wallet) =>
|
|
(wallet as EVMChainWallet).erc20TxEstimatedFee;
|
|
|
|
// Chain-specific integrations (only for Ethereum)
|
|
@override
|
|
Future<Money>? getDEuroSavingsBalance(WalletBase wallet) {
|
|
if (wallet.chainId == 1 && wallet is EVMChainWallet) {
|
|
return DEuro(wallet).savingsBalance;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
Future<Money>? getDEuroSavingsV1Balance(WalletBase wallet) {
|
|
if (wallet.chainId == 1 && wallet is EVMChainWallet) {
|
|
return DEuro(wallet).savingsBalanceV1;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
Future<Money>? getDEuroAccruedInterest(WalletBase wallet) {
|
|
if (wallet.chainId == 1 && wallet is EVMChainWallet) {
|
|
return DEuro(wallet).accruedInterest;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
Future<BigInt>? getDEuroInterestRate(WalletBase wallet) {
|
|
if (wallet.chainId == 1 && wallet is EVMChainWallet) {
|
|
return DEuro(wallet).interestRate;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
Future<BigInt>? getDEuroSavingsApproved(WalletBase wallet) {
|
|
if (wallet.chainId == 1 && wallet is EVMChainWallet) {
|
|
return DEuro(wallet).approvedBalance;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
Future<PendingTransaction>? addDEuroSaving(
|
|
WalletBase wallet, BigInt amount, TransactionPriority priority) {
|
|
if (wallet.chainId == 1 && wallet is EVMChainWallet) {
|
|
return DEuro(wallet).depositSavings(amount, priority as EVMChainTransactionPriority);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
Future<PendingTransaction>? removeDEuroSaving(
|
|
WalletBase wallet, BigInt amount, TransactionPriority priority) {
|
|
if (wallet.chainId == 1 && wallet is EVMChainWallet) {
|
|
return DEuro(wallet).withdrawSavings(amount, priority as EVMChainTransactionPriority);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
Future<PendingTransaction>? withdrawDEuroSavingV1(
|
|
WalletBase wallet, TransactionPriority priority) {
|
|
if (wallet.chainId == 1 && wallet is EVMChainWallet) {
|
|
return DEuro(wallet).withdrawSavingsV1(priority as EVMChainTransactionPriority);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
Future<PendingTransaction>? reinvestDEuroInterest(
|
|
WalletBase wallet, TransactionPriority priority) {
|
|
if (wallet.chainId == 1 && wallet is EVMChainWallet) {
|
|
return DEuro(wallet).reinvestInterest(priority as EVMChainTransactionPriority);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
Future<PendingTransaction>? enableDEuroSaving(WalletBase wallet, TransactionPriority priority) {
|
|
if (wallet.chainId == 1 && wallet is EVMChainWallet) {
|
|
return DEuro(wallet).enableSavings(priority as EVMChainTransactionPriority);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// Registry helper methods
|
|
static final EvmChainRegistry _registry = EvmChainRegistry();
|
|
|
|
@override
|
|
int getChainIdByWalletType(WalletType walletType) {
|
|
final config = _registry.getChainConfigByWalletType(walletType);
|
|
return config?.chainId ?? 1; // Default to Ethereum
|
|
}
|
|
|
|
@override
|
|
String getChainNameByWalletType(WalletType walletType) {
|
|
final config = _registry.getChainConfigByWalletType(walletType);
|
|
return config?.shortCode ?? 'eth';
|
|
}
|
|
|
|
@override
|
|
String getTokenNameByWalletType(WalletType walletType) {
|
|
final config = _registry.getChainConfigByWalletType(walletType);
|
|
return config?.nativeCurrency.title ?? 'ETH';
|
|
}
|
|
|
|
@override
|
|
String getCaip2ByChainId(int chainId) {
|
|
final config = _registry.getChainConfig(chainId);
|
|
return config?.caip2 ?? 'eip155:1';
|
|
}
|
|
|
|
@override
|
|
String getChainNameByChainId(int chainId) {
|
|
final config = _registry.getChainConfig(chainId);
|
|
return config?.shortCode ?? 'eth';
|
|
}
|
|
|
|
@override
|
|
String getTokenNameByChainId(int chainId) {
|
|
final config = _registry.getChainConfig(chainId);
|
|
return config?.nativeCurrency.title ?? 'ETH';
|
|
}
|
|
|
|
@override
|
|
int? getChainIdByTag(String tag) {
|
|
final config = _registry.getChainConfigByTag(tag);
|
|
return config?.chainId;
|
|
}
|
|
|
|
@override
|
|
int? getChainIdByTitle(String title) {
|
|
// Try as tag first (uppercase)
|
|
final tagResult = getChainIdByTag(title.toUpperCase());
|
|
if (tagResult != null) return tagResult;
|
|
|
|
// Try as lowercase title
|
|
return getChainIdByTag(title.toLowerCase());
|
|
}
|
|
|
|
@override
|
|
WalletType? getWalletTypeByChainId(int chainId) {
|
|
return _registry.getWalletTypeByChainId(chainId);
|
|
}
|
|
|
|
@override
|
|
List<ChainInfo> getAllChains() {
|
|
final allChains = _registry.getAllChains();
|
|
return allChains
|
|
.map((config) => ChainInfo(
|
|
chainId: config.chainId,
|
|
name: config.name,
|
|
shortCode: config.shortCode,
|
|
currency: config.nativeCurrency,
|
|
))
|
|
.toList();
|
|
}
|
|
|
|
@override
|
|
ChainInfo? getChainInfoByChainId(int chainId) {
|
|
final config = _registry.getChainConfig(chainId);
|
|
if (config == null) return null;
|
|
|
|
return ChainInfo(
|
|
chainId: config.chainId,
|
|
name: config.name,
|
|
shortCode: config.shortCode,
|
|
currency: config.nativeCurrency,
|
|
);
|
|
}
|
|
|
|
@override
|
|
ChainInfo? getCurrentChain(WalletBase wallet) {
|
|
if (wallet is EVMChainWallet) {
|
|
final config = wallet.selectedChainConfig;
|
|
if (config == null) return null;
|
|
return ChainInfo(
|
|
chainId: config.chainId,
|
|
name: config.name,
|
|
shortCode: config.shortCode,
|
|
currency: config.nativeCurrency,
|
|
);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
int? getSelectedChainId(WalletBase wallet) {
|
|
if (wallet is EVMChainWallet) {
|
|
return wallet.selectedChainId;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
Future<void> selectChain(WalletBase wallet, int chainId, {required Node node}) async {
|
|
if (wallet is EVMChainWallet) {
|
|
await wallet.selectChain(chainId, node: node);
|
|
}
|
|
}
|
|
|
|
@override
|
|
String? getExplorerUrlForChainId(int chainId, {bool showProtocol = true}) {
|
|
final config = _registry.getChainConfig(chainId);
|
|
|
|
if (config != null && config.explorerUrls.isNotEmpty) {
|
|
final url = config.explorerUrls.first;
|
|
return showProtocol
|
|
? url
|
|
: url.replaceAll('https://', '').replaceAll('http://', '').split('/')[0];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
bool hasPriorityFee(int chainId) => EVMChainUtils.hasPriorityFee(chainId);
|
|
|
|
@override
|
|
bool isUSDT0Token(WalletBase wallet, CryptoCurrency token) {
|
|
if (token is! Erc20Token) return false;
|
|
|
|
final chainId = getSelectedChainId(wallet);
|
|
if (chainId == null) return false;
|
|
|
|
return USDT0Config.isUSDT0Token(token, chainId);
|
|
}
|
|
|
|
@override
|
|
List<ChainInfo> getUSDT0DestinationChains(WalletBase wallet) {
|
|
final currentChainId = getSelectedChainId(wallet);
|
|
if (currentChainId == null) return [];
|
|
|
|
final result = <ChainInfo>[];
|
|
for (final config in _registry.getAllChains()) {
|
|
if (USDT0Config.isChainSupported(config.chainId) && config.chainId != currentChainId) {
|
|
result.add(ChainInfo(
|
|
chainId: config.chainId,
|
|
name: config.name,
|
|
shortCode: config.shortCode,
|
|
currency: config.nativeCurrency,
|
|
));
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@override
|
|
Future<BridgeQuote> quoteUSDT0Transfer({
|
|
required WalletBase wallet,
|
|
required int sourceChainId,
|
|
required int destinationChainId,
|
|
required BigInt amount,
|
|
required String recipientAddress,
|
|
}) async {
|
|
final evmWallet = wallet as EVMChainWallet;
|
|
final client = evmWallet.getWeb3Client();
|
|
if (client == null) {
|
|
throw StateError('Wallet not connected');
|
|
}
|
|
|
|
final quote = await USDT0Service.quoteCrossChainTransfer(
|
|
client: client,
|
|
sourceChainId: sourceChainId,
|
|
destinationChainId: destinationChainId,
|
|
amount: amount,
|
|
recipientAddress: recipientAddress,
|
|
);
|
|
return BridgeQuote(
|
|
nativeFee: quote.nativeFee,
|
|
lzTokenFee: quote.lzTokenFee,
|
|
);
|
|
}
|
|
|
|
@override
|
|
Future<PendingTransaction> executeUSDT0Transfer({
|
|
required WalletBase wallet,
|
|
required CryptoCurrency token,
|
|
required int sourceChainId,
|
|
required int destinationChainId,
|
|
required BigInt amount,
|
|
required String recipientAddress,
|
|
required BridgeQuote quote,
|
|
required TransactionPriority priority,
|
|
bool useBlinkProtection = true,
|
|
}) {
|
|
final evmWallet = wallet as EVMChainWallet;
|
|
final tokenErc20 = token as Erc20Token;
|
|
|
|
return USDT0Service.executeCrossChainTransfer(
|
|
wallet: evmWallet,
|
|
sourceChainId: sourceChainId,
|
|
destinationChainId: destinationChainId,
|
|
amount: amount,
|
|
recipientAddress: recipientAddress,
|
|
quote: USDT0Quote(nativeFee: quote.nativeFee, lzTokenFee: quote.lzTokenFee),
|
|
token: tokenErc20,
|
|
priority: priority as EVMChainTransactionPriority,
|
|
useBlinkProtection: useBlinkProtection,
|
|
);
|
|
}
|
|
|
|
Future<EvmWalletConnectFeeQuote?> getWCBufferedFeeQuote(
|
|
WalletBase wallet,
|
|
TransactionPriority priority,
|
|
) async {
|
|
if (wallet is! EVMChainWallet) return null;
|
|
|
|
final data = await wallet.getWCBufferedFeeQuote(priority);
|
|
if (data == null) return null;
|
|
|
|
return EvmWalletConnectFeeQuote(
|
|
maxFeePerGasWei: data.maxFeePerGasWei,
|
|
maxPriorityFeePerGasWei: data.maxPriorityFeePerGasWei,
|
|
latestBaseFeeWei: data.latestBaseFeeWei,
|
|
);
|
|
}
|
|
|
|
Future<({double usdValue, bool hasValidFiatPrice})> _getTokenUsdValueAndFiatCheck(
|
|
Erc20Token token,
|
|
BigInt balanceWei,
|
|
) async {
|
|
try {
|
|
final settingsStore = getIt.get<SettingsStore>();
|
|
final torOnly = settingsStore.fiatApiMode == FiatApiMode.torOnly;
|
|
|
|
final price = await FiatConversionService.fetchPrice(
|
|
crypto: token,
|
|
fiat: FiatCurrency.usd,
|
|
torOnly: torOnly,
|
|
);
|
|
|
|
final hasValidFiatPrice = price > 0;
|
|
|
|
final decimals = token.decimal;
|
|
final balance = balanceWei.toDouble() / math.pow(10, decimals);
|
|
final usdValue = balance * price;
|
|
|
|
return (usdValue: usdValue, hasValidFiatPrice: hasValidFiatPrice);
|
|
} catch (e) {
|
|
return (usdValue: 0.0, hasValidFiatPrice: false);
|
|
}
|
|
}
|
|
|
|
static const _minTokenUsdValue = 0.1;
|
|
@override
|
|
Future<void> discoverAndAddWalletTokens(WalletBase wallet) async {
|
|
if (wallet is! EVMChainWallet) return;
|
|
|
|
try {
|
|
final result = await wallet.discoverTokensFromMoralis();
|
|
|
|
if (result.newTokens.isEmpty) return;
|
|
|
|
final List<Future<void>> tokenChecks = [];
|
|
|
|
for (final item in result.newTokens) {
|
|
tokenChecks.add((() async {
|
|
final token = item.token;
|
|
|
|
final isPropertiesSuspicious = wallet.isTokenPropertiesSuspicious(token);
|
|
|
|
final fiatResult = await _getTokenUsdValueAndFiatCheck(
|
|
token,
|
|
item.balanceWei,
|
|
);
|
|
final isSpam = isPropertiesSuspicious || !fiatResult.hasValidFiatPrice;
|
|
|
|
token.isPotentialScam = isSpam;
|
|
|
|
token.enabled = (fiatResult.usdValue >= _minTokenUsdValue) && !isSpam;
|
|
|
|
await wallet.addErc20Token(token);
|
|
})());
|
|
}
|
|
|
|
await Future.wait(tokenChecks);
|
|
} catch (_) {}
|
|
}
|
|
}
|