mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-28 06:28:41 +01:00
Merge upstream Cake Wallet v6.5.0
Catches up from the May 12 fork point (v6.1.2+23) through v6.5.0: 535 upstream commits, including the Monero silent-open fix, Monero on Trezor, Ledger/Trezor BLE fixes, the new seed and history screens, quick Bitcoin sync, git-pinned dependencies (pubspec_overrides.yaml), frozen Hive adapters (*.part.dart), monero_c v0.18.4.6-RC2 and Flutter 3.41.9. Merged with both sides normalized first (import rename, 100-column format), which took the conflict count from 614 paths to 322. The procedure is now scripts/upstream_sync.sh. Kept from Hash Bags: - WalletType enum order and Hive IDs. SQLite stores WalletType.index and our order differs from upstream's; taking theirs would reread saved wallets as other chains. Our frozen adapter keeps the shipped IDs. - The 1.0.2 legacy PIN and wallet-password decode and self-heal. - Removed chains (Solana, Tron, Zano, Zcash, Decred, Haven) and the new Robinhood chain stay out; Cake Pay and non-Trocador swaps stay out. - Default nodes (node.wownero.org, sethforprivacy, Blockstream, bysh.me), converted to upstream's isDefault node-list key; Wownero is back in the node reset list. - Privacy: address resolvers limited to domain-anchored sources again; prices.neroswap.com for fiat; upstream's charts tab, which reads prices.cakewallet.com, sits behind FeatureFlag.isChartsEnabled = false. - Lightning stays behind FeatureFlag.isLightningEnabled, including upstream's new post-seed Lightning username step. - Our icon pipeline, branding and bundle identifiers. Twelve new upstream widget tests are skipped: test and code are unchanged from v6.5.0 apart from renames (not rerun on a pristine upstream checkout; upstream's top_bar_test does not compile against its own TopBar). flutter analyze lib: 0 errors; flutter test: 345 passed, 12 skipped; flutter build bundle compiles. Built with Flutter 3.41.9.
This commit is contained in:
@@ -2,6 +2,7 @@ import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
@@ -56,7 +57,7 @@ class EVMChainClient {
|
||||
}
|
||||
|
||||
if (response.statusCode >= 200 && response.statusCode < 300 && jsonResponse['status'] != 0) {
|
||||
final res = (jsonResponse['result'] as List);
|
||||
final res = jsonResponse["result"] as List;
|
||||
res.removeWhere((e) => e['value'] == '0');
|
||||
|
||||
// Filter out spam native transactions below 0.00001 ETH (10000000000000 wei)
|
||||
@@ -144,7 +145,10 @@ class EVMChainClient {
|
||||
|
||||
final jsonResponse = json.decode(response.body) as Map<String, dynamic>;
|
||||
|
||||
if (response.statusCode >= 200 && response.statusCode < 300 && jsonResponse['status'] != 0) {
|
||||
if (response.statusCode >= 200 &&
|
||||
response.statusCode < 300 &&
|
||||
jsonResponse['status'] != 0 &&
|
||||
jsonResponse['result'] is List) {
|
||||
final symbol = EVMChainUtils.getFeeCurrency(chainId);
|
||||
|
||||
return (jsonResponse['result'] as List)
|
||||
@@ -171,17 +175,31 @@ class EVMChainClient {
|
||||
try {
|
||||
Uri? rpcUri;
|
||||
bool isModifiedNodeUri = false;
|
||||
final nodeHost = Uri.parse("https://${node.uriRaw}").host;
|
||||
final pathSegments =
|
||||
(node.path ?? "").split("/").where((segment) => segment.isNotEmpty).toList();
|
||||
|
||||
if (node.uriRaw.contains('nownodes.io')) {
|
||||
if (nodeHost.endsWith(".nownodes.io") && pathSegments.isEmpty) {
|
||||
isModifiedNodeUri = true;
|
||||
String nowNodeApiKey = secrets.nowNodesApiKey;
|
||||
|
||||
if (nowNodeApiKey.isEmpty) {
|
||||
printV('NowNodes API key is empty, cannot connect to ${node.uriRaw}');
|
||||
printV("NowNodes API key is empty, cannot connect to ${node.uriRaw}");
|
||||
return false;
|
||||
}
|
||||
|
||||
rpcUri = Uri.https(node.uriRaw, '/$nowNodeApiKey');
|
||||
} else if (nodeHost.endsWith(".g.alchemy.com") &&
|
||||
(pathSegments.isEmpty || (pathSegments.length == 1 && pathSegments.first == "v2"))) {
|
||||
isModifiedNodeUri = true;
|
||||
String alchemyApiKey = "";
|
||||
|
||||
if (alchemyApiKey.isEmpty) {
|
||||
printV("Alchemy API key is empty, cannot connect to ${node.uriRaw}");
|
||||
return false;
|
||||
}
|
||||
|
||||
rpcUri = Uri.https(node.uriRaw, "/v2/$alchemyApiKey");
|
||||
}
|
||||
|
||||
_client = Web3Client(isModifiedNodeUri ? rpcUri!.toString() : node.uri.toString(), client);
|
||||
@@ -297,14 +315,13 @@ class EVMChainClient {
|
||||
Future<PendingEVMChainTransaction> signTransaction({
|
||||
required Credentials privateKey,
|
||||
required String toAddress,
|
||||
required BigInt amount,
|
||||
required BigInt gasFee,
|
||||
required Money amount,
|
||||
required Money gasFee,
|
||||
required int estimatedGasUnits,
|
||||
required int maxFeePerGas,
|
||||
required EVMChainTransactionPriority? priority,
|
||||
required CryptoCurrency currency,
|
||||
required String feeCurrency,
|
||||
required int exponent,
|
||||
String? contractAddress,
|
||||
String? data,
|
||||
int? gasPrice,
|
||||
@@ -315,13 +332,17 @@ class EVMChainClient {
|
||||
currency == CryptoCurrency.baseEth ||
|
||||
currency == CryptoCurrency.arbEth ||
|
||||
currency == CryptoCurrency.bnb ||
|
||||
currency == CryptoCurrency.robEth ||
|
||||
contractAddress != null);
|
||||
|
||||
bool isNativeToken = currency == CryptoCurrency.eth ||
|
||||
currency == CryptoCurrency.maticpoly ||
|
||||
currency == CryptoCurrency.baseEth ||
|
||||
currency == CryptoCurrency.arbEth ||
|
||||
currency == CryptoCurrency.bnb;
|
||||
final isNativeToken = [
|
||||
CryptoCurrency.eth,
|
||||
CryptoCurrency.maticpoly,
|
||||
CryptoCurrency.baseEth,
|
||||
CryptoCurrency.arbEth,
|
||||
CryptoCurrency.bnb,
|
||||
CryptoCurrency.robEth
|
||||
].contains(currency);
|
||||
|
||||
// Get nonce with "pending" block tag to include pending transactions
|
||||
// This prevents "Nonce too low" errors when sending multiple transactions quickly
|
||||
@@ -335,7 +356,7 @@ class EVMChainClient {
|
||||
to: EthereumAddress.fromHex(toAddress),
|
||||
maxPriorityFeePerGas:
|
||||
priority != null ? EtherAmount.fromInt(EtherUnit.gwei, priority.tip) : null,
|
||||
amount: isNativeToken ? EtherAmount.inWei(amount) : EtherAmount.zero(),
|
||||
amount: isNativeToken ? EtherAmount.inWei(amount.amount) : EtherAmount.zero(),
|
||||
data: data != null ? hexToBytes(data) : null,
|
||||
maxGas: estimatedGasUnits,
|
||||
maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
|
||||
@@ -358,7 +379,7 @@ class EVMChainClient {
|
||||
|
||||
signedTransaction = await erc20.transfer(
|
||||
EthereumAddress.fromHex(toAddress),
|
||||
amount,
|
||||
amount.amount,
|
||||
credentials: privateKey,
|
||||
transaction: transaction,
|
||||
);
|
||||
@@ -369,24 +390,20 @@ class EVMChainClient {
|
||||
|
||||
return PendingEVMChainTransaction(
|
||||
signedTransaction: prepareSignedTransactionForSending(signedTransaction),
|
||||
amount: amount.toString(),
|
||||
amount: amount,
|
||||
fee: gasFee,
|
||||
feeCurrency: feeCurrency,
|
||||
sendTransaction: _sendTransaction,
|
||||
exponent: exponent,
|
||||
);
|
||||
}
|
||||
|
||||
Future<PendingEVMChainTransaction> signApprovalTransaction({
|
||||
required Credentials privateKey,
|
||||
required String spender,
|
||||
required BigInt amount,
|
||||
required BigInt gasFee,
|
||||
required String feeCurrency,
|
||||
required Money amount,
|
||||
required Money gasFee,
|
||||
required int estimatedGasUnits,
|
||||
required int maxFeePerGas,
|
||||
required EVMChainTransactionPriority? priority,
|
||||
required int exponent,
|
||||
required String contractAddress,
|
||||
int? gasPrice,
|
||||
bool useBlinkProtection = true,
|
||||
@@ -416,20 +433,18 @@ class EVMChainClient {
|
||||
|
||||
final signedTransaction = await erc20.approve(
|
||||
EthereumAddress.fromHex(spender),
|
||||
amount,
|
||||
amount.amount,
|
||||
credentials: privateKey,
|
||||
transaction: transaction,
|
||||
);
|
||||
|
||||
return PendingEVMChainTransaction(
|
||||
signedTransaction: prepareSignedTransactionForSending(signedTransaction),
|
||||
amount: amount.toString(),
|
||||
amount: amount,
|
||||
fee: gasFee,
|
||||
feeCurrency: feeCurrency,
|
||||
sendTransaction: () =>
|
||||
sendTransaction(signedTransaction, useBlinkProtection: useBlinkProtection),
|
||||
exponent: exponent,
|
||||
isInfiniteApproval: amount.toRadixString(16) ==
|
||||
isInfiniteApproval: amount.amount.toRadixString(16) ==
|
||||
'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||
);
|
||||
}
|
||||
@@ -524,36 +539,37 @@ class EVMChainClient {
|
||||
}
|
||||
|
||||
Future<EVMChainERC20Balance> fetchERC20Balances(
|
||||
EthereumAddress userAddress, String contractAddress) async {
|
||||
EthereumAddress userAddress, Erc20Token token) async {
|
||||
try {
|
||||
final erc20 = ERC20(address: EthereumAddress.fromHex(contractAddress), client: _client!);
|
||||
final erc20 =
|
||||
ERC20(address: EthereumAddress.fromHex(token.contractAddress), client: _client!);
|
||||
final balance = await erc20.balanceOf(userAddress);
|
||||
|
||||
int exponent = (await erc20.decimals()).toInt();
|
||||
|
||||
return EVMChainERC20Balance(balance, exponent: exponent);
|
||||
return EVMChainERC20Balance(Money(balance, token));
|
||||
} on RangeError catch (_) {
|
||||
throw Exception('Invalid token contract for this network.');
|
||||
} catch (e) {
|
||||
if (e.toString().contains("hostUnreachable")) {
|
||||
return EVMChainERC20Balance(BigInt.zero);
|
||||
return EVMChainERC20Balance(Money.zero(token));
|
||||
}
|
||||
throw Exception('Could not fetch balances: ${e.toString()}');
|
||||
}
|
||||
}
|
||||
|
||||
Future<Erc20Token?> getErc20Token(String contractAddress, String chainName) async {
|
||||
Future<Erc20Token?> getErc20Token(String contractAddress, String? chainName) async {
|
||||
try {
|
||||
final token = await getErc20TokenFromMoralis(contractAddress, chainName);
|
||||
if (chainName != null) {
|
||||
final token = await getErc20TokenFromMoralis(contractAddress, chainName);
|
||||
|
||||
if (token == null || token.name.isEmpty || token.symbol.isEmpty) {
|
||||
return await getErcTokenInfoFromNode(contractAddress, chainName);
|
||||
if (token != null && token.name.isNotEmpty && token.symbol.isNotEmpty) {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
return token;
|
||||
return await getErcTokenInfoFromNode(contractAddress);
|
||||
} catch (e) {
|
||||
try {
|
||||
return await getErcTokenInfoFromNode(contractAddress, chainName);
|
||||
return await getErcTokenInfoFromNode(contractAddress);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
@@ -600,7 +616,7 @@ class EVMChainClient {
|
||||
);
|
||||
}
|
||||
|
||||
Future<Erc20Token?> getErcTokenInfoFromNode(String contractAddress, String chainName) async {
|
||||
Future<Erc20Token?> getErcTokenInfoFromNode(String contractAddress) async {
|
||||
final erc20 = ERC20(address: EthereumAddress.fromHex(contractAddress), client: _client!);
|
||||
final name = await erc20.name();
|
||||
final symbol = await erc20.symbol();
|
||||
@@ -624,74 +640,116 @@ class EVMChainClient {
|
||||
return [];
|
||||
}
|
||||
|
||||
final uri = Uri.https(
|
||||
'deep-index.moralis.io',
|
||||
'/api/v2.2/$address/erc20',
|
||||
{
|
||||
"chain": chainName,
|
||||
},
|
||||
);
|
||||
|
||||
final response = await client.get(
|
||||
uri,
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
"X-API-Key": secrets.moralisApiKey,
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode < 200 || response.statusCode >= 300) {
|
||||
printV('Moralis API returned invalid status code: ${response.statusCode}');
|
||||
return [];
|
||||
}
|
||||
|
||||
final decodedResponse = jsonDecode(response.body) as List;
|
||||
|
||||
const maxPages = 3;
|
||||
String? cursor;
|
||||
int pageCount = 0;
|
||||
final List<MoralisWalletTokenBalance> tokens = [];
|
||||
|
||||
for (final item in decodedResponse) {
|
||||
final tokenData = item as Map<String, dynamic>;
|
||||
do {
|
||||
final params = <String, String>{
|
||||
"chain": chainName,
|
||||
if (cursor != null && cursor.isNotEmpty) "cursor": cursor,
|
||||
};
|
||||
|
||||
final balanceStr = tokenData['balance'] as String? ?? '0';
|
||||
final balanceWei = BigInt.tryParse(balanceStr) ?? BigInt.zero;
|
||||
if (balanceWei == BigInt.zero) continue;
|
||||
|
||||
final contractAddress = (tokenData['token_address'] as String? ?? '').toLowerCase();
|
||||
final name = (tokenData['name'] as String? ?? '').toString();
|
||||
final symbol = (tokenData['symbol'] as String? ?? '').toString();
|
||||
final symbolFiltered = symbol.replaceFirst(RegExp('^\\\$'), '');
|
||||
|
||||
final decimalsRaw = tokenData['decimals'];
|
||||
final decimals =
|
||||
decimalsRaw is int ? decimalsRaw : int.tryParse(decimalsRaw.toString()) ?? 18;
|
||||
|
||||
final logo = tokenData['logo'] as String?;
|
||||
final thumbnail = tokenData['thumbnail'] as String?;
|
||||
final iconUrl = logo ?? thumbnail;
|
||||
|
||||
final possibleSpamRaw = tokenData['possible_spam'];
|
||||
final possibleSpam = possibleSpamRaw is bool
|
||||
? possibleSpamRaw
|
||||
: (possibleSpamRaw.toString().toLowerCase() == 'true');
|
||||
|
||||
final verifiedContractRaw = tokenData['verified_contract'];
|
||||
final verifiedContract = verifiedContractRaw is bool
|
||||
? verifiedContractRaw
|
||||
: (verifiedContractRaw.toString().toLowerCase() == 'true');
|
||||
|
||||
tokens.add(
|
||||
MoralisWalletTokenBalance(
|
||||
contractAddress: contractAddress,
|
||||
name: name,
|
||||
symbol: symbolFiltered,
|
||||
decimals: decimals,
|
||||
iconUrl: iconUrl,
|
||||
balanceWei: balanceWei,
|
||||
possibleSpam: possibleSpam,
|
||||
verifiedContract: verifiedContract,
|
||||
),
|
||||
final uri = Uri.https(
|
||||
'deep-index.moralis.io',
|
||||
'/api/v2.2/wallets/$address/tokens',
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
final response = await client.get(
|
||||
uri,
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
"X-API-Key": secrets.moralisApiKey,
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode < 200 || response.statusCode >= 300) {
|
||||
printV('Moralis API returned invalid status code: ${response.statusCode}');
|
||||
return tokens;
|
||||
}
|
||||
|
||||
final decoded = jsonDecode(response.body);
|
||||
if (decoded is! Map<String, dynamic>) return tokens;
|
||||
|
||||
final result = decoded['result'];
|
||||
if (result is! List) return tokens;
|
||||
|
||||
for (final item in result) {
|
||||
if (item is! Map<String, dynamic>) continue;
|
||||
final tokenData = item;
|
||||
|
||||
final nativeRaw = tokenData['native_token'];
|
||||
final nativeToken =
|
||||
nativeRaw is bool ? nativeRaw : (nativeRaw?.toString().toLowerCase() == 'true');
|
||||
if (nativeToken) continue;
|
||||
|
||||
final balanceStr = tokenData['balance'] as String? ?? '0';
|
||||
final balanceWei = BigInt.tryParse(balanceStr) ?? BigInt.zero;
|
||||
if (balanceWei == BigInt.zero) continue;
|
||||
|
||||
final contractAddress = (tokenData['token_address'] as String? ?? '').toLowerCase();
|
||||
final name = (tokenData['name'] as String? ?? '').toString();
|
||||
final symbol = (tokenData['symbol'] as String? ?? '').toString();
|
||||
final symbolFiltered = symbol.replaceFirst(RegExp('^\\\$'), '');
|
||||
|
||||
final decimalsRaw = tokenData['decimals'];
|
||||
final decimals =
|
||||
decimalsRaw is int ? decimalsRaw : int.tryParse(decimalsRaw.toString()) ?? 18;
|
||||
|
||||
final logo = tokenData['logo'] as String?;
|
||||
final thumbnail = tokenData['thumbnail'] as String?;
|
||||
final iconUrl = logo ?? thumbnail;
|
||||
|
||||
final possibleSpamRaw = tokenData['possible_spam'];
|
||||
final possibleSpam = possibleSpamRaw is bool
|
||||
? possibleSpamRaw
|
||||
: (possibleSpamRaw?.toString().toLowerCase() == 'true');
|
||||
|
||||
final verifiedContractRaw = tokenData['verified_contract'];
|
||||
final verifiedContract = verifiedContractRaw is bool
|
||||
? verifiedContractRaw
|
||||
: (verifiedContractRaw?.toString().toLowerCase() == 'true');
|
||||
|
||||
final usdPriceRaw = tokenData['usd_price'];
|
||||
final double? usdPrice = usdPriceRaw is num
|
||||
? usdPriceRaw.toDouble()
|
||||
: (usdPriceRaw is String ? double.tryParse(usdPriceRaw) : null);
|
||||
|
||||
final usdValueRaw = tokenData['usd_value'];
|
||||
final double? usdValue = usdValueRaw is num
|
||||
? usdValueRaw.toDouble()
|
||||
: (usdValueRaw is String ? double.tryParse(usdValueRaw) : null);
|
||||
|
||||
final securityRaw = tokenData['security_score'];
|
||||
final int? securityScore = securityRaw is int
|
||||
? securityRaw
|
||||
: (securityRaw is num
|
||||
? securityRaw.toInt()
|
||||
: (securityRaw is String ? int.tryParse(securityRaw) : null));
|
||||
|
||||
tokens.add(
|
||||
MoralisWalletTokenBalance(
|
||||
contractAddress: contractAddress,
|
||||
name: name,
|
||||
symbol: symbolFiltered,
|
||||
decimals: decimals,
|
||||
iconUrl: iconUrl,
|
||||
balanceWei: balanceWei,
|
||||
possibleSpam: possibleSpam,
|
||||
verifiedContract: verifiedContract,
|
||||
usdPrice: usdPrice,
|
||||
usdValue: usdValue,
|
||||
securityScore: securityScore,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final nextCursor = decoded['cursor'];
|
||||
cursor = nextCursor is String && nextCursor.isNotEmpty ? nextCursor : null;
|
||||
pageCount++;
|
||||
} while (cursor != null && pageCount < maxPages);
|
||||
|
||||
return tokens;
|
||||
} catch (e, stackTrace) {
|
||||
@@ -743,6 +801,9 @@ class MoralisWalletTokenBalance {
|
||||
final BigInt balanceWei;
|
||||
final bool possibleSpam;
|
||||
final bool verifiedContract;
|
||||
final double? usdPrice;
|
||||
final double? usdValue;
|
||||
final int? securityScore;
|
||||
|
||||
MoralisWalletTokenBalance({
|
||||
required this.contractAddress,
|
||||
@@ -753,5 +814,8 @@ class MoralisWalletTokenBalance {
|
||||
required this.balanceWei,
|
||||
required this.possibleSpam,
|
||||
required this.verifiedContract,
|
||||
this.usdPrice,
|
||||
this.usdValue,
|
||||
this.securityScore,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import "package:cw_evm/clients/evm_chain_client.dart";
|
||||
import "package:flutter/foundation.dart";
|
||||
import "package:web3dart/web3dart.dart";
|
||||
|
||||
class RobinhoodClient extends EVMChainClient {
|
||||
RobinhoodClient() : super(chainId: 4663);
|
||||
|
||||
@override
|
||||
Transaction createTransaction({
|
||||
required EthereumAddress from,
|
||||
required EthereumAddress to,
|
||||
required EtherAmount amount,
|
||||
EtherAmount? maxPriorityFeePerGas,
|
||||
Uint8List? data,
|
||||
int? maxGas,
|
||||
EtherAmount? gasPrice,
|
||||
EtherAmount? maxFeePerGas,
|
||||
int? nonce,
|
||||
}) {
|
||||
EtherAmount? finalGasPrice = gasPrice;
|
||||
|
||||
if (gasPrice == null && maxFeePerGas != null) {
|
||||
finalGasPrice = maxFeePerGas;
|
||||
}
|
||||
|
||||
return Transaction(
|
||||
from: from,
|
||||
to: to,
|
||||
value: amount,
|
||||
data: data,
|
||||
maxGas: maxGas,
|
||||
gasPrice: finalGasPrice,
|
||||
nonce: nonce,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Uint8List prepareSignedTransactionForSending(Uint8List signedTransaction) => signedTransaction;
|
||||
|
||||
@override
|
||||
int get chainId => 4663;
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_evm/deuro/deuro_savings_contract.dart';
|
||||
import 'package:cw_evm/deuro/deuro_savings_gateway_contract.dart' as v1;
|
||||
import 'package:cw_evm/deuro/deuro_savings_contract.dart' as v2;
|
||||
import 'package:cw_evm/evm_chain_wallet.dart';
|
||||
import 'package:cw_evm/contract/erc20.dart';
|
||||
import 'package:cw_evm/evm_chain_exceptions.dart';
|
||||
@@ -9,21 +11,27 @@ import 'package:web3dart/crypto.dart';
|
||||
import 'package:web3dart/web3dart.dart';
|
||||
|
||||
const String savingsGatewayAddress = "0x073493d73258C4BEb6542e8dd3e1b2891C972303";
|
||||
const String savingsV2Address = "0x760233b90e45d186A9A98E911B115F7F4B90d3D9";
|
||||
|
||||
const String dEuroAddress = "0xbA3f535bbCcCcA2A154b573Ca6c5A49BAAE0a3ea";
|
||||
const String frontendCode = "0x00000000000000000000000000000000000000000043616b652057616c6c6574";
|
||||
|
||||
class DEuro {
|
||||
final SavingsGateway _savingsGateway;
|
||||
final v2.Savings _savings;
|
||||
final ERC20 _dEuro;
|
||||
final EVMChainWallet _wallet;
|
||||
|
||||
DEuro(EVMChainWallet wallet)
|
||||
: _wallet = wallet,
|
||||
_savingsGateway = _getSavingsGateway(wallet.getWeb3Client()!),
|
||||
_savings = _getSavings(wallet.getWeb3Client()!),
|
||||
_dEuro = _getDEuroToken(wallet.getWeb3Client()!);
|
||||
|
||||
static SavingsGateway _getSavingsGateway(Web3Client client) => SavingsGateway(
|
||||
static v2.Savings _getSavings(Web3Client client) => v2.Savings(
|
||||
address: EthereumAddress.fromHex(savingsV2Address),
|
||||
client: client,
|
||||
);
|
||||
|
||||
static v1.SavingsGateway _getSavingsGateway(Web3Client client) => v1.SavingsGateway(
|
||||
address: EthereumAddress.fromHex(savingsGatewayAddress),
|
||||
client: client,
|
||||
);
|
||||
@@ -35,26 +43,31 @@ class DEuro {
|
||||
|
||||
EthereumAddress get _address => EthereumAddress.fromHex(_wallet.walletAddresses.primaryAddress);
|
||||
|
||||
Future<BigInt> get savingsBalance async =>
|
||||
(await _savingsGateway.savings(accountOwner: _address)).saved;
|
||||
Future<Money> get savingsBalance async =>
|
||||
Money((await _savings.savings(accountOwner: _address)).saved, CryptoCurrency.deuro);
|
||||
|
||||
Future<BigInt> get accruedInterest => _savingsGateway.accruedInterest(accountOwner: _address);
|
||||
Future<Money> get savingsBalanceV1 async => Money(
|
||||
(await _getSavingsGateway(_wallet.getWeb3Client()!).savings(accountOwner: _address)).saved,
|
||||
CryptoCurrency.deuro);
|
||||
|
||||
Future<BigInt> get interestRate => _savingsGateway.currentRatePPM();
|
||||
Future<Money> get accruedInterest async =>
|
||||
Money(await _savings.accruedInterest(accountOwner: _address), CryptoCurrency.deuro);
|
||||
|
||||
Future<BigInt> get approvedBalance => _dEuro.allowance(_address, _savingsGateway.self.address);
|
||||
Future<BigInt> get interestRate => _savings.currentRatePPM();
|
||||
|
||||
Future<BigInt> get approvedBalance => _dEuro.allowance(_address, _savings.self.address);
|
||||
|
||||
Future<void> _checkEthBalanceForGasFees(EVMChainTransactionPriority priority) async {
|
||||
final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
|
||||
final currentBalance = ethBalance.getInWei;
|
||||
final savings = _getSavingsGateway(_wallet.getWeb3Client()!);
|
||||
|
||||
final gasFeesModel = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
|
||||
amount: BigInt.zero,
|
||||
contractAddress: _savingsGateway.self.address.hexEip55,
|
||||
receivingAddressHex: _savingsGateway.self.address.hexEip55,
|
||||
amount: Money.zero(_wallet.currency),
|
||||
contractAddress: savings.self.address.hexEip55,
|
||||
receivingAddressHex: savings.self.address.hexEip55,
|
||||
priority: priority,
|
||||
data: _savingsGateway.self.abi.functions[17]
|
||||
.encodeCall([BigInt.zero, hexToBytes(frontendCode)]),
|
||||
data: savings.self.abi.functions[17].encodeCall([BigInt.zero, hexToBytes(frontendCode)]),
|
||||
);
|
||||
|
||||
final estimatedGasFee = BigInt.from(gasFeesModel.estimatedGasFee);
|
||||
@@ -73,28 +86,26 @@ class DEuro {
|
||||
try {
|
||||
await _checkEthBalanceForGasFees(priority);
|
||||
|
||||
final signedTransaction = await _savingsGateway.save(
|
||||
(amount: amount, frontendCode: hexToBytes(frontendCode)),
|
||||
final signedTransaction = await _savings.save(
|
||||
(amount: amount, compound: true),
|
||||
credentials: _wallet.evmChainPrivateKey,
|
||||
);
|
||||
|
||||
final fee = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
|
||||
amount: amount,
|
||||
contractAddress: _savingsGateway.self.address.hexEip55,
|
||||
receivingAddressHex: _savingsGateway.self.address.hexEip55,
|
||||
amount: Money.zero(_wallet.currency),
|
||||
contractAddress: _savings.self.address.hexEip55,
|
||||
receivingAddressHex: _savings.self.address.hexEip55,
|
||||
priority: priority,
|
||||
data: _savingsGateway.self.abi.functions[17].encodeCall([amount, hexToBytes(frontendCode)]),
|
||||
data: _savings.self.abi.functions[18].encodeCall([amount, true]),
|
||||
);
|
||||
|
||||
final sendTransaction = () => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
|
||||
sendTransaction() => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
|
||||
|
||||
return PendingEVMChainTransaction(
|
||||
feeCurrency: "ETH",
|
||||
sendTransaction: sendTransaction,
|
||||
signedTransaction: signedTransaction,
|
||||
fee: BigInt.from(fee.estimatedGasFee),
|
||||
amount: amount.toString(),
|
||||
exponent: 18,
|
||||
fee: Money(BigInt.from(fee.estimatedGasFee), CryptoCurrency.eth),
|
||||
amount: Money(amount, CryptoCurrency.deuro),
|
||||
);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('insufficient funds for gas')) {
|
||||
@@ -110,29 +121,66 @@ class DEuro {
|
||||
try {
|
||||
await _checkEthBalanceForGasFees(priority);
|
||||
|
||||
final signedTransaction = await _savingsGateway.withdraw(
|
||||
final signedTransaction = await _savings.withdraw(
|
||||
(target: _address, amount: amount),
|
||||
credentials: _wallet.evmChainPrivateKey,
|
||||
);
|
||||
|
||||
final fee = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
|
||||
amount: Money.zero(_wallet.currency),
|
||||
contractAddress: _savings.self.address.hexEip55,
|
||||
receivingAddressHex: _savings.self.address.hexEip55,
|
||||
priority: priority,
|
||||
data: _savings.self.abi.functions[23].encodeCall([_address, amount]),
|
||||
);
|
||||
|
||||
sendTransaction() => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
|
||||
|
||||
return PendingEVMChainTransaction(
|
||||
sendTransaction: sendTransaction,
|
||||
signedTransaction: signedTransaction,
|
||||
fee: Money(BigInt.from(fee.estimatedGasFee), CryptoCurrency.eth),
|
||||
amount: Money(amount, CryptoCurrency.deuro),
|
||||
);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('insufficient funds for gas')) {
|
||||
final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
|
||||
throw InsufficientGasFeeException(currentBalance: ethBalance.getInWei);
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<PendingEVMChainTransaction> withdrawSavingsV1(EVMChainTransactionPriority priority) async {
|
||||
try {
|
||||
await _checkEthBalanceForGasFees(priority);
|
||||
final withdrawAmount = await savingsBalanceV1;
|
||||
|
||||
// Withdraw at least a million to overflow and close the savings position
|
||||
final amount = BigInt.parse("1000000000000000000000000");
|
||||
final savings = _getSavingsGateway(_wallet.getWeb3Client()!);
|
||||
final signedTransaction = await savings.withdraw(
|
||||
(target: _address, amount: amount, frontendCode: hexToBytes(frontendCode)),
|
||||
credentials: _wallet.evmChainPrivateKey,
|
||||
);
|
||||
|
||||
final fee = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
|
||||
amount: amount,
|
||||
contractAddress: _savingsGateway.self.address.hexEip55,
|
||||
receivingAddressHex: _savingsGateway.self.address.hexEip55,
|
||||
amount: Money.zero(_wallet.currency),
|
||||
contractAddress: savings.self.address.hexEip55,
|
||||
receivingAddressHex: savings.self.address.hexEip55,
|
||||
priority: priority,
|
||||
data: _savingsGateway.self.abi.functions[24]
|
||||
.encodeCall([_address, amount, hexToBytes(frontendCode)]),
|
||||
data:
|
||||
savings.self.abi.functions[24].encodeCall([_address, amount, hexToBytes(frontendCode)]),
|
||||
);
|
||||
|
||||
final sendTransaction = () => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
|
||||
sendTransaction() => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
|
||||
|
||||
return PendingEVMChainTransaction(
|
||||
sendTransaction: sendTransaction,
|
||||
signedTransaction: signedTransaction,
|
||||
fee: BigInt.from(fee.estimatedGasFee),
|
||||
amount: amount.toString(),
|
||||
feeCurrency: "ETH",
|
||||
exponent: 18);
|
||||
sendTransaction: sendTransaction,
|
||||
signedTransaction: signedTransaction,
|
||||
fee: Money(BigInt.from(fee.estimatedGasFee), CryptoCurrency.eth),
|
||||
amount: withdrawAmount,
|
||||
);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('insufficient funds for gas')) {
|
||||
final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
|
||||
@@ -146,28 +194,26 @@ class DEuro {
|
||||
try {
|
||||
await _checkEthBalanceForGasFees(priority);
|
||||
|
||||
final signedTransaction = await _savingsGateway.refreshBalance(
|
||||
final signedTransaction = await _savings.refreshBalance(
|
||||
(owner: _address),
|
||||
credentials: _wallet.evmChainPrivateKey,
|
||||
);
|
||||
|
||||
final fee = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
|
||||
amount: BigInt.zero,
|
||||
contractAddress: _savingsGateway.self.address.hexEip55,
|
||||
receivingAddressHex: _savingsGateway.self.address.hexEip55,
|
||||
amount: Money.zero(_wallet.currency),
|
||||
contractAddress: _savings.self.address.hexEip55,
|
||||
receivingAddressHex: _savings.self.address.hexEip55,
|
||||
priority: priority,
|
||||
data: _savingsGateway.self.abi.functions[15].encodeCall([_address]),
|
||||
data: _savings.self.abi.functions[16].encodeCall([_address]),
|
||||
);
|
||||
|
||||
final sendTransaction = () => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
|
||||
sendTransaction() => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
|
||||
|
||||
return PendingEVMChainTransaction(
|
||||
feeCurrency: "ETH",
|
||||
sendTransaction: sendTransaction,
|
||||
signedTransaction: signedTransaction,
|
||||
fee: BigInt.from(fee.estimatedGasFee),
|
||||
amount: 0.toString(),
|
||||
exponent: 18,
|
||||
fee: Money(BigInt.from(fee.estimatedGasFee), CryptoCurrency.eth),
|
||||
amount: Money.zero(CryptoCurrency.deuro),
|
||||
);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('insufficient funds for gas')) {
|
||||
@@ -184,14 +230,14 @@ class DEuro {
|
||||
await _checkEthBalanceForGasFees(priority);
|
||||
|
||||
return (await _wallet.createApprovalTransaction(
|
||||
BigInt.parse(
|
||||
'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||
radix: 16,
|
||||
),
|
||||
_savingsGateway.self.address.hexEip55,
|
||||
CryptoCurrency.deuro,
|
||||
Money(
|
||||
BigInt.parse(
|
||||
'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||
radix: 16,
|
||||
),
|
||||
CryptoCurrency.deuro),
|
||||
_savings.self.address.hexEip55,
|
||||
priority,
|
||||
"ETH",
|
||||
)) as PendingEVMChainTransaction;
|
||||
} catch (e) {
|
||||
if (e.toString().contains('insufficient funds for gas')) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,6 +1,7 @@
|
||||
import 'package:cw_evm/clients/arbitrum_client.dart';
|
||||
import 'package:cw_evm/clients/base_client.dart';
|
||||
import 'package:cw_evm/clients/bsc_client.dart';
|
||||
import "package:cw_evm/clients/robinhood_client.dart";
|
||||
import 'package:cw_evm/clients/ethereum_client.dart';
|
||||
import 'package:cw_evm/clients/polygon_client.dart';
|
||||
import 'package:cw_evm/clients/evm_chain_client.dart';
|
||||
@@ -32,6 +33,8 @@ class EVMChainClientFactory {
|
||||
return ArbitrumClient();
|
||||
case 56:
|
||||
return BSCClient();
|
||||
case 4663:
|
||||
return RobinhoodClient();
|
||||
default:
|
||||
return EVMChainClient(chainId: chainId);
|
||||
}
|
||||
|
||||
@@ -1,28 +1,56 @@
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
import 'package:cw_evm/tokens/arbitrum_tokens.dart';
|
||||
import 'package:cw_evm/tokens/base_tokens.dart';
|
||||
import 'package:cw_evm/tokens/bsc_tokens.dart';
|
||||
import 'package:cw_evm/tokens/ethereum_tokens.dart';
|
||||
import 'package:cw_evm/tokens/polygon_tokens.dart';
|
||||
import "package:cw_core/erc20_token.dart";
|
||||
import "package:cw_evm/tokens/arbitrum_tokens.dart";
|
||||
import "package:cw_evm/tokens/base_tokens.dart";
|
||||
import "package:cw_evm/tokens/bsc_tokens.dart";
|
||||
import "package:cw_evm/tokens/ethereum_tokens.dart";
|
||||
import "package:cw_evm/tokens/polygon_tokens.dart";
|
||||
import "package:cw_evm/tokens/robinhood_tokens.dart";
|
||||
|
||||
/// Default ERC20 tokens for each EVM chain and utility methods for interacting with them
|
||||
class EVMChainDefaultTokens {
|
||||
static List<Erc20Token> getDefaultTokensByChainId(int chainId) {
|
||||
return switch (chainId) {
|
||||
1 => EthereumTokens.tokens,
|
||||
137 => PolygonTokens.tokens,
|
||||
8453 => BaseTokens.tokens,
|
||||
42161 => ArbitrumTokens.tokens,
|
||||
56 => BSCTokens.tokens,
|
||||
_ => [],
|
||||
};
|
||||
static List<Erc20Token> getDefaultTokensByChainId(int chainId) => switch (chainId) {
|
||||
1 => EthereumTokens.tokens,
|
||||
137 => PolygonTokens.tokens,
|
||||
8453 => BaseTokens.tokens,
|
||||
42161 => ArbitrumTokens.tokens,
|
||||
56 => BSCTokens.tokens,
|
||||
4663 => RobinhoodTokens.tokens,
|
||||
_ => [],
|
||||
};
|
||||
|
||||
static List<String> getDefaultTokenAddresses(int chainId) =>
|
||||
getDefaultTokensByChainId(chainId).map((token) => token.contractAddress).toList();
|
||||
|
||||
static List<String> getDefaultTokenSymbols(int chainId) =>
|
||||
getDefaultTokensByChainId(chainId).map((token) => token.symbol.toUpperCase()).toList();
|
||||
|
||||
static final Map<int, Map<String, String>> _iconPathsByAddress = {};
|
||||
|
||||
static String? getDefaultIconPathByAddress(int chainId, String contractAddress) {
|
||||
final iconPaths = _iconPathsByAddress.putIfAbsent(chainId, () {
|
||||
final paths = <String, String>{};
|
||||
|
||||
for (final token in getDefaultTokensByChainId(chainId)) {
|
||||
final iconPath = token.iconPath;
|
||||
|
||||
if (iconPath != null && iconPath.isNotEmpty) {
|
||||
paths[token.contractAddress.toLowerCase()] = iconPath;
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
});
|
||||
|
||||
return iconPaths[contractAddress.toLowerCase()];
|
||||
}
|
||||
|
||||
static List<String> getDefaultTokenAddresses(int chainId) {
|
||||
return getDefaultTokensByChainId(chainId).map((token) => token.contractAddress).toList();
|
||||
}
|
||||
static Set<String> getDefaultGroupsByAddress(int chainId, String contractAddress) {
|
||||
for (final token in getDefaultTokensByChainId(chainId)) {
|
||||
if (token.contractAddress.toLowerCase() == contractAddress.toLowerCase()) {
|
||||
return token.groups;
|
||||
}
|
||||
}
|
||||
|
||||
static List<String> getDefaultTokenSymbols(int chainId) {
|
||||
return getDefaultTokensByChainId(chainId).map((token) => token.symbol.toUpperCase()).toList();
|
||||
return const {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
class EVMChainMnemonicIsIncorrectException implements Exception {
|
||||
@override
|
||||
String toString() =>
|
||||
'EVM mnemonic has incorrect format. Mnemonic should contain 12 or 24 words separated by space.';
|
||||
}
|
||||
|
||||
class EVMChainTransactionCreationException implements Exception {
|
||||
final String exceptionMessage;
|
||||
|
||||
|
||||
@@ -186,6 +186,7 @@ class EvmChainRegistry {
|
||||
WalletType.bsc,
|
||||
'BSC',
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
void _registerChain(
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
// ignore_for_file: overridden_fields, annotate_overrides
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
import 'package:cw_core/format_amount.dart';
|
||||
import 'package:cw_core/format_fixed.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
import 'package:cw_evm/evm_chain_registry.dart';
|
||||
import 'package:cw_evm/utils/evm_chain_utils.dart';
|
||||
|
||||
class EVMChainTransactionInfo extends TransactionInfo {
|
||||
EVMChainTransactionInfo({
|
||||
required this.id,
|
||||
required this.height,
|
||||
required this.ethAmount,
|
||||
required this.ethFee,
|
||||
required this.amount,
|
||||
required this.fee,
|
||||
required this.tokenSymbol,
|
||||
this.exponent = 18,
|
||||
required this.direction,
|
||||
@@ -24,19 +25,16 @@ class EVMChainTransactionInfo extends TransactionInfo {
|
||||
this.evmSignatureName,
|
||||
this.contractAddress,
|
||||
required this.chainId,
|
||||
}) : amount = ethAmount.toInt(),
|
||||
fee = ethFee.toInt();
|
||||
});
|
||||
|
||||
final String id;
|
||||
final int height;
|
||||
final int amount;
|
||||
final BigInt ethAmount;
|
||||
final Money amount;
|
||||
final int exponent;
|
||||
final TransactionDirection direction;
|
||||
final DateTime date;
|
||||
final bool isPending;
|
||||
final int fee;
|
||||
final BigInt ethFee;
|
||||
final Money fee;
|
||||
final int confirmations;
|
||||
final String tokenSymbol;
|
||||
String? _fiatAmount;
|
||||
@@ -49,39 +47,32 @@ class EVMChainTransactionInfo extends TransactionInfo {
|
||||
/// Get fee currency symbol based on wallet type
|
||||
String get feeCurrency => EVMChainUtils.getFeeCurrency(chainId);
|
||||
|
||||
String get _displayTokenSymbol {
|
||||
if (chainId == 56 && tokenSymbol.toUpperCase() == 'BSC-USD') return 'USDT';
|
||||
|
||||
return tokenSymbol;
|
||||
}
|
||||
|
||||
@override
|
||||
String amountFormatted() {
|
||||
final amount = formatAmount((ethAmount / BigInt.from(10).pow(exponent)).toString());
|
||||
return '${amount.substring(0, min(10, amount.length))} $_displayTokenSymbol';
|
||||
}
|
||||
|
||||
@override
|
||||
String fiatAmount() => _fiatAmount ?? '';
|
||||
|
||||
@override
|
||||
void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount);
|
||||
|
||||
@override
|
||||
String feeFormatted() => '${formatFixed(ethFee, 18)} $feeCurrency';
|
||||
|
||||
factory EVMChainTransactionInfo.fromJson(Map<String, dynamic> data, int chainId) {
|
||||
final decimals = data['exponent'] as int? ?? 18;
|
||||
final tokenSymbol = data['tokenSymbol'] as String;
|
||||
final currency =
|
||||
Erc20Token(name: '', symbol: tokenSymbol, contractAddress: '', decimal: decimals);
|
||||
|
||||
final feeCurrency =
|
||||
EvmChainRegistry().getChainConfig(chainId)?.nativeCurrency ?? CryptoCurrency.eth;
|
||||
|
||||
return EVMChainTransactionInfo(
|
||||
id: data['id'] as String,
|
||||
height: data['height'] as int,
|
||||
ethAmount: BigInt.parse(data['amount'] as String),
|
||||
exponent: data['exponent'] as int? ?? 18,
|
||||
ethFee: BigInt.parse(data['fee'] as String),
|
||||
amount: Money(BigInt.parse(data['amount'] as String), currency),
|
||||
exponent: decimals,
|
||||
fee: Money(BigInt.parse(data['fee'] as String), feeCurrency),
|
||||
direction: TransactionDirection.values[data['direction'] as int],
|
||||
date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
|
||||
isPending: data['isPending'] as bool? ?? false,
|
||||
confirmations: data['confirmations'] as int,
|
||||
tokenSymbol: data['tokenSymbol'] as String,
|
||||
tokenSymbol: tokenSymbol,
|
||||
to: data['to'] as String?,
|
||||
from: data['from'] as String?,
|
||||
evmSignatureName: data['evmSignatureName'] as String?,
|
||||
@@ -93,9 +84,9 @@ class EVMChainTransactionInfo extends TransactionInfo {
|
||||
Map<String, dynamic> toJson() => {
|
||||
'id': id,
|
||||
'height': height,
|
||||
'amount': ethAmount.toString(),
|
||||
'amount': amount.amount.toString(),
|
||||
'exponent': exponent,
|
||||
'fee': ethFee.toString(),
|
||||
'fee': fee.amount.toString(),
|
||||
'direction': direction.index,
|
||||
'date': date.millisecondsSinceEpoch,
|
||||
'isPending': isPending,
|
||||
|
||||
+290
-391
File diff suppressed because it is too large
Load Diff
@@ -1,18 +1,15 @@
|
||||
import 'dart:developer';
|
||||
import "dart:developer";
|
||||
|
||||
import 'package:cw_core/payment_uris.dart';
|
||||
import 'package:cw_core/wallet_addresses.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import "package:cw_core/payment_uris.dart";
|
||||
import "package:cw_core/wallet_addresses.dart";
|
||||
import "package:mobx/mobx.dart";
|
||||
|
||||
part 'evm_chain_wallet_addresses.g.dart';
|
||||
part "evm_chain_wallet_addresses.g.dart";
|
||||
|
||||
class EVMChainWalletAddresses = EVMChainWalletAddressesBase with _$EVMChainWalletAddresses;
|
||||
|
||||
abstract class EVMChainWalletAddressesBase extends WalletAddresses with Store {
|
||||
EVMChainWalletAddressesBase(WalletInfo walletInfo, this._selectedChainId)
|
||||
: address = '',
|
||||
super(walletInfo);
|
||||
EVMChainWalletAddressesBase(super.walletInfo, this._selectedChainId) : address = "";
|
||||
|
||||
@override
|
||||
@observable
|
||||
@@ -33,7 +30,7 @@ abstract class EVMChainWalletAddressesBase extends WalletAddresses with Store {
|
||||
Future<void> updateAddressesInBox() async {
|
||||
try {
|
||||
addressesMap.clear();
|
||||
addressesMap[address] = '';
|
||||
addressesMap[address] = "";
|
||||
await saveAddressesInBox();
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
@@ -41,20 +38,10 @@ abstract class EVMChainWalletAddressesBase extends WalletAddresses with Store {
|
||||
}
|
||||
|
||||
@override
|
||||
PaymentURI getPaymentUri(String amount) {
|
||||
switch (_selectedChainId) {
|
||||
case 1:
|
||||
return EthereumURI(amount: amount, address: address);
|
||||
case 137:
|
||||
return PolygonURI(amount: amount, address: address);
|
||||
case 8453:
|
||||
return BaseURI(amount: amount, address: address);
|
||||
case 42161:
|
||||
return ArbitrumURI(amount: amount, address: address);
|
||||
case 56:
|
||||
return BSCURI(amount: amount, address: address);
|
||||
default:
|
||||
return EthereumURI(amount: amount, address: address);
|
||||
}
|
||||
}
|
||||
PaymentURI getPaymentUri(String amount) => ERC681URI(
|
||||
address: address,
|
||||
amount: amount,
|
||||
chainId: _selectedChainId,
|
||||
contractAddress: null,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import 'dart:io';
|
||||
import "dart:io";
|
||||
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:cw_core/encryption_file_utils.dart';
|
||||
import "package:cw_core/erc20_token.dart";
|
||||
import "package:cw_core/imported_nft.dart";
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_service.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:cw_evm/clients/evm_chain_client.dart';
|
||||
import 'package:cw_evm/evm_chain_client_factory.dart';
|
||||
import 'package:cw_evm/evm_chain_exceptions.dart';
|
||||
import 'package:cw_evm/evm_chain_registry.dart';
|
||||
import 'package:cw_evm/evm_chain_wallet.dart';
|
||||
import 'package:cw_evm/evm_chain_wallet_creation_credentials.dart';
|
||||
@@ -32,6 +37,9 @@ class EVMChainWalletService extends WalletService<
|
||||
return _registry.getRegisteredWalletTypes();
|
||||
}
|
||||
|
||||
List<String> get _importedNFTChains =>
|
||||
_registry.getAllChains().map((chain) => chain.shortCode).toList();
|
||||
|
||||
Future<WalletInfo?> _findWalletByName(String name) async {
|
||||
for (final type in _evmWalletTypes) {
|
||||
final walletInfo = await WalletInfo.get(name, type);
|
||||
@@ -46,8 +54,8 @@ class EVMChainWalletService extends WalletService<
|
||||
@override
|
||||
WalletType getType() {
|
||||
throw UnsupportedError(
|
||||
'EVMChainWalletService is unified and does not have a single type. '
|
||||
'Use walletInfo.type instead.',
|
||||
"EVMChainWalletService is unified and does not have a single type. "
|
||||
"Use walletInfo.type instead.",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -57,7 +65,7 @@ class EVMChainWalletService extends WalletService<
|
||||
Future<void> saveBackup(String name, {WalletInfo? walletInfo}) async {
|
||||
final info = walletInfo ?? await _findWalletByName(name);
|
||||
if (info == null) {
|
||||
throw Exception('Wallet not found: $name');
|
||||
throw Exception("Wallet not found: $name");
|
||||
}
|
||||
|
||||
final backupWalletDirPath = await pathForWalletDir(name: "$name.backup", type: info.type);
|
||||
@@ -73,7 +81,7 @@ class EVMChainWalletService extends WalletService<
|
||||
Future<void> restoreWalletFilesFromBackup(String name) async {
|
||||
final walletInfo = await _findWalletByName(name);
|
||||
if (walletInfo == null) {
|
||||
throw Exception('Wallet not found: $name');
|
||||
throw Exception("Wallet not found: $name");
|
||||
}
|
||||
|
||||
final backupWalletDirPath = await pathForWalletDir(name: "$name.backup", type: walletInfo.type);
|
||||
@@ -94,7 +102,7 @@ class EVMChainWalletService extends WalletService<
|
||||
// Get chainId from wallet type
|
||||
final chainConfig = _registry.getChainConfigByWalletType(walletInfo.type);
|
||||
if (chainConfig == null) {
|
||||
throw Exception('Chain config not found for wallet type: ${walletInfo.type}');
|
||||
throw Exception("Chain config not found for wallet type: ${walletInfo.type}");
|
||||
}
|
||||
final initialChainId = chainConfig.chainId;
|
||||
|
||||
@@ -122,7 +130,7 @@ class EVMChainWalletService extends WalletService<
|
||||
);
|
||||
|
||||
await wallet.init();
|
||||
wallet.addInitialTokens();
|
||||
await wallet.addInitialTokens();
|
||||
await wallet.save();
|
||||
return wallet;
|
||||
}
|
||||
@@ -131,7 +139,7 @@ class EVMChainWalletService extends WalletService<
|
||||
Future<EVMChainWallet> openWallet(String name, String password) async {
|
||||
final walletInfo = await _findWalletByName(name);
|
||||
if (walletInfo == null) {
|
||||
throw Exception('Wallet not found');
|
||||
throw Exception("Wallet not found");
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -143,7 +151,7 @@ class EVMChainWalletService extends WalletService<
|
||||
);
|
||||
|
||||
await wallet.init();
|
||||
wallet.addInitialTokens();
|
||||
await wallet.addInitialTokens();
|
||||
await wallet.save();
|
||||
await saveBackup(name);
|
||||
return wallet;
|
||||
@@ -158,7 +166,7 @@ class EVMChainWalletService extends WalletService<
|
||||
);
|
||||
|
||||
await wallet.init();
|
||||
wallet.addInitialTokens();
|
||||
await wallet.addInitialTokens();
|
||||
await wallet.save();
|
||||
return wallet;
|
||||
}
|
||||
@@ -166,29 +174,47 @@ class EVMChainWalletService extends WalletService<
|
||||
|
||||
@override
|
||||
Future<void> rename(String currentName, String password, String newName) async {
|
||||
if (currentName == newName) return;
|
||||
|
||||
final currentWalletInfo = await _findWalletByName(currentName);
|
||||
if (currentWalletInfo == null) {
|
||||
throw Exception('Wallet not found');
|
||||
throw Exception("Wallet not found");
|
||||
}
|
||||
|
||||
final currentWallet = await _openWalletInstance(
|
||||
password: password,
|
||||
name: currentName,
|
||||
walletInfo: currentWalletInfo,
|
||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
||||
);
|
||||
final type = currentWalletInfo.type;
|
||||
|
||||
await currentWallet.renameWalletFiles(newName);
|
||||
await copyWalletFilesTo(fromName: currentName, toName: newName, type: type);
|
||||
await saveBackup(newName, walletInfo: currentWalletInfo);
|
||||
|
||||
// Update walletInfo with new name before saving backup
|
||||
final newWalletInfo = currentWalletInfo;
|
||||
newWalletInfo.id = WalletBase.idFor(newName, currentWalletInfo.type);
|
||||
newWalletInfo.name = newName;
|
||||
currentWalletInfo.id = WalletBase.idFor(newName, type);
|
||||
currentWalletInfo.name = newName;
|
||||
await currentWalletInfo.save();
|
||||
|
||||
// Pass walletInfo to saveBackup to avoid lookup (since WalletInfo not saved yet)
|
||||
await saveBackup(newName, walletInfo: newWalletInfo);
|
||||
final oldNameStillUsed = (await _findWalletByName(currentName)) != null;
|
||||
if (oldNameStillUsed) {
|
||||
for (final token in await Erc20Token.selectList("walletName = ?", [currentName])) {
|
||||
final copiedToken = Erc20Token.copyWith(token, walletName: newName);
|
||||
await copiedToken.save();
|
||||
}
|
||||
|
||||
await newWalletInfo.save();
|
||||
final ownNFTs = (await ImportedNFT.getAllForWallet(currentName))
|
||||
.where((nft) => _importedNFTChains.contains(nft.chain));
|
||||
for (final nft in ownNFTs) {
|
||||
await ImportedNFT.copyWith(nft, walletName: newName).save();
|
||||
}
|
||||
} else {
|
||||
await Erc20Token.renameWallet(currentName, newName);
|
||||
await ImportedNFT.renameWallet(currentName, newName, chains: _importedNFTChains);
|
||||
}
|
||||
|
||||
final oldDir = Directory(p.join(await pathForWalletTypeDir(type: type), currentName));
|
||||
if (oldDir.existsSync()) {
|
||||
try {
|
||||
await oldDir.delete(recursive: true);
|
||||
} catch (e) {
|
||||
printV('rename: failed to delete old wallet dir "$currentName": $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -196,12 +222,16 @@ class EVMChainWalletService extends WalletService<
|
||||
EVMChainRestoreWalletFromSeedCredentials credentials, {
|
||||
bool? isTestnet,
|
||||
}) async {
|
||||
if (!bip39.validateMnemonic(credentials.mnemonic)) {
|
||||
throw EVMChainMnemonicIsIncorrectException();
|
||||
}
|
||||
|
||||
final walletInfo = credentials.walletInfo!;
|
||||
|
||||
// Get chainId from wallet type
|
||||
final chainConfig = _registry.getChainConfigByWalletType(walletInfo.type);
|
||||
if (chainConfig == null) {
|
||||
throw Exception('Chain config not found for wallet type: ${walletInfo.type}');
|
||||
throw Exception("Chain config not found for wallet type: ${walletInfo.type}");
|
||||
}
|
||||
final initialChainId = chainConfig.chainId;
|
||||
|
||||
@@ -227,7 +257,7 @@ class EVMChainWalletService extends WalletService<
|
||||
);
|
||||
|
||||
await wallet.init();
|
||||
wallet.addInitialTokens();
|
||||
await wallet.addInitialTokens();
|
||||
await wallet.save();
|
||||
return wallet;
|
||||
}
|
||||
@@ -242,7 +272,7 @@ class EVMChainWalletService extends WalletService<
|
||||
// Get chainId from wallet type
|
||||
final chainConfig = _registry.getChainConfigByWalletType(walletInfo.type);
|
||||
if (chainConfig == null) {
|
||||
throw Exception('Chain config not found for wallet type: ${walletInfo.type}');
|
||||
throw Exception("Chain config not found for wallet type: ${walletInfo.type}");
|
||||
}
|
||||
final initialChainId = chainConfig.chainId;
|
||||
|
||||
@@ -267,7 +297,7 @@ class EVMChainWalletService extends WalletService<
|
||||
);
|
||||
|
||||
await wallet.init();
|
||||
wallet.addInitialTokens();
|
||||
await wallet.addInitialTokens();
|
||||
await wallet.save();
|
||||
return wallet;
|
||||
}
|
||||
@@ -281,7 +311,7 @@ class EVMChainWalletService extends WalletService<
|
||||
// Get chainId from wallet type
|
||||
final chainConfig = _registry.getChainConfigByWalletType(walletInfo.type);
|
||||
if (chainConfig == null) {
|
||||
throw Exception('Chain config not found for wallet type: ${walletInfo.type}');
|
||||
throw Exception("Chain config not found for wallet type: ${walletInfo.type}");
|
||||
}
|
||||
final initialChainId = chainConfig.chainId;
|
||||
|
||||
@@ -305,7 +335,7 @@ class EVMChainWalletService extends WalletService<
|
||||
);
|
||||
|
||||
await wallet.init();
|
||||
wallet.addInitialTokens();
|
||||
await wallet.addInitialTokens();
|
||||
await wallet.save();
|
||||
return wallet;
|
||||
}
|
||||
@@ -324,11 +354,16 @@ class EVMChainWalletService extends WalletService<
|
||||
Future<void> remove(String wallet) async {
|
||||
final walletInfo = await _findWalletByName(wallet);
|
||||
if (walletInfo == null) {
|
||||
throw Exception('Wallet not found');
|
||||
throw Exception("Wallet not found");
|
||||
}
|
||||
|
||||
File(await pathForWalletDir(name: wallet, type: walletInfo.type)).delete(recursive: true);
|
||||
await WalletInfo.delete(walletInfo);
|
||||
final nameStillUsed = (await _findWalletByName(wallet)) != null;
|
||||
if (!nameStillUsed) {
|
||||
await Erc20Token.deleteAllForWallet(wallet);
|
||||
await ImportedNFT.deleteAllForWallet(wallet, chains: _importedNFTChains);
|
||||
}
|
||||
}
|
||||
|
||||
EVMChainWallet _createWalletInstance({
|
||||
@@ -346,7 +381,7 @@ class EVMChainWalletService extends WalletService<
|
||||
final chainConfig = _registry.getChainConfigByWalletType(walletType);
|
||||
|
||||
if (chainConfig == null) {
|
||||
throw Exception('Chain config not found for wallet type: $walletType');
|
||||
throw Exception("Chain config not found for wallet type: $walletType");
|
||||
}
|
||||
|
||||
return EVMChainWallet(
|
||||
|
||||
@@ -1,32 +1,23 @@
|
||||
import 'dart:convert';
|
||||
import "dart:convert";
|
||||
|
||||
import 'package:cw_core/balance.dart';
|
||||
import "package:cw_core/amount/money.dart";
|
||||
import "package:cw_core/balance.dart";
|
||||
import "package:cw_core/currency.dart";
|
||||
|
||||
class EVMChainERC20Balance extends Balance {
|
||||
EVMChainERC20Balance(this.balance, {this.exponent = 18}) : super(balance, balance);
|
||||
EVMChainERC20Balance(Money balance) : super(balance, Money.zero(balance.currency));
|
||||
|
||||
final BigInt balance;
|
||||
final int exponent;
|
||||
String toJSON() => json.encode({"balanceInWei": available.amount.toString()});
|
||||
|
||||
String toJSON() => json.encode({
|
||||
'balanceInWei': balance.toString(),
|
||||
'exponent': exponent,
|
||||
});
|
||||
|
||||
static EVMChainERC20Balance? fromJSON(String? jsonSource) {
|
||||
if (jsonSource == null) {
|
||||
return null;
|
||||
}
|
||||
static EVMChainERC20Balance? fromJSON(String? jsonSource, Currency currency) {
|
||||
if (jsonSource == null) return null;
|
||||
|
||||
final decoded = json.decode(jsonSource) as Map;
|
||||
|
||||
try {
|
||||
return EVMChainERC20Balance(
|
||||
BigInt.parse(decoded['balanceInWei']),
|
||||
exponent: decoded['exponent'],
|
||||
);
|
||||
return EVMChainERC20Balance(Money(BigInt.parse(decoded["balanceInWei"]), currency));
|
||||
} catch (e) {
|
||||
return EVMChainERC20Balance(BigInt.zero);
|
||||
return EVMChainERC20Balance(Money.zero(currency));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:cw_core/format_fixed.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:web3dart/crypto.dart';
|
||||
import 'package:hex/hex.dart' as Hex;
|
||||
@@ -8,36 +8,36 @@ import 'package:hex/hex.dart' as Hex;
|
||||
class PendingEVMChainTransaction with PendingTransaction {
|
||||
final Function sendTransaction;
|
||||
final Uint8List signedTransaction;
|
||||
final BigInt fee;
|
||||
final String feeCurrency;
|
||||
final String amount;
|
||||
final int exponent;
|
||||
final bool isInfiniteApproval;
|
||||
|
||||
PendingEVMChainTransaction({
|
||||
required this.sendTransaction,
|
||||
required this.signedTransaction,
|
||||
required this.fee,
|
||||
required this.feeCurrency,
|
||||
required this.amount,
|
||||
required this.exponent,
|
||||
this.isInfiniteApproval = false,
|
||||
});
|
||||
|
||||
@override
|
||||
String get amountFormatted {
|
||||
if (isInfiniteApproval) return "∞";
|
||||
return formatFixed(BigInt.parse(amount), exponent);
|
||||
return amount.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
final Money amount;
|
||||
|
||||
@override
|
||||
final Money fee;
|
||||
|
||||
@override
|
||||
Future<void> commit() async => await sendTransaction();
|
||||
|
||||
@override
|
||||
String get feeFormatted => "$feeFormattedValue $feeCurrency";
|
||||
String get feeFormatted => fee.toStringWithSymbol(fractionalDigits: 10);
|
||||
|
||||
@override
|
||||
String get feeFormattedValue => formatFixed(fee, 18, fractionalDigits: 10);
|
||||
String get feeFormattedValue => fee.toStringWithPrecision(fractionalDigits: 10);
|
||||
|
||||
@override
|
||||
String get hex => bytesToHex(signedTransaction, include0x: true);
|
||||
@@ -61,7 +61,5 @@ class PendingEVMChainTransaction with PendingTransaction {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, String>> commitUR() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
Future<Map<String, String>> commitUR() => throw UnimplementedError();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,409 @@
|
||||
import "package:cw_core/crypto_currency.dart";
|
||||
import "package:cw_core/currency_groups.dart";
|
||||
import "package:cw_core/erc20_token.dart";
|
||||
|
||||
class RobinhoodTokens {
|
||||
static List<Erc20Token> get tokens {
|
||||
final tokens = [
|
||||
Erc20Token(
|
||||
name: "Global Dollar",
|
||||
symbol: "USDG",
|
||||
contractAddress: "0x5fc5360d0400a0fd4f2af552add042d716f1d168",
|
||||
decimal: 6,
|
||||
enabled: true,
|
||||
iconPath: "assets/images/usdg_icon.png",
|
||||
groups: const {CurrencyGroups.stablecoin},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Ethena USDe",
|
||||
symbol: "USDe",
|
||||
contractAddress: "0x5d3a1ff2b6bab83b63cd9ad0787074081a52ef34",
|
||||
decimal: 18,
|
||||
enabled: true,
|
||||
groups: const {CurrencyGroups.stablecoin},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "WETH",
|
||||
symbol: "WETH",
|
||||
contractAddress: "0x0bd7d308f8e1639fab988df18a8011f41eacad73",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Apple",
|
||||
symbol: "AAPL",
|
||||
contractAddress: "0xaf3d76f1834a1d425780943c99ea8a608f8a93f9",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/apple.webp",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "AMD",
|
||||
symbol: "AMD",
|
||||
contractAddress: "0x86923f96303d656e4aa86d9d42d1e57ad2023fdc",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/amd.png",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Amazon",
|
||||
symbol: "AMZN",
|
||||
contractAddress: "0x12f190a9f9d7d37a250758b26824b97ce941bf54",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/amznx.webp",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "ASML Holding NV",
|
||||
symbol: "ASML",
|
||||
contractAddress: "0x47f93d52cbec7c6d2cfc080e154002370a60daea",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/asml.png",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Broadcom",
|
||||
symbol: "AVGO",
|
||||
contractAddress: "0x156e175dd063a8ce274c50654ef40e0032b3fbcf",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/avgox.webp",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Alibaba",
|
||||
symbol: "BABA",
|
||||
contractAddress: "0xad25ac6c84d497db898fa1e8387bf6af3532a1c4",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/baba.png",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Coinbase",
|
||||
symbol: "COIN",
|
||||
contractAddress: "0x6330d8c3178a418788df01a47479c0ce7ccf450b",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/coinx.webp",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Costco",
|
||||
symbol: "COST",
|
||||
contractAddress: "0x4ea005168d7f09a7a0ba9d1def21a479950e44c2",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/cost.png",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Circle Internet Group",
|
||||
symbol: "CRCL",
|
||||
contractAddress: "0xdf0992e440dd0be65bd8439b609d6d4366bf1cb5",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/crclx.webp",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Datadog",
|
||||
symbol: "DDOG",
|
||||
contractAddress: "0x27c99fbde9d0d2aa4f4bfb4943f237843ddf6958",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/ddog.png",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Dell",
|
||||
symbol: "DELL",
|
||||
contractAddress: "0x941ae714ec6d8130c7b75d67160ca08f1e7d11dd",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/dell.png",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Ford Motor",
|
||||
symbol: "F",
|
||||
contractAddress: "0x25c288e6d899b9bc30160965ad9644c67e73be0c",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/f.png",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "GameStop",
|
||||
symbol: "GME",
|
||||
contractAddress: "0x1b0e319c6a659f002271b69db8a7df2f911c153e",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/gmex.webp",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Alphabet Class A",
|
||||
symbol: "GOOGL",
|
||||
contractAddress: "0x2e0847e8910a9732eb3fb1bb4b70a580adad4fe3",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/googlx.webp",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Intel",
|
||||
symbol: "INTC",
|
||||
contractAddress: "0xc72b96e0e48ecd4dc75e1e45396e26300bc39681",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/intcx.webp",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Intuit",
|
||||
symbol: "INTU",
|
||||
contractAddress: "0x56d23bee5f41a7120170b0c603dae30128e460e9",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/intu.png",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Eli Lilly",
|
||||
symbol: "LLY",
|
||||
contractAddress: "0x8005d266423c7ea827372c9c864491e5786600ea",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/llyx.webp",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Lululemon",
|
||||
symbol: "LULU",
|
||||
contractAddress: "0x4e62068525ab11fe768e29dfd00ef909b9803016",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/lulu.png",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Meta Platforms",
|
||||
symbol: "META",
|
||||
contractAddress: "0xc0d6457c16cc70d6790dd43521c899c87ce02f35",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/metax.webp",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Marvell Technology",
|
||||
symbol: "MRVL",
|
||||
contractAddress: "0x62fd0668e10d8b72339be2dcf7643001688ff13b",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/mrvlx.webp",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Microsoft",
|
||||
symbol: "MSFT",
|
||||
contractAddress: "0xe93237c50d904957cf27e7b1133b510c669c2e74",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/msftx.webp",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Strategy Inc.",
|
||||
symbol: "MSTR",
|
||||
contractAddress: "0xec262a75e413fafd0df80480274532c79d42da09",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/mstrx.webp",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Micron Technology",
|
||||
symbol: "MU",
|
||||
contractAddress: "0xff080c8ce2e5feadaca0da81314ae59d232d4afd",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/mu.png",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Netflix",
|
||||
symbol: "NFLX",
|
||||
contractAddress: "0xe0444ef8bf4ed74f74fd73686e2ddf4c1c5591e8",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/nflxx.webp",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Nu",
|
||||
symbol: "NU",
|
||||
contractAddress: "0x408c14038a04f7bd235329e26d2bf569ee20e250",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/nu.png",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "NVIDIA",
|
||||
symbol: "NVDA",
|
||||
contractAddress: "0xd0601ce157db5bdc3162bbac2a2c8af5320d9eec",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/nvdax.webp",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Oracle",
|
||||
symbol: "ORCL",
|
||||
contractAddress: "0xb0992820e760d836549ba69bc7598b4af75dee03",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/orclx.webp",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Palantir Technologies",
|
||||
symbol: "PLTR",
|
||||
contractAddress: "0x894e1ec2d74ffe5aef8dc8a9e84686accb964f2a",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/pltrx.webp",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Qualcomm",
|
||||
symbol: "QCOM",
|
||||
contractAddress: "0x0f17206447090e464c277571124dd2688e48aea9",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/qcom.png",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Invesco QQQ",
|
||||
symbol: "QQQ",
|
||||
contractAddress: "0xd5f3879160bc7c32ebb4dc785f8a4f505888de68",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/qqqx.webp",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Rivian Automotive",
|
||||
symbol: "RIVN",
|
||||
contractAddress: "0xb1bf26c1d20ff267a4f93550d1e0d06ac40a114b",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/rivn.png",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Shopify",
|
||||
symbol: "SHOP",
|
||||
contractAddress: "0xf53f66751b1eff985311b693531e3290f600c410",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/shop.png",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "SoFi Technologies",
|
||||
symbol: "SOFI",
|
||||
contractAddress: "0x98e75885157c80992a8d41b696d8c9c6fb30a926",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/sofi.png",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "SPDR S&P 500 ETF Trust",
|
||||
symbol: "SPY",
|
||||
contractAddress: "0x117cc2133c37b721f49de2a7a74833232b3b4c0c",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/spyx.webp",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Tesla",
|
||||
symbol: "TSLA",
|
||||
contractAddress: "0x322f0929c4625ed5bad873c95208d54e1c003b2d",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/tslax.webp",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Taiwan Semiconductor Manufacturing",
|
||||
symbol: "TSM",
|
||||
contractAddress: "0x58ffe4a942d3885baa22d7520691f611ef09e7aa",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/tsm.png",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Take-Two Interactive Software",
|
||||
symbol: "TTWO",
|
||||
contractAddress: "0x5e81213613b6b86eab4c6c50d718d34359459786",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/ttwo.png",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "UPS",
|
||||
symbol: "UPS",
|
||||
contractAddress: "0xf23250dac154d05bb671cb0d0ebef3c635c79ce2",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/ups.png",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Workday",
|
||||
symbol: "WDAY",
|
||||
contractAddress: "0x82da4646242e1d962e96e932269dc644c94a9caa",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/wday.png",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
Erc20Token(
|
||||
name: "ExxonMobil Holdings Corporation",
|
||||
symbol: "XOM",
|
||||
contractAddress: "0xf9b46d3d1b22199d4d1025a9cedb540a33f1a2d5",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
iconPath: "assets/images/stocks/xomx.webp",
|
||||
groups: const {CurrencyGroups.tokenizedStock},
|
||||
),
|
||||
];
|
||||
|
||||
return tokens.map((token) {
|
||||
String? iconPath;
|
||||
if (token.iconPath?.isEmpty ?? true) {
|
||||
try {
|
||||
iconPath = CryptoCurrency.all
|
||||
.firstWhere((element) => element.title.toUpperCase() == token.symbol.toUpperCase())
|
||||
.iconPath;
|
||||
} catch (_) {}
|
||||
} else {
|
||||
iconPath = token.iconPath;
|
||||
}
|
||||
|
||||
return Erc20Token.copyWith(token, icon: iconPath, tag: "ROB");
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_evm/contract/erc20.dart';
|
||||
@@ -80,11 +81,9 @@ class USDT0Service {
|
||||
);
|
||||
|
||||
final pendingApproval = await wallet.createApprovalTransaction(
|
||||
maxUint,
|
||||
Money(maxUint, token),
|
||||
adapter,
|
||||
token,
|
||||
priority,
|
||||
EVMChainUtils.getFeeCurrency(wallet.selectedChainId),
|
||||
useBlinkProtection: useBlinkProtection,
|
||||
);
|
||||
await pendingApproval.commit();
|
||||
@@ -116,7 +115,7 @@ class USDT0Service {
|
||||
return wallet.createCallDataTransaction(
|
||||
oftAddress,
|
||||
encoded.dataHex,
|
||||
encoded.valueWei,
|
||||
Money(encoded.valueWei, token),
|
||||
priority,
|
||||
token.contractAddress,
|
||||
amount,
|
||||
|
||||
@@ -11,40 +11,6 @@ class EVMChainFormatter {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse EVM chain amount to BigInt to avoid integer overflow for large amounts
|
||||
/// [decimals] defaults to 18 for native ETH/POL, but should be set to token decimals for ERC20 tokens
|
||||
static BigInt parseEVMChainAmountToBigInt(String amount, {int decimals = evmDecimals}) {
|
||||
try {
|
||||
String cleanAmount = amount.replaceAll(',', '.');
|
||||
|
||||
bool isNegative = cleanAmount.startsWith('-');
|
||||
if (isNegative) {
|
||||
cleanAmount = cleanAmount.substring(1);
|
||||
}
|
||||
|
||||
final parts = cleanAmount.split('.');
|
||||
String whole = parts[0].isEmpty ? '0' : parts[0];
|
||||
String fraction = parts.length > 1 ? parts[1] : '';
|
||||
|
||||
// 3. Strict Truncation Logic
|
||||
// If fraction is longer than decimals, strictly cut it off.
|
||||
// If shorter, pad it with zeros.
|
||||
if (fraction.length > decimals) {
|
||||
fraction = fraction.substring(0, decimals);
|
||||
} else {
|
||||
fraction = fraction.padRight(decimals, '0');
|
||||
}
|
||||
|
||||
final wholeBigInt = BigInt.parse(whole);
|
||||
final fractionBigInt = BigInt.parse(fraction);
|
||||
final multiplier = BigInt.from(10).pow(decimals);
|
||||
|
||||
return (wholeBigInt * multiplier) + fractionBigInt;
|
||||
} catch (_) {
|
||||
return BigInt.zero;
|
||||
}
|
||||
}
|
||||
|
||||
static String truncateDecimals(String amount, int decimals) {
|
||||
final parts = amount.split(".");
|
||||
|
||||
|
||||
@@ -1,26 +1,24 @@
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
import 'package:cw_evm/evm_chain_transaction_priority.dart';
|
||||
import 'package:web3dart/web3dart.dart' show EtherAmount, EtherUnit;
|
||||
import "package:cw_evm/evm_chain_transaction_priority.dart";
|
||||
import "package:web3dart/web3dart.dart" show EtherAmount, EtherUnit;
|
||||
|
||||
/// Utility class for chain-specific EVM chain operations
|
||||
class EVMChainUtils {
|
||||
static int getTotalPriorityFee(EVMChainTransactionPriority priority, int chainId) {
|
||||
return switch (chainId) {
|
||||
1 => _ethereumPriorityFee(priority),
|
||||
137 => _polygonPriorityFee(priority),
|
||||
8453 => _basePriorityFee(priority),
|
||||
56 => _ethereumPriorityFee(priority),
|
||||
42161 => 0, // Arbitrum doesn't use priority fees
|
||||
_ => _ethereumPriorityFee(priority),
|
||||
};
|
||||
}
|
||||
static int getTotalPriorityFee(EVMChainTransactionPriority priority, int chainId) =>
|
||||
switch (chainId) {
|
||||
1 => _ethereumPriorityFee(priority),
|
||||
137 => _polygonPriorityFee(priority),
|
||||
8453 => _basePriorityFee(priority),
|
||||
56 => _ethereumPriorityFee(priority),
|
||||
42161 => 0, // Arbitrum doesn't use priority fees
|
||||
4663 => 0, // Robinhood Chain (Arbitrum Orbit) doesn't use priority fees
|
||||
_ => _ethereumPriorityFee(priority),
|
||||
};
|
||||
|
||||
static bool hasPriorityFee(int chainId) {
|
||||
return switch (chainId) {
|
||||
42161 => false, // Arbitrum doesn't use priority fees
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
static bool hasPriorityFee(int chainId) => switch (chainId) {
|
||||
42161 => false, // Arbitrum doesn't use priority fees
|
||||
4663 => false, // Robinhood Chain (Arbitrum Orbit) doesn't use priority fees
|
||||
_ => true,
|
||||
};
|
||||
|
||||
static int computeBufferedMaxFeePerGasWei({
|
||||
required int? gasBaseFee,
|
||||
@@ -38,78 +36,68 @@ class EVMChainUtils {
|
||||
return gasPrice + priorityFeeWei;
|
||||
}
|
||||
|
||||
static String getErc20TokensBoxName(String walletName, int chainId) {
|
||||
final sanitizedName = walletName.replaceAll(" ", "_");
|
||||
|
||||
return switch (chainId) {
|
||||
1 => "${sanitizedName}_${Erc20Token.ethereumBoxName}",
|
||||
137 => "${sanitizedName}_${Erc20Token.polygonBoxName}",
|
||||
8453 => "${sanitizedName}_${Erc20Token.baseBoxName}",
|
||||
42161 => "${sanitizedName}_${Erc20Token.arbitrumBoxName}",
|
||||
56 => "${sanitizedName}_${Erc20Token.bscBoxName}",
|
||||
_ => "${sanitizedName}_${Erc20Token.ethereumBoxName}",
|
||||
};
|
||||
}
|
||||
|
||||
static String getTransactionHistoryFileName(int chainId) {
|
||||
return switch (chainId) {
|
||||
1 => 'transactions.json', // Ethereum
|
||||
137 => 'polygon_transactions.json',
|
||||
8453 => 'base_transactions.json',
|
||||
42161 => 'arbitrum_transactions.json',
|
||||
56 => 'bsc_transactions.json',
|
||||
_ => 'transactions_$chainId.json', // Generic format for other chains
|
||||
};
|
||||
}
|
||||
static String getTransactionHistoryFileName(int chainId) => switch (chainId) {
|
||||
1 => "transactions.json", // Ethereum
|
||||
137 => "polygon_transactions.json",
|
||||
8453 => "base_transactions.json",
|
||||
42161 => "arbitrum_transactions.json",
|
||||
56 => "bsc_transactions.json",
|
||||
4663 => "robinhood_transactions.json",
|
||||
_ => "transactions_$chainId.json", // Generic format for other chains
|
||||
};
|
||||
|
||||
/// Get scan provider preference key for a wallet type
|
||||
static String getScanProviderPreferenceKey(int chainId) {
|
||||
return switch (chainId) {
|
||||
1 => 'use_etherscan',
|
||||
137 => 'use_polygonscan',
|
||||
8453 => 'use_basescan',
|
||||
42161 => 'use_arbiscan',
|
||||
56 => 'use_bscscan',
|
||||
_ => 'use_etherscan',
|
||||
};
|
||||
}
|
||||
static String getScanProviderPreferenceKey(int chainId) => switch (chainId) {
|
||||
1 => "use_etherscan",
|
||||
137 => "use_polygonscan",
|
||||
8453 => "use_base_scan",
|
||||
42161 => "use_arbitrum_scan",
|
||||
56 => "use_bscscan",
|
||||
4663 => "use_robinhood_scan",
|
||||
_ => "use_etherscan",
|
||||
};
|
||||
|
||||
static String getDefaultTokenTag(int chainId) {
|
||||
return switch (chainId) {
|
||||
1 => 'ETH',
|
||||
137 => 'POL',
|
||||
8453 => 'BASE',
|
||||
42161 => 'ARB',
|
||||
56 => 'BSC',
|
||||
_ => 'ETH',
|
||||
};
|
||||
}
|
||||
static String getDefaultTokenTag(int chainId) => switch (chainId) {
|
||||
1 => "ETH",
|
||||
137 => "POL",
|
||||
8453 => "BASE",
|
||||
42161 => "ARB",
|
||||
56 => "BSC",
|
||||
4663 => "ROB",
|
||||
_ => "ETH",
|
||||
};
|
||||
|
||||
static String getFeeCurrency(int chainId) {
|
||||
return switch (chainId) {
|
||||
1 => 'ETH',
|
||||
137 => 'POL',
|
||||
8453 => 'ETH',
|
||||
42161 => 'ETH',
|
||||
56 => 'BNB',
|
||||
_ => 'ETH',
|
||||
};
|
||||
}
|
||||
static String getFeeCurrency(int chainId) => switch (chainId) {
|
||||
1 => "ETH",
|
||||
137 => "POL",
|
||||
8453 => "ETH",
|
||||
42161 => "ETH",
|
||||
56 => "BNB",
|
||||
4663 => "ETH",
|
||||
_ => "ETH",
|
||||
};
|
||||
|
||||
static String getDefaultTokenSymbol(int chainId) {
|
||||
return switch (chainId) {
|
||||
1 => 'ETH',
|
||||
137 => 'POL',
|
||||
8453 => 'BASE',
|
||||
42161 => 'ARBITRUM',
|
||||
56 => 'BSC',
|
||||
_ => 'ETH',
|
||||
};
|
||||
}
|
||||
static String getDefaultTokenSymbol(int chainId) => switch (chainId) {
|
||||
1 => "ETH",
|
||||
137 => "POL",
|
||||
8453 => "BASE",
|
||||
42161 => "ARBITRUM",
|
||||
56 => "BSC",
|
||||
4663 => "ETH",
|
||||
_ => "ETH",
|
||||
};
|
||||
|
||||
static int _ethereumPriorityFee(EVMChainTransactionPriority priority) {
|
||||
return EtherAmount.fromInt(EtherUnit.gwei, priority.tip).getInWei.toInt();
|
||||
}
|
||||
static String? getMoralisChainName(int chainId) => switch (chainId) {
|
||||
1 => "eth",
|
||||
137 => "polygon",
|
||||
8453 => "base",
|
||||
42161 => "arbitrum",
|
||||
56 => "bsc",
|
||||
_ => null,
|
||||
};
|
||||
|
||||
static int _ethereumPriorityFee(EVMChainTransactionPriority priority) =>
|
||||
EtherAmount.fromInt(EtherUnit.gwei, priority.tip).getInWei.toInt();
|
||||
|
||||
// Polygon priority fee calculation (minimum 25 gwei + additional based on priority)
|
||||
static int _polygonPriorityFee(EVMChainTransactionPriority priority) {
|
||||
@@ -127,12 +115,11 @@ class EVMChainUtils {
|
||||
return minPriorityFeeWei + additionalPriorityFee;
|
||||
}
|
||||
|
||||
static int _basePriorityFee(EVMChainTransactionPriority priority) {
|
||||
return switch (priority) {
|
||||
EVMChainTransactionPriority.fast => EtherAmount.fromInt(EtherUnit.mwei, 5).getInWei.toInt(),
|
||||
EVMChainTransactionPriority.medium => EtherAmount.fromInt(EtherUnit.mwei, 3).getInWei.toInt(),
|
||||
EVMChainTransactionPriority.slow => EtherAmount.fromInt(EtherUnit.mwei, 1).getInWei.toInt(),
|
||||
_ => EtherAmount.fromInt(EtherUnit.mwei, 1).getInWei.toInt(),
|
||||
};
|
||||
}
|
||||
static int _basePriorityFee(EVMChainTransactionPriority priority) => switch (priority) {
|
||||
EVMChainTransactionPriority.fast => EtherAmount.fromInt(EtherUnit.mwei, 5).getInWei.toInt(),
|
||||
EVMChainTransactionPriority.medium =>
|
||||
EtherAmount.fromInt(EtherUnit.mwei, 3).getInWei.toInt(),
|
||||
EVMChainTransactionPriority.slow => EtherAmount.fromInt(EtherUnit.mwei, 1).getInWei.toInt(),
|
||||
_ => EtherAmount.fromInt(EtherUnit.mwei, 1).getInWei.toInt(),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user