mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-17 05:41:29 +02:00
Add Base Wallet (#2558)
* chore: Create Base package template * chore: Update configuration scripts and dependencies to include 'base' coin support * chore: Update pubspec.yaml, adjust SDK constraints, and add new dependencies * feat: Add support for base wallet type and cryptocurrency in related configurations * feat: Implement BaseWallet and DefaultBaseTokens classes for managing base cryptocurrency transactions * feat: Add BaseTransactionHistory and BaseTransactionInfo classes for handling base cryptocurrency transaction data * feat: Implement BaseClient, BaseWalletService, and BaseMnemonicIsIncorrectException * feat: Setup base proxy for main package and adjust token class name * feat: Setup initial base node configuration * feat: Integrate support for base wallet type across various functions and services * feat: Add base support to exchange and outputs * feat: Add base wallet type integration to wallet creation and node management * feat: Add base support to transaction details and transaction list item * feat: Implement BaseURI class and add base support across wallet creation and keys view models * feat: Add support for BaseScan preference in settings store * feat: Integrate Base support into various workflows and documentation * feat: Addingbase config to windows and secret key support * feat: Add base support to settings and address handling * chore: Move walletTypeToCurrency to another file and adjust across codebase * feat: Add 'basescan_history' string localization * feat: Remove Cryptocurrency.base, it doesn't have a base currency * feat: Set eth as native currency * feat: Enhance Base support across various sending, exchange, transaction details, fees, dashboard and balance * feat: Integrate Base wallet support in advanced privacy settings and home settings view models * feat: Add Base wallet support with icon and integration across providers, default settings migration, preferences, wallet connect, settings and token utilities * feat: Update currency handling to use baseEth for Base wallet integration across various components * fix: Update default token symbol to ETH in Base wallet transaction model * refactor: Change default currency references from BASE to ETH across client, transaction info, and wallet components * feat: Introduce normalizedIconPath for CryptoCurrency to standardize icon retrieval across wallet components * fix: Include baseEth as a native token in EVMChainClient for currency handling * chore: Update base_icon.png * feat: Add more nodes to base_node_list.yml * refactor: Update wallet icon retrieval to use normalizedIconPath * chore: Remove unused cases and cleanup impl. * feat: Add base proxy generation to configure * refactor: Update base proxy and generation code in configure.dart * feat: Add missing wallet and mono images * fix: Wrong image path for pr and add missing base qr image * feat: Enhance EVMTransactionErrorFeesHandler to parse new insufficient funds error format and calculate transaction fees in ETH and USD * feat: Add gas price safety buffer for Base chain transactions to improve fee estimation accuracy, also fixes send all not calculating correctly * Update model_generator.sh [skip ci] * Update assets/base_node_list.yml [skip ci] * Update cw_base/lib/default_base_erc20_tokens.dart [skip ci] * Review fixes * fixes --------- Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>
This commit is contained in:
co-authored by
OmarHatem
parent
025fc9154c
commit
2a74317aff
@@ -163,6 +163,7 @@ jobs:
|
||||
echo "const litecoinTestWalletSeeds = '${{ secrets.LITECOIN_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
|
||||
echo "const bitcoinCashTestWalletSeeds = '${{ secrets.BITCOIN_CASH_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
|
||||
echo "const polygonTestWalletSeeds = '${{ secrets.POLYGON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
|
||||
echo "const baseTestWalletSeeds = '${{ secrets.BASE_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
|
||||
echo "const solanaTestWalletSeeds = '${{ secrets.SOLANA_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
|
||||
echo "const solanaTestWalletSeeds2 = '${{ secrets.SOLANA_TEST_WALLET_SEEDS2 }}';" >> lib/.secrets.g.dart
|
||||
echo "const tronTestWalletSeeds = '${{ secrets.TRON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
|
||||
@@ -176,6 +177,7 @@ jobs:
|
||||
echo "const litecoinTestWalletReceiveAddress = '${{ secrets.LITECOIN_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
|
||||
echo "const bitcoinCashTestWalletReceiveAddress = '${{ secrets.BITCOIN_CASH_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
|
||||
echo "const polygonTestWalletReceiveAddress = '${{ secrets.POLYGON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
|
||||
echo "const baseTestWalletReceiveAddress = '${{ secrets.BASE_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
|
||||
echo "const solanaTestWalletReceiveAddress = '${{ secrets.SOLANA_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
|
||||
echo "const tronTestWalletReceiveAddress = '${{ secrets.TRON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
|
||||
echo "const nanoTestWalletReceiveAddress = '${{ secrets.NANO_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
|
||||
|
||||
@@ -20,6 +20,7 @@ jobs:
|
||||
"cw_mweb"
|
||||
"cw_nano"
|
||||
"cw_polygon"
|
||||
"cw_base"
|
||||
"cw_solana"
|
||||
"cw_tron"
|
||||
"cw_wownero"
|
||||
|
||||
@@ -157,6 +157,7 @@ jobs:
|
||||
echo "const litecoinTestWalletSeeds = '${{ secrets.LITECOIN_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
|
||||
echo "const bitcoinCashTestWalletSeeds = '${{ secrets.BITCOIN_CASH_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
|
||||
echo "const polygonTestWalletSeeds = '${{ secrets.POLYGON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
|
||||
echo "const baseTestWalletSeeds = '${{ secrets.BASE_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
|
||||
echo "const solanaTestWalletSeeds = '${{ secrets.SOLANA_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
|
||||
echo "const tronTestWalletSeeds = '${{ secrets.TRON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
|
||||
echo "const nanoTestWalletSeeds = '${{ secrets.NANO_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
|
||||
@@ -168,6 +169,7 @@ jobs:
|
||||
echo "const litecoinTestWalletReceiveAddress = '${{ secrets.LITECOIN_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
|
||||
echo "const bitcoinCashTestWalletReceiveAddress = '${{ secrets.BITCOIN_CASH_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
|
||||
echo "const polygonTestWalletReceiveAddress = '${{ secrets.POLYGON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
|
||||
echo "const baseTestWalletReceiveAddress = '${{ secrets.BASE_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
|
||||
echo "const solanaTestWalletReceiveAddress = '${{ secrets.SOLANA_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
|
||||
echo "const tronTestWalletReceiveAddress = '${{ secrets.TRON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
|
||||
echo "const nanoTestWalletReceiveAddress = '${{ secrets.NANO_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
|
||||
|
||||
@@ -150,6 +150,7 @@ jobs:
|
||||
echo "const litecoinTestWalletSeeds = '${{ secrets.LITECOIN_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
|
||||
echo "const bitcoinCashTestWalletSeeds = '${{ secrets.BITCOIN_CASH_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
|
||||
echo "const polygonTestWalletSeeds = '${{ secrets.POLYGON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
|
||||
echo "const baseTestWalletSeeds = '${{ secrets.BASE_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
|
||||
echo "const solanaTestWalletSeeds = '${{ secrets.SOLANA_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
|
||||
echo "const tronTestWalletSeeds = '${{ secrets.TRON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
|
||||
echo "const nanoTestWalletSeeds = '${{ secrets.NANO_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
|
||||
@@ -162,6 +163,7 @@ jobs:
|
||||
echo "const litecoinTestWalletReceiveAddress = '${{ secrets.LITECOIN_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
|
||||
echo "const bitcoinCashTestWalletReceiveAddress = '${{ secrets.BITCOIN_CASH_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
|
||||
echo "const polygonTestWalletReceiveAddress = '${{ secrets.POLYGON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
|
||||
echo "const baseTestWalletReceiveAddress = '${{ secrets.BASE_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
|
||||
echo "const solanaTestWalletReceiveAddress = '${{ secrets.SOLANA_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
|
||||
echo "const tronTestWalletReceiveAddress = '${{ secrets.TRON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
|
||||
echo "const nanoTestWalletReceiveAddress = '${{ secrets.NANO_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
|
||||
|
||||
@@ -141,6 +141,7 @@ lib/wownero/wownero.dart
|
||||
lib/zano/zano.dart
|
||||
lib/decred/decred.dart
|
||||
lib/dogecoin/dogecoin.dart
|
||||
lib/base/base.dart
|
||||
|
||||
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x.png
|
||||
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x~ipad.png
|
||||
|
||||
@@ -25,6 +25,7 @@ Cake Wallet includes support for several cryptocurrencies, including:
|
||||
* Litecoin (LTC)
|
||||
* Bitcoin Cash (BCH)
|
||||
* Polygon (POL)
|
||||
* Base (BASE)
|
||||
* Solana (SOL)
|
||||
* Tron (TRX)
|
||||
* Nano (XNO)
|
||||
|
||||
@@ -23,6 +23,7 @@ analyzer:
|
||||
lib/tron/cw_tron.dart,
|
||||
lib/wownero/cw_wownero.dart,
|
||||
lib/zano/cw_zano.dart,
|
||||
lib/base/cw_base.dart,
|
||||
]
|
||||
language:
|
||||
strict-casts: true
|
||||
|
||||
@@ -106,6 +106,9 @@
|
||||
<data android:scheme="dogecoin" />
|
||||
<data android:scheme="dogecoin-wallet" />
|
||||
<data android:scheme="dogecoin_wallet" />
|
||||
<data android:scheme="base" />
|
||||
<data android:scheme="base-wallet" />
|
||||
<data android:scheme="base_wallet" />
|
||||
</intent-filter>
|
||||
<!-- nano-gpt link scheme -->
|
||||
<intent-filter android:autoVerify="true">
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
-
|
||||
uri: base.nownodes.io
|
||||
is_default: true
|
||||
useSSL: true
|
||||
isEnabledForAutoSwitching: true
|
||||
-
|
||||
uri: base.llamarpc.com
|
||||
useSSL: true
|
||||
-
|
||||
uri: base-rpc.publicnode.com
|
||||
useSSL: true
|
||||
isEnabledForAutoSwitching: true
|
||||
-
|
||||
uri: 1rpc.io/base
|
||||
useSSL: true
|
||||
@@ -0,0 +1,5 @@
|
||||
<svg width="45" height="45" viewBox="0 0 45 45" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="1" y="1" width="43" height="43" rx="6.03125" fill="white"/>
|
||||
<rect x="1" y="1" width="43" height="43" rx="6.03125" stroke="black" stroke-width="2"/>
|
||||
<path d="M22.4774 35.4375C29.635 35.4375 35.4375 29.6451 35.4375 22.5C35.4375 15.3548 29.635 9.5625 22.4774 9.5625C15.6866 9.5625 10.1158 14.7762 9.5625 21.4125H26.6927V23.5874H9.5625C10.1158 30.2238 15.6866 35.4375 22.4774 35.4375Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 509 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15.9748 30.4C23.9416 30.4 30.4 23.9528 30.4 16C30.4 8.04709 23.9416 1.60001 15.9748 1.60001C8.41642 1.60001 2.2158 7.40309 1.59998 14.7895H20.6666V17.2104H1.59998C2.2158 24.5969 8.41642 30.4 15.9748 30.4Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 334 B |
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
+2
-2
@@ -1,5 +1,5 @@
|
||||
@echo off
|
||||
set cw_win_app_config=--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --dogecoin
|
||||
set cw_win_app_config=--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --dogecoin --base
|
||||
set cw_root=%cd%
|
||||
set cw_archive_name=Cake Wallet.zip
|
||||
set cw_archive_path=%cw_root%\%cw_archive_name%
|
||||
@@ -24,7 +24,7 @@ IF NOT EXIST "%secrets_file_path%" (
|
||||
) ELSE (echo === Using previously/already generated secrets file: %secrets_file_path% ===)
|
||||
|
||||
echo === Generating mobx models ===
|
||||
for /d %%i in (cw_core cw_monero cw_bitcoin cw_ethereum cw_evm cw_polygon cw_nano cw_bitcoin_cash cw_solana cw_tron .) do (
|
||||
for /d %%i in (cw_core cw_monero cw_bitcoin cw_ethereum cw_evm cw_polygon cw_nano cw_bitcoin_cash cw_solana cw_tron cw_base.) do (
|
||||
cd %%i
|
||||
call flutter pub get > nul
|
||||
call dart run build_runner build --delete-conflicting-outputs > nul
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
/pubspec.lock
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
build/
|
||||
@@ -0,0 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: "fcf2c11572af6f390246c056bc905eca609533a0"
|
||||
channel: "[user-branch]"
|
||||
|
||||
project_type: package
|
||||
@@ -0,0 +1,3 @@
|
||||
## 0.0.1
|
||||
|
||||
* TODO: Describe initial release.
|
||||
@@ -0,0 +1 @@
|
||||
TODO: Add your license here.
|
||||
@@ -0,0 +1,39 @@
|
||||
<!--
|
||||
This README describes the package. If you publish this package to pub.dev,
|
||||
this README's contents appear on the landing page for your package.
|
||||
|
||||
For information about how to write a good package README, see the guide for
|
||||
[writing package pages](https://dart.dev/tools/pub/writing-package-pages).
|
||||
|
||||
For general information about developing packages, see the Dart guide for
|
||||
[creating packages](https://dart.dev/guides/libraries/create-packages)
|
||||
and the Flutter guide for
|
||||
[developing packages and plugins](https://flutter.dev/to/develop-packages).
|
||||
-->
|
||||
|
||||
TODO: Put a short description of the package here that helps potential users
|
||||
know whether this package might be useful for them.
|
||||
|
||||
## Features
|
||||
|
||||
TODO: List what your package can do. Maybe include images, gifs, or videos.
|
||||
|
||||
## Getting started
|
||||
|
||||
TODO: List prerequisites and provide or point to information on how to
|
||||
start using the package.
|
||||
|
||||
## Usage
|
||||
|
||||
TODO: Include short and useful examples for package users. Add longer examples
|
||||
to `/example` folder.
|
||||
|
||||
```dart
|
||||
const like = 'sample';
|
||||
```
|
||||
|
||||
## Additional information
|
||||
|
||||
TODO: Tell users more about the package: where to find more information, how to
|
||||
contribute to the package, how to file issues, what response they can expect
|
||||
from the package authors, and more.
|
||||
@@ -0,0 +1,4 @@
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
@@ -0,0 +1,103 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:cw_evm/evm_chain_client.dart';
|
||||
import 'package:cw_evm/.secrets.g.dart' as secrets;
|
||||
import 'package:cw_evm/evm_chain_transaction_model.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:web3dart/web3dart.dart';
|
||||
|
||||
class BaseClient extends EVMChainClient {
|
||||
@override
|
||||
Transaction createTransaction({
|
||||
required EthereumAddress from,
|
||||
required EthereumAddress to,
|
||||
required EtherAmount amount,
|
||||
EtherAmount? maxPriorityFeePerGas,
|
||||
Uint8List? data,
|
||||
int? maxGas,
|
||||
EtherAmount? gasPrice,
|
||||
EtherAmount? maxFeePerGas,
|
||||
}) {
|
||||
EtherAmount? finalGasPrice = gasPrice;
|
||||
|
||||
if (gasPrice == null && maxFeePerGas != null) {
|
||||
// If we have EIP-1559 parameters but no legacy gasPrice, then use maxFeePerGas as gasPrice
|
||||
finalGasPrice = maxFeePerGas;
|
||||
}
|
||||
|
||||
return Transaction(
|
||||
from: from,
|
||||
to: to,
|
||||
value: amount,
|
||||
data: data,
|
||||
maxGas: maxGas,
|
||||
gasPrice: finalGasPrice,
|
||||
// maxFeePerGas: maxFeePerGas,
|
||||
// maxPriorityFeePerGas: maxPriorityFeePerGas,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Uint8List prepareSignedTransactionForSending(Uint8List signedTransaction) => signedTransaction;
|
||||
|
||||
@override
|
||||
int get chainId => 8453;
|
||||
|
||||
@override
|
||||
Future<List<EVMChainTransactionModel>> fetchTransactions(String address,
|
||||
{String? contractAddress}) async {
|
||||
try {
|
||||
final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
|
||||
"chainid": "$chainId",
|
||||
"module": "account",
|
||||
"action": contractAddress != null ? "tokentx" : "txlist",
|
||||
if (contractAddress != null) "contractaddress": contractAddress,
|
||||
"address": address,
|
||||
"apikey": secrets.etherScanApiKey,
|
||||
}));
|
||||
|
||||
final jsonResponse = json.decode(response.body) as Map<String, dynamic>;
|
||||
|
||||
if (response.statusCode >= 200 && response.statusCode < 300 && jsonResponse['status'] != 0) {
|
||||
final res = (jsonResponse['result'] as List);
|
||||
|
||||
res.removeWhere((e) => e['value'] == '0');
|
||||
|
||||
return res
|
||||
.map(
|
||||
(e) => EVMChainTransactionModel.fromJson(e as Map<String, dynamic>, 'ETH'),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<EVMChainTransactionModel>> fetchInternalTransactions(String address) async {
|
||||
try {
|
||||
final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
|
||||
"chainid": "$chainId",
|
||||
"module": "account",
|
||||
"action": "txlistinternal",
|
||||
"address": address,
|
||||
"apikey": secrets.etherScanApiKey,
|
||||
}));
|
||||
|
||||
final jsonResponse = json.decode(response.body) as Map<String, dynamic>;
|
||||
|
||||
if (response.statusCode >= 200 && response.statusCode < 300 && jsonResponse['status'] != 0) {
|
||||
return (jsonResponse['result'] as List)
|
||||
.map((e) => EVMChainTransactionModel.fromJson(e as Map<String, dynamic>, 'ETH'))
|
||||
.toList();
|
||||
}
|
||||
|
||||
return [];
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
class BaseMnemonicIsIncorrectException implements Exception {
|
||||
@override
|
||||
String toString() =>
|
||||
'Base mnemonic has incorrect format. Mnemonic should contain 12 or 24 words separated by space.';
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import 'dart:core';
|
||||
|
||||
import 'package:cw_evm/evm_chain_transaction_history.dart';
|
||||
import 'package:cw_evm/evm_chain_transaction_info.dart';
|
||||
import 'package:cw_base/base_transaction_info.dart';
|
||||
|
||||
class BaseTransactionHistory extends EVMChainTransactionHistory {
|
||||
BaseTransactionHistory({
|
||||
required super.walletInfo,
|
||||
required super.password,
|
||||
required super.encryptionFileUtils,
|
||||
});
|
||||
|
||||
@override
|
||||
String getTransactionHistoryFileName() => 'base_transactions.json';
|
||||
|
||||
@override
|
||||
EVMChainTransactionInfo getTransactionInfo(Map<String, dynamic> val) =>
|
||||
BaseTransactionInfo.fromJson(val);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_evm/evm_chain_transaction_info.dart';
|
||||
|
||||
class BaseTransactionInfo extends EVMChainTransactionInfo {
|
||||
BaseTransactionInfo({
|
||||
required super.id,
|
||||
required super.height,
|
||||
required super.ethAmount,
|
||||
required super.ethFee,
|
||||
required super.tokenSymbol,
|
||||
required super.direction,
|
||||
required super.isPending,
|
||||
required super.date,
|
||||
required super.confirmations,
|
||||
required super.to,
|
||||
required super.from,
|
||||
super.contractAddress,
|
||||
super.exponent,
|
||||
});
|
||||
|
||||
factory BaseTransactionInfo.fromJson(Map<String, dynamic> data) {
|
||||
return BaseTransactionInfo(
|
||||
id: data['id'] as String,
|
||||
height: data['height'] as int,
|
||||
ethAmount: BigInt.parse(data['amount']),
|
||||
exponent: data['exponent'] as int,
|
||||
ethFee: BigInt.parse(data['fee']),
|
||||
direction: parseTransactionDirectionFromInt(data['direction'] as int),
|
||||
date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
|
||||
isPending: data['isPending'] as bool,
|
||||
confirmations: data['confirmations'] as int,
|
||||
tokenSymbol: data['tokenSymbol'] as String,
|
||||
to: data['to'],
|
||||
from: data['from'],
|
||||
contractAddress: data['contractAddress'],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String get feeCurrency => 'ETH';
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:cw_base/base_client.dart';
|
||||
import 'package:cw_base/base_transaction_history.dart';
|
||||
import 'package:cw_base/base_transaction_info.dart';
|
||||
import 'package:cw_base/default_base_erc20_tokens.dart';
|
||||
import 'package:cw_core/cake_hive.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/encryption_file_utils.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_keys_file.dart';
|
||||
import 'package:cw_evm/evm_chain_transaction_history.dart';
|
||||
import 'package:cw_evm/evm_chain_transaction_info.dart';
|
||||
import 'package:cw_evm/evm_chain_transaction_model.dart';
|
||||
import 'package:cw_evm/evm_chain_wallet.dart';
|
||||
import 'package:cw_evm/evm_erc20_balance.dart';
|
||||
|
||||
class BaseWallet extends EVMChainWallet {
|
||||
BaseWallet({
|
||||
required super.walletInfo,
|
||||
required super.password,
|
||||
super.mnemonic,
|
||||
super.initialBalance,
|
||||
super.privateKey,
|
||||
required super.client,
|
||||
required super.encryptionFileUtils,
|
||||
super.passphrase,
|
||||
}) : super(nativeCurrency: CryptoCurrency.baseEth);
|
||||
|
||||
@override
|
||||
Future<void> initErc20TokensBox() async {
|
||||
final boxName = "${walletInfo.name.replaceAll(" ", "_")}_ ${Erc20Token.baseBoxName}";
|
||||
|
||||
evmChainErc20TokensBox = await CakeHive.openBox<Erc20Token>(boxName);
|
||||
}
|
||||
|
||||
@override
|
||||
void addInitialTokens([bool isMigration = false]) {
|
||||
final initialErc20Tokens = DefaultBaseErc20Tokens().initialBaseErc20Tokens;
|
||||
|
||||
for (final token in initialErc20Tokens) {
|
||||
if (evmChainErc20TokensBox.containsKey(token.contractAddress)) {
|
||||
final existingToken = evmChainErc20TokensBox.get(token.contractAddress);
|
||||
if (existingToken?.tag != token.tag) {
|
||||
evmChainErc20TokensBox.put(token.contractAddress, token);
|
||||
}
|
||||
} else {
|
||||
if (isMigration) token.enabled = false;
|
||||
evmChainErc20TokensBox.put(token.contractAddress, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
List<String> get getDefaultTokenContractAddresses =>
|
||||
DefaultBaseErc20Tokens().initialBaseErc20Tokens.map((e) => e.contractAddress).toList();
|
||||
|
||||
@override
|
||||
Future<bool> checkIfScanProviderIsEnabled() async {
|
||||
return (await sharedPrefs.future).getBool("use_basescan") ?? true;
|
||||
}
|
||||
|
||||
@override
|
||||
String getTransactionHistoryFileName() => 'base_transactions.json';
|
||||
|
||||
@override
|
||||
Erc20Token createNewErc20TokenObject(Erc20Token token, String? iconPath) {
|
||||
return Erc20Token(
|
||||
name: token.name,
|
||||
symbol: token.symbol,
|
||||
contractAddress: token.contractAddress,
|
||||
decimal: token.decimal,
|
||||
enabled: token.enabled,
|
||||
tag: token.tag ?? 'ETH',
|
||||
iconPath: iconPath,
|
||||
isPotentialScam: token.isPotentialScam,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
EVMChainTransactionInfo getTransactionInfo(
|
||||
EVMChainTransactionModel transactionModel, String address) {
|
||||
final model = BaseTransactionInfo(
|
||||
id: transactionModel.hash,
|
||||
height: transactionModel.blockNumber,
|
||||
ethAmount: transactionModel.amount,
|
||||
direction: transactionModel.from == address
|
||||
? TransactionDirection.outgoing
|
||||
: TransactionDirection.incoming,
|
||||
isPending: false,
|
||||
date: transactionModel.date,
|
||||
confirmations: transactionModel.confirmations,
|
||||
ethFee: BigInt.from(transactionModel.gasUsed) * transactionModel.gasPrice,
|
||||
exponent: transactionModel.tokenDecimal ?? 18,
|
||||
tokenSymbol: transactionModel.tokenSymbol ?? "ETH",
|
||||
to: transactionModel.to,
|
||||
from: transactionModel.from,
|
||||
contractAddress: transactionModel.contractAddress,
|
||||
);
|
||||
return model;
|
||||
}
|
||||
|
||||
@override
|
||||
EVMChainTransactionHistory setUpTransactionHistory(
|
||||
WalletInfo walletInfo, String password, EncryptionFileUtils encryptionFileUtils) {
|
||||
return BaseTransactionHistory(
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
encryptionFileUtils: encryptionFileUtils,
|
||||
);
|
||||
}
|
||||
|
||||
static Future<BaseWallet> open({
|
||||
required String name,
|
||||
required String password,
|
||||
required WalletInfo walletInfo,
|
||||
required EncryptionFileUtils encryptionFileUtils,
|
||||
}) async {
|
||||
final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type);
|
||||
final path = await pathForWallet(name: name, type: walletInfo.type);
|
||||
|
||||
Map<String, dynamic>? data;
|
||||
try {
|
||||
final jsonSource = await encryptionFileUtils.read(path: path, password: password);
|
||||
|
||||
data = json.decode(jsonSource) as Map<String, dynamic>;
|
||||
} catch (e) {
|
||||
if (!hasKeysFile) rethrow;
|
||||
}
|
||||
|
||||
final balance = EVMChainERC20Balance.fromJSON(data?['balance'] as String?) ??
|
||||
EVMChainERC20Balance(BigInt.zero);
|
||||
|
||||
final WalletKeysData keysData;
|
||||
// Migrate wallet from the old scheme to then new .keys file scheme
|
||||
if (!hasKeysFile) {
|
||||
final mnemonic = data!['mnemonic'] as String?;
|
||||
final privateKey = data['private_key'] as String?;
|
||||
final passphrase = data['passphrase'] as String?;
|
||||
|
||||
keysData = WalletKeysData(mnemonic: mnemonic, privateKey: privateKey, passphrase: passphrase);
|
||||
} else {
|
||||
keysData = await WalletKeysFile.readKeysFile(
|
||||
name,
|
||||
walletInfo.type,
|
||||
password,
|
||||
encryptionFileUtils,
|
||||
);
|
||||
}
|
||||
|
||||
return BaseWallet(
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
mnemonic: keysData.mnemonic,
|
||||
privateKey: keysData.privateKey,
|
||||
passphrase: keysData.passphrase,
|
||||
initialBalance: balance,
|
||||
client: BaseClient(),
|
||||
encryptionFileUtils: encryptionFileUtils,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:cw_core/encryption_file_utils.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_evm/evm_chain_wallet_creation_credentials.dart';
|
||||
import 'package:cw_evm/evm_chain_wallet_service.dart';
|
||||
import 'package:cw_base/base_wallet.dart';
|
||||
import 'package:cw_base/base_client.dart';
|
||||
import 'package:cw_base/base_mnemonics_exception.dart';
|
||||
|
||||
class BaseWalletService extends EVMChainWalletService<BaseWallet> {
|
||||
BaseWalletService(
|
||||
super.walletInfoSource,
|
||||
super.isDirect, {
|
||||
required this.client,
|
||||
});
|
||||
|
||||
late BaseClient client;
|
||||
|
||||
@override
|
||||
WalletType getType() => WalletType.base;
|
||||
|
||||
@override
|
||||
Future<BaseWallet> create(EVMChainNewWalletCredentials credentials, {bool? isTestnet}) async {
|
||||
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
|
||||
|
||||
final mnemonic = credentials.mnemonic ?? bip39.generateMnemonic(strength: strength);
|
||||
|
||||
final wallet = BaseWallet(
|
||||
walletInfo: credentials.walletInfo!,
|
||||
mnemonic: mnemonic,
|
||||
password: credentials.password!,
|
||||
passphrase: credentials.passphrase,
|
||||
client: client,
|
||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
||||
);
|
||||
|
||||
await wallet.init();
|
||||
wallet.addInitialTokens();
|
||||
await wallet.save();
|
||||
return wallet;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BaseWallet> openWallet(String name, String password) async {
|
||||
final walletInfo =
|
||||
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
|
||||
|
||||
try {
|
||||
final wallet = await BaseWallet.open(
|
||||
name: name,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
||||
);
|
||||
|
||||
await wallet.init();
|
||||
wallet.addInitialTokens(true);
|
||||
await wallet.save();
|
||||
saveBackup(name);
|
||||
return wallet;
|
||||
} catch (_) {
|
||||
await restoreWalletFilesFromBackup(name);
|
||||
|
||||
final wallet = await BaseWallet.open(
|
||||
name: name,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
||||
);
|
||||
|
||||
await wallet.init();
|
||||
wallet.addInitialTokens(true);
|
||||
await wallet.save();
|
||||
return wallet;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BaseWallet> restoreFromKeys(EVMChainRestoreWalletFromPrivateKey credentials,
|
||||
{bool? isTestnet}) async {
|
||||
final wallet = BaseWallet(
|
||||
password: credentials.password!,
|
||||
privateKey: credentials.privateKey,
|
||||
walletInfo: credentials.walletInfo!,
|
||||
client: client,
|
||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
||||
);
|
||||
|
||||
await wallet.init();
|
||||
wallet.addInitialTokens();
|
||||
await wallet.save();
|
||||
return wallet;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BaseWallet> restoreFromHardwareWallet(
|
||||
EVMChainRestoreWalletFromHardware credentials) async {
|
||||
credentials.walletInfo!.derivationInfo = DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/44'/60'/${credentials.hwAccountData.accountIndex}'/0/0");
|
||||
credentials.walletInfo!.hardwareWalletType = credentials.hardwareWalletType;
|
||||
credentials.walletInfo!.address = credentials.hwAccountData.address;
|
||||
|
||||
final wallet = BaseWallet(
|
||||
walletInfo: credentials.walletInfo!,
|
||||
password: credentials.password!,
|
||||
client: client,
|
||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
||||
);
|
||||
|
||||
await wallet.init();
|
||||
wallet.addInitialTokens();
|
||||
await wallet.save();
|
||||
|
||||
return wallet;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BaseWallet> restoreFromSeed(EVMChainRestoreWalletFromSeedCredentials credentials,
|
||||
{bool? isTestnet}) async {
|
||||
if (!bip39.validateMnemonic(credentials.mnemonic)) {
|
||||
throw BaseMnemonicIsIncorrectException();
|
||||
}
|
||||
|
||||
final wallet = BaseWallet(
|
||||
password: credentials.password!,
|
||||
mnemonic: credentials.mnemonic,
|
||||
walletInfo: credentials.walletInfo!,
|
||||
passphrase: credentials.passphrase,
|
||||
client: client,
|
||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
||||
);
|
||||
|
||||
await wallet.init();
|
||||
wallet.addInitialTokens();
|
||||
await wallet.save();
|
||||
|
||||
return wallet;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> rename(String currentName, String password, String newName) async {
|
||||
final currentWalletInfo = walletInfoSource.values
|
||||
.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
|
||||
final currentWallet = await BaseWallet.open(
|
||||
password: password,
|
||||
name: currentName,
|
||||
walletInfo: currentWalletInfo,
|
||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
||||
);
|
||||
|
||||
await currentWallet.renameWalletFiles(newName);
|
||||
await saveBackup(newName);
|
||||
|
||||
final newWalletInfo = currentWalletInfo;
|
||||
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
||||
newWalletInfo.name = newName;
|
||||
|
||||
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/// A Calculator.
|
||||
class Calculator {
|
||||
/// Returns [value] plus 1.
|
||||
int addOne(int value) => value + 1;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
|
||||
class DefaultBaseErc20Tokens {
|
||||
final List<Erc20Token> _defaultTokens = [
|
||||
Erc20Token(
|
||||
name: "USD Coin",
|
||||
symbol: "USDC",
|
||||
contractAddress: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
|
||||
decimal: 6,
|
||||
enabled: true,
|
||||
),
|
||||
Erc20Token(
|
||||
name: "USDe",
|
||||
symbol: "USDe",
|
||||
contractAddress: "0x5d3a1Ff2b6BAb83b63cd9AD0787074081a52ef34",
|
||||
decimal: 18,
|
||||
enabled: true,
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Dai",
|
||||
symbol: "DAI",
|
||||
contractAddress: "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
|
||||
decimal: 18,
|
||||
enabled: true,
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Bridged Tether USD",
|
||||
symbol: "USDT",
|
||||
contractAddress: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2",
|
||||
decimal: 6,
|
||||
enabled: true,
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Wrapped Ether",
|
||||
symbol: "WETH",
|
||||
contractAddress: "0x4200000000000000000000000000000000000006",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Wrapped BTC",
|
||||
symbol: "WBTC",
|
||||
contractAddress: "0x0555E30da8f98308EdB960aa94C0Db47230d2B9c",
|
||||
decimal: 8,
|
||||
enabled: false,
|
||||
),
|
||||
Erc20Token(
|
||||
name: "SPX6900",
|
||||
symbol: "SPX",
|
||||
contractAddress: "0x50dA645f148798F68EF2d7dB7C1CB22A6819bb2C",
|
||||
decimal: 8,
|
||||
enabled: false,
|
||||
),
|
||||
];
|
||||
|
||||
List<Erc20Token> get initialBaseErc20Tokens => _defaultTokens.map((token) {
|
||||
String? iconPath;
|
||||
try {
|
||||
iconPath = CryptoCurrency.all
|
||||
.firstWhere((element) => element.title.toUpperCase() == token.symbol.toUpperCase())
|
||||
.iconPath;
|
||||
} catch (_) {}
|
||||
|
||||
return Erc20Token.copyWith(token, iconPath, 'BASE');
|
||||
}).toList();
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
name: cw_base
|
||||
description: "Base package for Cake Wallet"
|
||||
version: 0.0.1
|
||||
publish_to: none
|
||||
homepage: https://cakewallet.com
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.6 <4.0.0'
|
||||
flutter: ">=1.17.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
cw_core:
|
||||
path: ../cw_core
|
||||
cw_ethereum:
|
||||
path: ../cw_ethereum
|
||||
cw_evm:
|
||||
path: ../cw_evm
|
||||
web3dart: ^2.7.1
|
||||
hive: ^2.2.3
|
||||
bip39: ^1.0.6
|
||||
collection: ^1.17.1
|
||||
|
||||
dependency_overrides:
|
||||
web3dart:
|
||||
git:
|
||||
url: https://github.com/cake-tech/web3dart.git
|
||||
ref: cake
|
||||
watcher: ^1.1.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^2.0.0
|
||||
build_runner: ^2.4.15
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
# The following section is specific to Flutter packages.
|
||||
flutter:
|
||||
|
||||
# To add assets to your package, add an assets section, like this:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
#
|
||||
# For details regarding assets in packages, see
|
||||
# https://flutter.dev/to/asset-from-package
|
||||
#
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/to/resolution-aware-images
|
||||
|
||||
# To add custom fonts to your package, add a fonts section here,
|
||||
# in this "flutter" section. Each entry in this list should have a
|
||||
# "family" key with the font family name, and a "fonts" key with a
|
||||
# list giving the asset and other descriptors for the font. For
|
||||
# example:
|
||||
# fonts:
|
||||
# - family: Schyler
|
||||
# fonts:
|
||||
# - asset: fonts/Schyler-Regular.ttf
|
||||
# - asset: fonts/Schyler-Italic.ttf
|
||||
# style: italic
|
||||
# - family: Trajan Pro
|
||||
# fonts:
|
||||
# - asset: fonts/TrajanPro.ttf
|
||||
# - asset: fonts/TrajanPro_Bold.ttf
|
||||
# weight: 700
|
||||
#
|
||||
# For details regarding fonts in packages, see
|
||||
# https://flutter.dev/to/font-from-package
|
||||
@@ -0,0 +1,12 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:cw_base/cw_base.dart';
|
||||
|
||||
void main() {
|
||||
test('adds one to input values', () {
|
||||
final calculator = Calculator();
|
||||
expect(calculator.addOne(2), 3);
|
||||
expect(calculator.addOne(-7), -6);
|
||||
expect(calculator.addOne(0), 1);
|
||||
});
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import 'package:cw_bitcoin/address_from_output.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_unspent.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
import 'package:cw_core/format_amount.dart';
|
||||
|
||||
@@ -115,6 +115,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
|
||||
CryptoCurrency.flip,
|
||||
CryptoCurrency.ndeps,
|
||||
CryptoCurrency.deps,
|
||||
CryptoCurrency.baseEth,
|
||||
];
|
||||
|
||||
static const havenCurrencies = [
|
||||
@@ -241,6 +242,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
|
||||
static const deps = CryptoCurrency(title: 'DEPS', tag: 'ETH', fullName: 'Decentralized Euro Protocol Share', raw: 101, name: 'deps', iconPath: 'assets/images/deps_icon.png', decimals: 18);
|
||||
static const kbtc = CryptoCurrency(title: 'KBTC', tag: 'ETH', fullName: 'Kraken Wrapped Bitcoin', raw: 102, name: 'kbtc', iconPath: 'assets/images/kbtc_icon.png', decimals: 8);
|
||||
static const cbbtc = CryptoCurrency(title: 'CBBTC', tag: 'ETH', fullName: 'Coinbase Wrapped BTC', raw: 103, name: 'cbbtc', iconPath: 'assets/images/cbbtc_icon.png', decimals: 8);
|
||||
static const baseEth = CryptoCurrency(title: 'ETH', tag: 'BASE', fullName: 'Ethereum', raw: 104, name: 'baseth', iconPath: 'assets/images/eth_icon.png', decimals: 18);
|
||||
|
||||
static final Map<int, CryptoCurrency> _rawCurrencyMap =
|
||||
[...all, ...havenCurrencies].fold<Map<int, CryptoCurrency>>(<int, CryptoCurrency>{}, (acc, item) {
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
CryptoCurrency currencyForWalletType(WalletType type, {bool? isTestnet}) {
|
||||
CryptoCurrency walletTypeToCryptoCurrency(WalletType type, {bool isTestnet = false}) {
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
return CryptoCurrency.xmr;
|
||||
case WalletType.bitcoin:
|
||||
if (isTestnet == true) {
|
||||
if (isTestnet) {
|
||||
return CryptoCurrency.tbtc;
|
||||
}
|
||||
return CryptoCurrency.btc;
|
||||
case WalletType.monero:
|
||||
return CryptoCurrency.xmr;
|
||||
case WalletType.litecoin:
|
||||
return CryptoCurrency.ltc;
|
||||
case WalletType.haven:
|
||||
return CryptoCurrency.xhv;
|
||||
case WalletType.ethereum:
|
||||
return CryptoCurrency.eth;
|
||||
case WalletType.base:
|
||||
return CryptoCurrency.baseEth;
|
||||
case WalletType.bitcoinCash:
|
||||
return CryptoCurrency.bch;
|
||||
case WalletType.nano:
|
||||
@@ -38,41 +40,15 @@ CryptoCurrency currencyForWalletType(WalletType type, {bool? isTestnet}) {
|
||||
return CryptoCurrency.doge;
|
||||
case WalletType.none:
|
||||
throw Exception(
|
||||
'Unexpected wallet type: ${type.toString()} for CryptoCurrency currencyForWalletType');
|
||||
'Unexpected wallet type: ${type.toString()} for CryptoCurrency walletTypeToCryptoCurrency');
|
||||
}
|
||||
}
|
||||
|
||||
WalletType? walletTypeForCurrency(CryptoCurrency currency) {
|
||||
switch (currency) {
|
||||
case CryptoCurrency.btc:
|
||||
return WalletType.bitcoin;
|
||||
case CryptoCurrency.xmr:
|
||||
return WalletType.monero;
|
||||
case CryptoCurrency.ltc:
|
||||
return WalletType.litecoin;
|
||||
case CryptoCurrency.xhv:
|
||||
return WalletType.haven;
|
||||
case CryptoCurrency.eth:
|
||||
return WalletType.ethereum;
|
||||
case CryptoCurrency.bch:
|
||||
return WalletType.bitcoinCash;
|
||||
case CryptoCurrency.nano:
|
||||
return WalletType.nano;
|
||||
case CryptoCurrency.banano:
|
||||
return WalletType.banano;
|
||||
case CryptoCurrency.maticpoly:
|
||||
return WalletType.polygon;
|
||||
case CryptoCurrency.sol:
|
||||
return WalletType.solana;
|
||||
case CryptoCurrency.trx:
|
||||
return WalletType.tron;
|
||||
case CryptoCurrency.wow:
|
||||
return WalletType.wownero;
|
||||
case CryptoCurrency.zano:
|
||||
return WalletType.zano;
|
||||
case CryptoCurrency.dcr:
|
||||
return WalletType.decred;
|
||||
default:
|
||||
return null;
|
||||
extension CryptoCurrencyForWalletType on CryptoCurrency {
|
||||
String? get normalizedIconPath {
|
||||
if (this == CryptoCurrency.baseEth) {
|
||||
return 'assets/images/base_icon.png';
|
||||
}
|
||||
return iconPath;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ class Erc20Token extends CryptoCurrency with HiveObjectMixin {
|
||||
static const boxName = 'Erc20Tokens';
|
||||
static const ethereumBoxName = 'EthereumErc20Tokens';
|
||||
static const polygonBoxName = 'PolygonErc20Tokens';
|
||||
static const baseBoxName = 'BaseErc20Tokens';
|
||||
|
||||
@override
|
||||
bool operator ==(other) =>
|
||||
|
||||
@@ -106,6 +106,7 @@ class Node extends HiveObject with Keyable {
|
||||
case WalletType.banano:
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
case WalletType.zano:
|
||||
@@ -169,6 +170,7 @@ class Node extends HiveObject with Keyable {
|
||||
case WalletType.bitcoinCash:
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
case WalletType.dogecoin:
|
||||
|
||||
@@ -24,7 +24,7 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
|
||||
|
||||
WalletType get type => walletInfo.type;
|
||||
|
||||
CryptoCurrency get currency => currencyForWalletType(type, isTestnet: isTestnet);
|
||||
CryptoCurrency get currency => walletTypeToCryptoCurrency(type, isTestnet: isTestnet);
|
||||
|
||||
String get id => walletInfo.id;
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ const walletTypes = [
|
||||
WalletType.zano,
|
||||
WalletType.decred,
|
||||
WalletType.dogecoin,
|
||||
WalletType.base,
|
||||
];
|
||||
|
||||
@HiveType(typeId: WALLET_TYPE_TYPE_ID)
|
||||
@@ -69,7 +70,10 @@ enum WalletType {
|
||||
decred,
|
||||
|
||||
@HiveField(15)
|
||||
dogecoin
|
||||
dogecoin,
|
||||
|
||||
@HiveField(16)
|
||||
base,
|
||||
}
|
||||
|
||||
int serializeToInt(WalletType type) {
|
||||
@@ -104,6 +108,8 @@ int serializeToInt(WalletType type) {
|
||||
return 13;
|
||||
case WalletType.dogecoin:
|
||||
return 14;
|
||||
case WalletType.base:
|
||||
return 15;
|
||||
case WalletType.none:
|
||||
return -1;
|
||||
}
|
||||
@@ -141,9 +147,10 @@ WalletType deserializeFromInt(int raw) {
|
||||
return WalletType.decred;
|
||||
case 14:
|
||||
return WalletType.dogecoin;
|
||||
case 15:
|
||||
return WalletType.base;
|
||||
default:
|
||||
throw Exception(
|
||||
'Unexpected token: $raw for WalletType deserializeFromInt');
|
||||
throw Exception('Unexpected token: $raw for WalletType deserializeFromInt');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,6 +186,8 @@ String walletTypeToString(WalletType type) {
|
||||
return 'Decred';
|
||||
case WalletType.dogecoin:
|
||||
return 'Dogecoin';
|
||||
case WalletType.base:
|
||||
return 'Base';
|
||||
case WalletType.none:
|
||||
return '';
|
||||
}
|
||||
@@ -216,52 +225,13 @@ String walletTypeToDisplayName(WalletType type) {
|
||||
return 'Decred (DCR)';
|
||||
case WalletType.dogecoin:
|
||||
return 'Dogecoin (DOGE)';
|
||||
case WalletType.base:
|
||||
return 'Base (BASE)';
|
||||
case WalletType.none:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
CryptoCurrency walletTypeToCryptoCurrency(WalletType type, {bool isTestnet = false}) {
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
return CryptoCurrency.xmr;
|
||||
case WalletType.bitcoin:
|
||||
if (isTestnet) {
|
||||
return CryptoCurrency.tbtc;
|
||||
}
|
||||
return CryptoCurrency.btc;
|
||||
case WalletType.litecoin:
|
||||
return CryptoCurrency.ltc;
|
||||
case WalletType.haven:
|
||||
return CryptoCurrency.xhv;
|
||||
case WalletType.ethereum:
|
||||
return CryptoCurrency.eth;
|
||||
case WalletType.bitcoinCash:
|
||||
return CryptoCurrency.bch;
|
||||
case WalletType.nano:
|
||||
return CryptoCurrency.nano;
|
||||
case WalletType.banano:
|
||||
return CryptoCurrency.banano;
|
||||
case WalletType.polygon:
|
||||
return CryptoCurrency.maticpoly;
|
||||
case WalletType.solana:
|
||||
return CryptoCurrency.sol;
|
||||
case WalletType.tron:
|
||||
return CryptoCurrency.trx;
|
||||
case WalletType.wownero:
|
||||
return CryptoCurrency.wow;
|
||||
case WalletType.zano:
|
||||
return CryptoCurrency.zano;
|
||||
case WalletType.decred:
|
||||
return CryptoCurrency.dcr;
|
||||
case WalletType.dogecoin:
|
||||
return CryptoCurrency.doge;
|
||||
case WalletType.none:
|
||||
throw Exception(
|
||||
'Unexpected wallet type: ${type.toString()} for CryptoCurrency walletTypeToCryptoCurrency');
|
||||
}
|
||||
}
|
||||
|
||||
WalletType? cryptoCurrencyToWalletType(CryptoCurrency type) {
|
||||
switch (type) {
|
||||
case CryptoCurrency.xmr:
|
||||
@@ -294,6 +264,8 @@ WalletType? cryptoCurrencyToWalletType(CryptoCurrency type) {
|
||||
return WalletType.decred;
|
||||
case CryptoCurrency.doge:
|
||||
return WalletType.dogecoin;
|
||||
case CryptoCurrency.baseEth:
|
||||
return WalletType.base;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
import 'package:cw_core/format_amount.dart';
|
||||
|
||||
@@ -39,15 +39,14 @@ abstract class EVMChainClient {
|
||||
Uri? rpcUri;
|
||||
bool isModifiedNodeUri = false;
|
||||
|
||||
if (node.uriRaw == 'eth.nownodes.io' || node.uriRaw == 'matic.nownodes.io') {
|
||||
if (node.uriRaw.contains('nownodes.io')) {
|
||||
isModifiedNodeUri = true;
|
||||
String nowNodeApiKey = secrets.nowNodesApiKey;
|
||||
|
||||
rpcUri = Uri.https(node.uriRaw, '/$nowNodeApiKey');
|
||||
}
|
||||
|
||||
_client =
|
||||
Web3Client(isModifiedNodeUri ? rpcUri!.toString() : node.uri.toString(), client);
|
||||
_client = Web3Client(isModifiedNodeUri ? rpcUri!.toString() : node.uri.toString(), client);
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
@@ -127,10 +126,11 @@ abstract class EVMChainClient {
|
||||
final gasEstimate = await _client!.estimateGas(
|
||||
sender: senderAddress,
|
||||
to: EthereumAddress.fromHex(contractAddress),
|
||||
data: data ?? transfer.encodeCall([
|
||||
toAddress,
|
||||
value.getInWei,
|
||||
]),
|
||||
data: data ??
|
||||
transfer.encodeCall([
|
||||
toAddress,
|
||||
value.getInWei,
|
||||
]),
|
||||
);
|
||||
|
||||
return gasEstimate.toInt();
|
||||
@@ -171,10 +171,10 @@ abstract class EVMChainClient {
|
||||
int? gasPrice,
|
||||
}) async {
|
||||
assert(currency == CryptoCurrency.eth ||
|
||||
currency == CryptoCurrency.maticpoly ||
|
||||
currency == CryptoCurrency.maticpoly || currency == CryptoCurrency.baseEth ||
|
||||
contractAddress != null);
|
||||
|
||||
bool isNativeToken = currency == CryptoCurrency.eth || currency == CryptoCurrency.maticpoly;
|
||||
bool isNativeToken = currency == CryptoCurrency.eth || currency == CryptoCurrency.maticpoly || currency == CryptoCurrency.baseEth;
|
||||
|
||||
final Transaction transaction = createTransaction(
|
||||
from: privateKey.address,
|
||||
@@ -233,7 +233,6 @@ abstract class EVMChainClient {
|
||||
required String contractAddress,
|
||||
int? gasPrice,
|
||||
}) async {
|
||||
|
||||
final Transaction transaction = createTransaction(
|
||||
from: privateKey.address,
|
||||
to: EthereumAddress.fromHex(contractAddress),
|
||||
@@ -264,7 +263,8 @@ abstract class EVMChainClient {
|
||||
feeCurrency: feeCurrency,
|
||||
sendTransaction: () => sendTransaction(signedTransaction),
|
||||
exponent: exponent,
|
||||
isInfiniteApproval: amount.toRadixString(16) == 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||
isInfiniteApproval: amount.toRadixString(16) ==
|
||||
'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -342,7 +342,7 @@ abstract class EVMChainClient {
|
||||
final erc20 = ERC20(address: EthereumAddress.fromHex(contractAddress), client: _client!);
|
||||
final balance = await erc20.balanceOf(userAddress);
|
||||
|
||||
int exponent = (await erc20.decimals()).toInt();
|
||||
int exponent = (await erc20.decimals()).toInt();
|
||||
|
||||
return EVMChainERC20Balance(balance, exponent: exponent);
|
||||
} on RangeError catch (_) {
|
||||
|
||||
@@ -171,25 +171,29 @@ abstract class EVMChainWalletBase
|
||||
try {
|
||||
// Check native balance
|
||||
await _client.getBalance(_evmChainPrivateKey.address, throwOnError: true);
|
||||
|
||||
|
||||
// Check USDC token balance
|
||||
String usdcContractAddress;
|
||||
if (_client.chainId == 1) {
|
||||
// Ethereum mainnet
|
||||
usdcContractAddress = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48";
|
||||
} else if (_client.chainId == 137) {
|
||||
// Polygon mainnet
|
||||
usdcContractAddress = "0x2791bca1f2de4661ed88a30c99a7a9449aa84174";
|
||||
} else {
|
||||
// If we are not on Ethereum or Polygon, we skip ERC20 token check
|
||||
return true;
|
||||
|
||||
switch (_client.chainId) {
|
||||
case 1:
|
||||
usdcContractAddress = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48";
|
||||
break;
|
||||
case 137:
|
||||
usdcContractAddress = "0x2791bca1f2de4661ed88a30c99a7a9449aa84174";
|
||||
break;
|
||||
case 8453:
|
||||
usdcContractAddress = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
await _client.fetchERC20Balances(
|
||||
_evmChainPrivateKey.address,
|
||||
usdcContractAddress,
|
||||
);
|
||||
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
@@ -542,6 +546,32 @@ abstract class EVMChainWalletBase
|
||||
maxFeePerGasForTransaction = gasFeesModel.maxFeePerGas;
|
||||
|
||||
if (output.sendAll && transactionCurrency is! Erc20Token) {
|
||||
if (_client.chainId == 8453) {
|
||||
// Add a safety buffer for Base chain to account for gas price fluctuations
|
||||
// Use 1% buffer or minimum 1000 wei (0.000001 ETH), whichever is higher
|
||||
final gasBufferPercent =
|
||||
estimatedFeesForTransaction * BigInt.from(101) ~/ BigInt.from(100);
|
||||
final gasBufferMin = estimatedFeesForTransaction + BigInt.from(1000);
|
||||
final gasBuffer = gasBufferPercent > gasBufferMin ? gasBufferPercent : gasBufferMin;
|
||||
|
||||
totalAmount = (currencyBalance.balance - gasBuffer);
|
||||
|
||||
// Re-estimate gas with the correct amount to get more accurate gas estimation
|
||||
final refinedGasFeesModel = await calculateActualEstimatedFeeForCreateTransaction(
|
||||
amount: totalAmount,
|
||||
receivingAddressHex: toAddress,
|
||||
priority: _credentials.priority!,
|
||||
contractAddress: contractAddress,
|
||||
);
|
||||
|
||||
// Use the higher of the two gas estimations to be safe
|
||||
final refinedGasFee = BigInt.from(refinedGasFeesModel.estimatedGasFee);
|
||||
estimatedFeesForTransaction = refinedGasFee > gasBuffer ? refinedGasFee : gasBuffer;
|
||||
estimatedGasUnitsForTransaction = refinedGasFeesModel.estimatedGasUnits;
|
||||
maxFeePerGasForTransaction = refinedGasFeesModel.maxFeePerGas;
|
||||
}
|
||||
|
||||
// Final amount calculation with the higher gas estimation
|
||||
totalAmount = (currencyBalance.balance - estimatedFeesForTransaction);
|
||||
}
|
||||
|
||||
@@ -569,11 +599,7 @@ abstract class EVMChainWalletBase
|
||||
gasFee: estimatedFeesForTransaction,
|
||||
priority: _credentials.priority!,
|
||||
currency: transactionCurrency,
|
||||
feeCurrency: switch (_client.chainId) {
|
||||
1 => "ETH",
|
||||
137 => "POL",
|
||||
_ => ""
|
||||
},
|
||||
feeCurrency: switch (_client.chainId) { 1 => "ETH", 137 => "POL", 8453 => "ETH", _ => "ETH" },
|
||||
maxFeePerGas: maxFeePerGasForTransaction,
|
||||
exponent: exponent,
|
||||
contractAddress:
|
||||
@@ -890,6 +916,8 @@ abstract class EVMChainWalletBase
|
||||
/// EtherScan for Ethereum.
|
||||
///
|
||||
/// PolygonScan for Polygon.
|
||||
///
|
||||
/// BaseScan for Base.
|
||||
void updateScanProviderUsageState(bool isEnabled) {
|
||||
if (isEnabled) {
|
||||
_updateTransactions();
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'dart:io';
|
||||
import 'package:cake_wallet/entities/seed_type.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_utils.dart';
|
||||
import 'package:cake_wallet/wallet_types.g.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
@@ -109,7 +110,7 @@ class CommonTestFlows {
|
||||
|
||||
void confirmAllAvailableWalletTypeIconsDisplayCorrectly() {
|
||||
for (var walletType in availableWalletTypes) {
|
||||
final imageUrl = walletTypeToCryptoCurrency(walletType).iconPath;
|
||||
final imageUrl = walletTypeToCryptoCurrency(walletType).normalizedIconPath;
|
||||
|
||||
final walletIconFinder = find.image(
|
||||
Image.asset(
|
||||
@@ -353,6 +354,8 @@ class CommonTestFlows {
|
||||
return secrets.polygonTestWalletSeeds;
|
||||
case WalletType.solana:
|
||||
return secrets.solanaTestWalletSeeds;
|
||||
case WalletType.base:
|
||||
return secrets.baseTestWalletSeeds;
|
||||
case WalletType.tron:
|
||||
return secrets.tronTestWalletSeeds;
|
||||
case WalletType.nano:
|
||||
@@ -387,6 +390,8 @@ class CommonTestFlows {
|
||||
return secrets.bitcoinCashTestWalletReceiveAddress;
|
||||
case WalletType.polygon:
|
||||
return secrets.polygonTestWalletReceiveAddress;
|
||||
case WalletType.base:
|
||||
return secrets.baseTestWalletReceiveAddress;
|
||||
case WalletType.solana:
|
||||
return secrets.solanaTestWalletReceiveAddress;
|
||||
case WalletType.tron:
|
||||
|
||||
@@ -302,6 +302,26 @@
|
||||
<string>dogecoin-wallet</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>base</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>base</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>base-wallet</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>base-wallet</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
part of 'base.dart';
|
||||
|
||||
class CWBase extends Base {
|
||||
@override
|
||||
List<String> getBaseWordList(String language) => EVMChainMnemonics.englishWordlist;
|
||||
|
||||
WalletService createBaseWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) =>
|
||||
BaseWalletService(walletInfoSource, isDirect, client: BaseClient());
|
||||
|
||||
@override
|
||||
WalletCredentials createBaseNewWalletCredentials({
|
||||
required String name,
|
||||
String? mnemonic,
|
||||
WalletInfo? walletInfo,
|
||||
String? password,
|
||||
String? passphrase,
|
||||
}) =>
|
||||
EVMChainNewWalletCredentials(
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
mnemonic: mnemonic,
|
||||
passphrase: passphrase,
|
||||
);
|
||||
|
||||
@override
|
||||
WalletCredentials createBaseRestoreWalletFromSeedCredentials({
|
||||
required String name,
|
||||
required String mnemonic,
|
||||
required String password,
|
||||
String? passphrase,
|
||||
}) =>
|
||||
EVMChainRestoreWalletFromSeedCredentials(
|
||||
name: name,
|
||||
password: password,
|
||||
mnemonic: mnemonic,
|
||||
passphrase: passphrase,
|
||||
);
|
||||
|
||||
@override
|
||||
WalletCredentials createBaseRestoreWalletFromPrivateKey({
|
||||
required String name,
|
||||
required String privateKey,
|
||||
required String password,
|
||||
}) =>
|
||||
EVMChainRestoreWalletFromPrivateKey(name: name, password: password, privateKey: privateKey);
|
||||
|
||||
@override
|
||||
WalletCredentials createBaseHardwareWalletCredentials({
|
||||
required String name,
|
||||
required HardwareAccountData hwAccountData,
|
||||
WalletInfo? walletInfo,
|
||||
}) =>
|
||||
EVMChainRestoreWalletFromHardware(
|
||||
name: name,
|
||||
hwAccountData: hwAccountData,
|
||||
walletInfo: walletInfo,
|
||||
);
|
||||
|
||||
@override
|
||||
String getAddress(WalletBase wallet) => (wallet as BaseWallet).walletAddresses.address;
|
||||
|
||||
@override
|
||||
String getPrivateKey(WalletBase wallet) {
|
||||
final privateKeyHolder = (wallet as BaseWallet).evmChainPrivateKey;
|
||||
if (privateKeyHolder is EthPrivateKey) return bytesToHex(privateKeyHolder.privateKey);
|
||||
return "";
|
||||
}
|
||||
|
||||
@override
|
||||
String getPublicKey(WalletBase wallet) {
|
||||
final privateKeyInUnitInt = (wallet as BaseWallet).evmChainPrivateKey;
|
||||
return privateKeyInUnitInt.address.hex;
|
||||
}
|
||||
|
||||
@override
|
||||
TransactionPriority getDefaultTransactionPriority() => EVMChainTransactionPriority.medium;
|
||||
|
||||
@override
|
||||
TransactionPriority getBaseTransactionPrioritySlow() => EVMChainTransactionPriority.slow;
|
||||
|
||||
@override
|
||||
List<TransactionPriority> getTransactionPriorities() => EVMChainTransactionPriority.all;
|
||||
|
||||
@override
|
||||
TransactionPriority deserializeBaseTransactionPriority(int raw) =>
|
||||
EVMChainTransactionPriority.deserialize(raw: raw);
|
||||
|
||||
Object createBaseTransactionCredentials(
|
||||
List<Output> outputs, {
|
||||
required TransactionPriority priority,
|
||||
required CryptoCurrency currency,
|
||||
int? feeRate,
|
||||
}) =>
|
||||
EVMChainTransactionCredentials(
|
||||
outputs
|
||||
.map(
|
||||
(out) => OutputInfo(
|
||||
fiatAmount: out.fiatAmount,
|
||||
cryptoAmount: out.cryptoAmount,
|
||||
address: out.address,
|
||||
note: out.note,
|
||||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
formattedCryptoAmount: out.formattedCryptoAmount,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
priority: priority as EVMChainTransactionPriority,
|
||||
currency: currency,
|
||||
feeRate: feeRate,
|
||||
);
|
||||
|
||||
Object createBaseTransactionCredentialsRaw(
|
||||
List<OutputInfo> outputs, {
|
||||
TransactionPriority? priority,
|
||||
required CryptoCurrency currency,
|
||||
required int feeRate,
|
||||
}) =>
|
||||
EVMChainTransactionCredentials(
|
||||
outputs,
|
||||
priority: priority as EVMChainTransactionPriority?,
|
||||
currency: currency,
|
||||
feeRate: feeRate,
|
||||
);
|
||||
|
||||
@override
|
||||
int formatterBaseParseAmount(String amount) => EVMChainFormatter.parseEVMChainAmount(amount);
|
||||
|
||||
@override
|
||||
double formatterBaseAmountToDouble({
|
||||
TransactionInfo? transaction,
|
||||
BigInt? amount,
|
||||
int exponent = 18,
|
||||
}) {
|
||||
assert(transaction != null || amount != null);
|
||||
|
||||
if (transaction != null) {
|
||||
transaction as EVMChainTransactionInfo;
|
||||
return transaction.ethAmount / BigInt.from(10).pow(transaction.exponent);
|
||||
} else {
|
||||
return (amount!) / BigInt.from(10).pow(exponent);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
List<Erc20Token> getERC20Currencies(WalletBase wallet) => (wallet as BaseWallet).erc20Currencies;
|
||||
|
||||
@override
|
||||
Future<void> addErc20Token(WalletBase wallet, CryptoCurrency token) =>
|
||||
(wallet as BaseWallet).addErc20Token(token as Erc20Token);
|
||||
|
||||
@override
|
||||
Future<void> deleteErc20Token(WalletBase wallet, CryptoCurrency token) =>
|
||||
(wallet as BaseWallet).deleteErc20Token(token as Erc20Token);
|
||||
|
||||
@override
|
||||
Future<void> removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token) =>
|
||||
(wallet as BaseWallet).removeTokenTransactionsInHistory(token as Erc20Token);
|
||||
|
||||
@override
|
||||
Future<Erc20Token?> getErc20Token(WalletBase wallet, String contractAddress) =>
|
||||
(wallet as BaseWallet).getErc20Token(contractAddress, 'base');
|
||||
|
||||
@override
|
||||
CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction) {
|
||||
transaction as EVMChainTransactionInfo;
|
||||
if (transaction.tokenSymbol == CryptoCurrency.baseEth.title ||
|
||||
transaction.tokenSymbol == "BASE") {
|
||||
return CryptoCurrency.baseEth;
|
||||
}
|
||||
|
||||
wallet as BaseWallet;
|
||||
|
||||
return wallet.erc20Currencies.firstWhere(
|
||||
(element) =>
|
||||
transaction.contractAddress?.toLowerCase() == element.contractAddress?.toLowerCase(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void updateBaseScanUsageState(WalletBase wallet, bool isEnabled) =>
|
||||
(wallet as BaseWallet).updateScanProviderUsageState(isEnabled);
|
||||
|
||||
@override
|
||||
Web3Client? getWeb3Client(WalletBase wallet) => (wallet as BaseWallet).getWeb3Client();
|
||||
|
||||
@override
|
||||
String getTokenAddress(CryptoCurrency asset) => (asset as Erc20Token).contractAddress;
|
||||
|
||||
@override
|
||||
Future<PendingTransaction> createTokenApproval(
|
||||
WalletBase wallet,
|
||||
BigInt amount,
|
||||
String spender,
|
||||
CryptoCurrency token,
|
||||
TransactionPriority priority,
|
||||
) =>
|
||||
(wallet as EVMChainWallet).createApprovalTransaction(
|
||||
amount,
|
||||
spender,
|
||||
token,
|
||||
priority as EVMChainTransactionPriority,
|
||||
"BASE",
|
||||
);
|
||||
|
||||
@override
|
||||
void setHardwareWalletService(WalletBase wallet, HardwareWalletService service) {
|
||||
if (service is EVMChainLedgerService) {
|
||||
((wallet as EVMChainWallet).evmChainPrivateKey as EvmLedgerCredentials).setLedgerConnection(
|
||||
service.ledgerConnection, wallet.walletInfo.derivationInfo?.derivationPath);
|
||||
} else if (service is EVMChainBitboxService) {
|
||||
((wallet as EVMChainWallet).evmChainPrivateKey as EvmBitboxCredentials)
|
||||
.setBitbox(service.manager, wallet.walletInfo.derivationInfo?.derivationPath);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
HardwareWalletService getLedgerHardwareWalletService(ledger.LedgerConnection connection) =>
|
||||
EVMChainLedgerService(connection);
|
||||
|
||||
@override
|
||||
HardwareWalletService getBitboxHardwareWalletService(bitbox.BitboxManager manager) =>
|
||||
EVMChainBitboxService(manager, chainId: 8453);
|
||||
|
||||
@override
|
||||
List<String> getDefaultTokenContractAddresses() =>
|
||||
DefaultBaseErc20Tokens().initialBaseErc20Tokens.map((e) => e.contractAddress).toList();
|
||||
|
||||
@override
|
||||
bool isTokenAlreadyAdded(WalletBase wallet, String contractAddress) {
|
||||
final baseWallet = wallet as BaseWallet;
|
||||
return baseWallet.erc20Currencies.any(
|
||||
(element) => element.contractAddress.toLowerCase() == contractAddress.toLowerCase(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -135,6 +135,7 @@ class DFXBuyProvider extends BuyProvider {
|
||||
switch (wallet.type) {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
return wallet.signMessage(message);
|
||||
|
||||
@@ -218,6 +218,7 @@ class KryptonimBuyProvider extends BuyProvider {
|
||||
String _normalizeBlockChain(CryptoCurrency cur) {
|
||||
String? blockchain = switch (cur.tag) {
|
||||
'ETH' => 'Ethereum',
|
||||
'BASE' => 'Base',
|
||||
'POL' => 'Polygon',
|
||||
'AVAXC' => 'Avalanche',
|
||||
'SOL' => 'Solana',
|
||||
@@ -229,6 +230,7 @@ class KryptonimBuyProvider extends BuyProvider {
|
||||
CryptoCurrency.btc => 'Bitcoin',
|
||||
CryptoCurrency.ltc => 'Litecoin',
|
||||
CryptoCurrency.eth => 'Ethereum',
|
||||
CryptoCurrency.baseEth => 'Base',
|
||||
CryptoCurrency.maticpoly => 'Matic',
|
||||
_ => null,
|
||||
};
|
||||
|
||||
@@ -17,6 +17,7 @@ import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/themes/core/material_base_theme.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
|
||||
@@ -105,6 +105,7 @@ class RobinhoodBuyProvider extends BuyProvider {
|
||||
switch (wallet.type) {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
case WalletType.dogecoin:
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:cake_wallet/order/order_source_description.dart';
|
||||
import 'package:cake_wallet/buy/pairs_utils.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||
import 'package:cake_wallet/buy/buy_amount.dart';
|
||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||
|
||||
@@ -86,6 +86,21 @@ class EthereumURI extends PaymentURI {
|
||||
}
|
||||
}
|
||||
|
||||
class BaseURI extends PaymentURI {
|
||||
BaseURI({required super.amount, required super.address});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
var base = 'base:$address';
|
||||
|
||||
if (amount.isNotEmpty) {
|
||||
base += '?amount=${amount.replaceAll(',', '.')}';
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
}
|
||||
|
||||
class BitcoinCashURI extends PaymentURI {
|
||||
BitcoinCashURI({required super.amount, required super.address});
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/core/validator.dart';
|
||||
import 'package:cake_wallet/entities/mnemonic_item.dart';
|
||||
@@ -52,6 +53,8 @@ class SeedValidator extends Validator<MnemonicItem> {
|
||||
return zano!.getWordList(language);
|
||||
case WalletType.decred:
|
||||
return decred!.getDecredWordList();
|
||||
case WalletType.base:
|
||||
return base!.getBaseWordList(language);
|
||||
case WalletType.none:
|
||||
case WalletType.haven:
|
||||
return [];
|
||||
|
||||
@@ -80,6 +80,7 @@ class WalletCreationService {
|
||||
case WalletType.bitcoinCash:
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
case WalletType.dogecoin:
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:cake_wallet/anonpay/anonpay_api.dart';
|
||||
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
|
||||
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
|
||||
import 'package:cake_wallet/anypay/anypay_api.dart';
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
||||
import 'package:cake_wallet/buy/dfx/dfx_buy_provider.dart';
|
||||
@@ -1241,6 +1242,8 @@ Future<void> setup({
|
||||
return zano!.createZanoWalletService(_walletInfoSource);
|
||||
case WalletType.decred:
|
||||
return decred!.createDecredWalletService(_walletInfoSource, _unspentCoinsInfoSource);
|
||||
case WalletType.base:
|
||||
return base!.createBaseWalletService(_walletInfoSource, SettingsStoreBase.walletPasswordDirectInput);
|
||||
case WalletType.haven:
|
||||
return HavenWalletService(_walletInfoSource);
|
||||
case WalletType.none:
|
||||
|
||||
@@ -47,6 +47,7 @@ const zanoDefaultNodeUri = '37.27.100.59:10500';
|
||||
const moneroWorldNodeUri = '.moneroworld.com';
|
||||
const decredDefaultUri = "default-spv-nodes";
|
||||
const dogecoinDefaultNodeUri = 'dogecoin.stackwallet.com:50022';
|
||||
const baseDefaultNodeUri = 'base.nownodes.io';
|
||||
|
||||
Future<void> defaultSettingsMigration(
|
||||
{required int version,
|
||||
@@ -534,6 +535,15 @@ Future<void> defaultSettingsMigration(
|
||||
currentNodePreferenceKey: PreferencesKey.currentDogecoinNodeIdKey,
|
||||
);
|
||||
break;
|
||||
case 52:
|
||||
await addWalletNodeList(nodes: nodes, type: WalletType.base);
|
||||
await _changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.base,
|
||||
currentNodePreferenceKey: PreferencesKey.currentBaseNodeIdKey,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -642,6 +652,8 @@ String _getDefaultNodeUri(WalletType type) {
|
||||
return decredDefaultUri;
|
||||
case WalletType.dogecoin:
|
||||
return dogecoinDefaultNodeUri;
|
||||
case WalletType.base:
|
||||
return baseDefaultNodeUri;
|
||||
case WalletType.banano:
|
||||
case WalletType.none:
|
||||
return '';
|
||||
@@ -1041,6 +1053,7 @@ Future<void> checkCurrentNodes(
|
||||
final currentHavenNodeId = sharedPreferences.getInt(PreferencesKey.currentHavenNodeIdKey);
|
||||
final currentEthereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
|
||||
final currentPolygonNodeId = sharedPreferences.getInt(PreferencesKey.currentPolygonNodeIdKey);
|
||||
final currentBaseNodeId = sharedPreferences.getInt(PreferencesKey.currentBaseNodeIdKey);
|
||||
final currentNanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
|
||||
final currentNanoPowNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoPowNodeIdKey);
|
||||
final currentDecredNodeId = sharedPreferences.getInt(PreferencesKey.currentDecredNodeIdKey);
|
||||
@@ -1064,6 +1077,8 @@ Future<void> checkCurrentNodes(
|
||||
nodeSource.values.firstWhereOrNull((node) => node.key == currentEthereumNodeId);
|
||||
final currentPolygonNodeServer =
|
||||
nodeSource.values.firstWhereOrNull((node) => node.key == currentPolygonNodeId);
|
||||
final currentBaseNodeServer =
|
||||
nodeSource.values.firstWhereOrNull((node) => node.key == currentBaseNodeId);
|
||||
final currentNanoNodeServer =
|
||||
nodeSource.values.firstWhereOrNull((node) => node.key == currentNanoNodeId);
|
||||
final currentDecredNodeServer =
|
||||
@@ -1155,6 +1170,12 @@ Future<void> checkCurrentNodes(
|
||||
await sharedPreferences.setInt(PreferencesKey.currentPolygonNodeIdKey, node.key as int);
|
||||
}
|
||||
|
||||
if (currentBaseNodeServer == null) {
|
||||
final node = Node(uri: baseDefaultNodeUri, type: WalletType.base);
|
||||
await nodeSource.add(node);
|
||||
await sharedPreferences.setInt(PreferencesKey.currentBaseNodeIdKey, node.key as int);
|
||||
}
|
||||
|
||||
if (currentSolanaNodeServer == null) {
|
||||
final node = Node(uri: solanaDefaultNodeUri, type: WalletType.solana);
|
||||
await nodeSource.add(node);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cake_wallet/polygon/polygon.dart';
|
||||
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||
@@ -21,6 +22,10 @@ class EnsRecord {
|
||||
_client = polygon!.getWeb3Client(wallet);
|
||||
}
|
||||
|
||||
if (wallet != null && wallet.type == WalletType.base) {
|
||||
_client = base!.getWeb3Client(wallet);
|
||||
}
|
||||
|
||||
if (_client == null) {
|
||||
late final client = ProxyWrapper().getHttpIOClient();
|
||||
|
||||
@@ -42,6 +47,7 @@ class EnsRecord {
|
||||
return await ens.withName(name).getCoinAddress(CoinType.XHV);
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
default:
|
||||
return (await ens.withName(name).getAddress()).hex;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,50 @@ class EVMTransactionErrorFeesHandler {
|
||||
String errorMessage,
|
||||
double assetPriceUsd,
|
||||
) {
|
||||
// Pattern: "insufficient funds for gas * price + value: have 4728796358953246 want 4728796842182575"
|
||||
RegExp insufficientFundsRegExp = RegExp(r'have (\d+) want (\d+)');
|
||||
Match? insufficientFundsMatch = insufficientFundsRegExp.firstMatch(errorMessage);
|
||||
|
||||
if (insufficientFundsMatch != null) {
|
||||
try {
|
||||
// Extract the numerical strings from the new format
|
||||
String balanceStr = insufficientFundsMatch.group(1)!;
|
||||
String requiredStr = insufficientFundsMatch.group(2)!;
|
||||
|
||||
// Parse the numerical strings to BigInt
|
||||
BigInt balanceWei = BigInt.parse(balanceStr);
|
||||
BigInt requiredWei = BigInt.parse(requiredStr);
|
||||
|
||||
// Calculate overshot (how much more is needed)
|
||||
BigInt overshotWei = requiredWei - balanceWei;
|
||||
|
||||
// The transaction cost is the required amount
|
||||
BigInt txCostWei = requiredWei;
|
||||
|
||||
// Convert wei to ETH (1 ETH = 1e18 wei)
|
||||
double balanceEth = balanceWei.toDouble() / 1e18;
|
||||
double txCostEth = txCostWei.toDouble() / 1e18;
|
||||
double overshotEth = overshotWei.toDouble() / 1e18;
|
||||
|
||||
// Calculate the USD values
|
||||
double balanceUsd = balanceEth * assetPriceUsd;
|
||||
double txCostUsd = txCostEth * assetPriceUsd;
|
||||
double overshotUsd = overshotEth * assetPriceUsd;
|
||||
|
||||
return EVMTransactionErrorFeesHandler(
|
||||
balanceWei: balanceWei.toString(),
|
||||
balanceEth: balanceEth.toString().safeSubString(0, 12),
|
||||
balanceUsd: balanceUsd.toString().safeSubString(0, 4),
|
||||
txCostWei: txCostWei.toString(),
|
||||
txCostEth: txCostEth.toString().safeSubString(0, 12),
|
||||
txCostUsd: txCostUsd.toString().safeSubString(0, 4),
|
||||
overshotWei: overshotWei.toString(),
|
||||
overshotEth: overshotEth.toString().safeSubString(0, 12),
|
||||
overshotUsd: overshotUsd.toString().safeSubString(0, 4),
|
||||
);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// Define Regular Expressions to extract the numerical values
|
||||
RegExp balanceRegExp = RegExp(r'balance (\d+)');
|
||||
RegExp txCostRegExp = RegExp(r'tx cost (\d+)');
|
||||
@@ -44,40 +88,44 @@ class EVMTransactionErrorFeesHandler {
|
||||
|
||||
// Check if all required values are found
|
||||
if (balanceMatch != null && txCostMatch != null && overshotMatch != null) {
|
||||
// Extract the numerical strings
|
||||
String balanceStr = balanceMatch.group(1)!;
|
||||
String txCostStr = txCostMatch.group(1)!;
|
||||
String overshotStr = overshotMatch.group(1)!;
|
||||
try {
|
||||
// Extract the numerical strings
|
||||
String balanceStr = balanceMatch.group(1)!;
|
||||
String txCostStr = txCostMatch.group(1)!;
|
||||
String overshotStr = overshotMatch.group(1)!;
|
||||
|
||||
// Parse the numerical strings to BigInt
|
||||
BigInt balanceWei = BigInt.parse(balanceStr);
|
||||
BigInt txCostWei = BigInt.parse(txCostStr);
|
||||
BigInt overshotWei = BigInt.parse(overshotStr);
|
||||
// Parse the numerical strings to BigInt
|
||||
BigInt balanceWei = BigInt.parse(balanceStr);
|
||||
BigInt txCostWei = BigInt.parse(txCostStr);
|
||||
BigInt overshotWei = BigInt.parse(overshotStr);
|
||||
|
||||
// Convert wei to ETH (1 ETH = 1e18 wei)
|
||||
double balanceEth = balanceWei.toDouble() / 1e18;
|
||||
double txCostEth = txCostWei.toDouble() / 1e18;
|
||||
double overshotEth = overshotWei.toDouble() / 1e18;
|
||||
// Convert wei to ETH (1 ETH = 1e18 wei)
|
||||
double balanceEth = balanceWei.toDouble() / 1e18;
|
||||
double txCostEth = txCostWei.toDouble() / 1e18;
|
||||
double overshotEth = overshotWei.toDouble() / 1e18;
|
||||
|
||||
// Calculate the USD values
|
||||
double balanceUsd = balanceEth * assetPriceUsd;
|
||||
double txCostUsd = txCostEth * assetPriceUsd;
|
||||
double overshotUsd = overshotEth * assetPriceUsd;
|
||||
// Calculate the USD values
|
||||
double balanceUsd = balanceEth * assetPriceUsd;
|
||||
double txCostUsd = txCostEth * assetPriceUsd;
|
||||
double overshotUsd = overshotEth * assetPriceUsd;
|
||||
|
||||
return EVMTransactionErrorFeesHandler(
|
||||
balanceWei: balanceWei.toString(),
|
||||
balanceEth: balanceEth.toString().safeSubString(0, 12),
|
||||
balanceUsd: balanceUsd.toString().safeSubString(0, 4),
|
||||
txCostWei: txCostWei.toString(),
|
||||
txCostEth: txCostEth.toString().safeSubString(0, 12),
|
||||
txCostUsd: txCostUsd.toString().safeSubString(0, 4),
|
||||
overshotWei: overshotWei.toString(),
|
||||
overshotEth: overshotEth.toString().safeSubString(0, 12),
|
||||
overshotUsd: overshotUsd.toString().safeSubString(0, 4),
|
||||
);
|
||||
} else {
|
||||
// If any value is missing, return an error message
|
||||
return EVMTransactionErrorFeesHandler(error: 'Could not parse the error message.');
|
||||
return EVMTransactionErrorFeesHandler(
|
||||
balanceWei: balanceWei.toString(),
|
||||
balanceEth: balanceEth.toString().safeSubString(0, 12),
|
||||
balanceUsd: balanceUsd.toString().safeSubString(0, 4),
|
||||
txCostWei: txCostWei.toString(),
|
||||
txCostEth: txCostEth.toString().safeSubString(0, 12),
|
||||
txCostUsd: txCostUsd.toString().safeSubString(0, 4),
|
||||
overshotWei: overshotWei.toString(),
|
||||
overshotEth: overshotEth.toString().safeSubString(0, 12),
|
||||
overshotUsd: overshotUsd.toString().safeSubString(0, 4),
|
||||
);
|
||||
} catch (e) {
|
||||
// If parsing fails, continue to error case
|
||||
}
|
||||
}
|
||||
|
||||
// If both parsing attempts fail, return an error message
|
||||
return EVMTransactionErrorFeesHandler(error: 'Could not parse the error message.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,9 @@ Future<List<Node>> loadDefaultNodes(WalletType type) async {
|
||||
case WalletType.dogecoin:
|
||||
path = 'assets/dogecoin_electrum_server_list.yml';
|
||||
break;
|
||||
case WalletType.base:
|
||||
path = 'assets/base_node_list.yml';
|
||||
break;
|
||||
case WalletType.banano:
|
||||
case WalletType.none:
|
||||
path = '';
|
||||
@@ -100,6 +103,7 @@ Future<void> resetToDefault(Box<Node> nodeSource) async {
|
||||
final decredNodes = await loadDefaultNodes(WalletType.decred);
|
||||
final zanoNodes = await loadDefaultNodes(WalletType.zano);
|
||||
final dogecoinElectrumServerList = await loadDefaultNodes(WalletType.dogecoin);
|
||||
final baseNodes = await loadDefaultNodes(WalletType.base);
|
||||
|
||||
final nodes = moneroNodes +
|
||||
bitcoinElectrumServerList +
|
||||
@@ -113,7 +117,8 @@ Future<void> resetToDefault(Box<Node> nodeSource) async {
|
||||
tronNodes +
|
||||
zanoNodes +
|
||||
decredNodes +
|
||||
dogecoinElectrumServerList;
|
||||
dogecoinElectrumServerList +
|
||||
baseNodes;
|
||||
|
||||
await nodeSource.clear();
|
||||
await nodeSource.addAll(nodes);
|
||||
|
||||
@@ -114,6 +114,7 @@ class AddressResolver {
|
||||
"podcast",
|
||||
"pog",
|
||||
"polygon",
|
||||
"base",
|
||||
"press",
|
||||
"privacy",
|
||||
"pro",
|
||||
|
||||
@@ -8,6 +8,7 @@ class PreferencesKey {
|
||||
static const currentZanoNodeIdKey = 'current_node_id_zano';
|
||||
static const currentEthereumNodeIdKey = 'current_node_id_eth';
|
||||
static const currentPolygonNodeIdKey = 'current_node_id_matic';
|
||||
static const currentBaseNodeIdKey = 'current_node_id_base';
|
||||
static const currentNanoNodeIdKey = 'current_node_id_nano';
|
||||
static const currentNanoPowNodeIdKey = 'current_node_id_nano_pow';
|
||||
static const currentDecredNodeIdKey = 'current_node_id_decred';
|
||||
@@ -52,6 +53,7 @@ class PreferencesKey {
|
||||
static const litecoinTransactionPriority = 'current_fee_priority_litecoin';
|
||||
static const ethereumTransactionPriority = 'current_fee_priority_ethereum';
|
||||
static const polygonTransactionPriority = 'current_fee_priority_polygon';
|
||||
static const baseTransactionPriority = 'current_fee_priority_base';
|
||||
static const bitcoinCashTransactionPriority = 'current_fee_priority_bitcoin_cash';
|
||||
static const zanoTransactionPriority = 'current_fee_priority_zano';
|
||||
static const wowneroTransactionPriority = 'current_fee_priority_wownero';
|
||||
@@ -77,6 +79,7 @@ class PreferencesKey {
|
||||
static const pinNativeTokenAtTop = 'pin_native_token_at_top';
|
||||
static const useEtherscan = 'use_etherscan';
|
||||
static const usePolygonScan = 'use_polygonscan';
|
||||
static const useBaseScan = 'use_base_scan';
|
||||
static const useTronGrid = 'use_trongrid';
|
||||
static const useMempoolFeeAPI = 'use_mempool_fee_api';
|
||||
static const defaultNanoRep = 'default_nano_representative';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
||||
import 'package:cake_wallet/dogecoin/dogecoin.dart';
|
||||
@@ -28,6 +29,8 @@ List<TransactionPriority> priorityForWalletType(WalletType type) {
|
||||
return dogecoin!.getTransactionPriorities();
|
||||
case WalletType.polygon:
|
||||
return polygon!.getTransactionPriorities();
|
||||
case WalletType.base:
|
||||
return base!.getTransactionPriorities();
|
||||
// no such thing for nano/banano/solana/tron:
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/core/fiat_conversion_service.dart';
|
||||
import 'package:cake_wallet/entities/fiat_api_mode.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
@@ -44,6 +45,11 @@ Future<void> startFiatRateUpdate(
|
||||
polygon!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
|
||||
}
|
||||
|
||||
if (appStore.wallet!.type == WalletType.base) {
|
||||
currencies =
|
||||
base!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
|
||||
}
|
||||
|
||||
if (appStore.wallet!.type == WalletType.solana) {
|
||||
currencies =
|
||||
solana!.getSPLTokenCurrencies(appStore.wallet!).where((element) => element.enabled);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
|
||||
@@ -130,6 +131,10 @@ void startCurrentWalletChangeReaction(
|
||||
currencies =
|
||||
polygon!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
|
||||
}
|
||||
if (wallet.type == WalletType.base) {
|
||||
currencies =
|
||||
base!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
|
||||
}
|
||||
if (wallet.type == WalletType.solana) {
|
||||
currencies =
|
||||
solana!.getSPLTokenCurrencies(appStore.wallet!).where((element) => element.enabled);
|
||||
|
||||
@@ -8,6 +8,7 @@ bool isEVMCompatibleChain(WalletType walletType) {
|
||||
switch (walletType) {
|
||||
case WalletType.polygon:
|
||||
case WalletType.ethereum:
|
||||
case WalletType.base:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -18,6 +19,7 @@ bool isNFTACtivatedChain(WalletType walletType) {
|
||||
switch (walletType) {
|
||||
case WalletType.polygon:
|
||||
case WalletType.ethereum:
|
||||
case WalletType.base:
|
||||
case WalletType.solana:
|
||||
return true;
|
||||
default:
|
||||
@@ -29,6 +31,7 @@ bool isWalletConnectCompatibleChain(WalletType walletType) {
|
||||
switch (walletType) {
|
||||
case WalletType.polygon:
|
||||
case WalletType.ethereum:
|
||||
case WalletType.base:
|
||||
case WalletType.solana:
|
||||
return true;
|
||||
default:
|
||||
@@ -42,6 +45,8 @@ String getChainNameSpaceAndIdBasedOnWalletType(WalletType walletType) {
|
||||
return EVMChainId.ethereum.chain();
|
||||
case WalletType.polygon:
|
||||
return EVMChainId.polygon.chain();
|
||||
case WalletType.base:
|
||||
return EVMChainId.base.chain();
|
||||
case WalletType.solana:
|
||||
return SolanaChainId.mainnet.chain();
|
||||
default:
|
||||
@@ -52,8 +57,8 @@ String getChainNameSpaceAndIdBasedOnWalletType(WalletType walletType) {
|
||||
List<String> getChainSupportedMethodsOnWalletType(WalletType walletType) {
|
||||
switch (walletType) {
|
||||
case WalletType.ethereum:
|
||||
return EVMSupportedMethods.values.map((e) => e.name).toList();
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
return EVMSupportedMethods.values.map((e) => e.name).toList();
|
||||
case WalletType.solana:
|
||||
return SolanaSupportedMethods.values.map((e) => e.name).toList();
|
||||
@@ -66,7 +71,8 @@ int getChainIdBasedOnWalletType(WalletType walletType) {
|
||||
switch (walletType) {
|
||||
case WalletType.polygon:
|
||||
return 137;
|
||||
|
||||
case WalletType.base:
|
||||
return 8453;
|
||||
// For now, we return eth chain Id as the default, we'll modify as we add more wallets
|
||||
case WalletType.ethereum:
|
||||
default:
|
||||
@@ -80,6 +86,8 @@ String getChainNameBasedOnWalletType(WalletType walletType) {
|
||||
return 'eth';
|
||||
case WalletType.polygon:
|
||||
return 'polygon';
|
||||
case WalletType.base:
|
||||
return 'base';
|
||||
case WalletType.solana:
|
||||
return 'mainnet';
|
||||
default:
|
||||
@@ -93,6 +101,8 @@ String getTokenNameBasedOnWalletType(WalletType walletType) {
|
||||
return 'ETH';
|
||||
case WalletType.polygon:
|
||||
return 'MATIC';
|
||||
case WalletType.base:
|
||||
return 'BASE';
|
||||
case WalletType.solana:
|
||||
return 'SOL';
|
||||
default:
|
||||
|
||||
@@ -4,6 +4,7 @@ bool isBIP39Wallet(WalletType walletType) {
|
||||
switch (walletType) {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
case WalletType.bitcoin:
|
||||
@@ -41,6 +42,7 @@ bool hasTokens(WalletType walletType) {
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
case WalletType.zano:
|
||||
case WalletType.base:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_hardware_restore_view_model.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
@@ -148,7 +149,7 @@ class _SelectHardwareWalletAccountFormState extends State<SelectHardwareWalletAc
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: SelectButton(
|
||||
image: Image.asset(
|
||||
walletTypeToCryptoCurrency(_walletHardwareRestoreVM.type).iconPath ??
|
||||
walletTypeToCryptoCurrency(_walletHardwareRestoreVM.type).normalizedIconPath ??
|
||||
'',
|
||||
height: 24,
|
||||
width: 24,
|
||||
|
||||
@@ -38,6 +38,7 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
|
||||
final ethereumIcon = Image.asset('assets/images/crypto/ethereum.png', height: 24, width: 24);
|
||||
final polygonIcon = Image.asset('assets/images/crypto/polygon.png', height: 24, width: 24);
|
||||
final bitcoinCashIcon = Image.asset('assets/images/crypto/bitcoin-cash.png', height: 24, width: 24);
|
||||
final baseIcon = Image.asset('assets/images/base_icon.png', height: 24, width: 24);
|
||||
final nanoIcon = Image.asset('assets/images/crypto/nano.png', height: 24, width: 24);
|
||||
final bananoIcon = Image.asset('assets/images/crypto/nano.png', height: 24, width: 24);
|
||||
final solanaIcon = Image.asset('assets/images/crypto/solana.png', height: 24, width: 24);
|
||||
@@ -184,6 +185,8 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
|
||||
return decredIcon;
|
||||
case WalletType.dogecoin:
|
||||
return dogeIcon;
|
||||
case WalletType.base:
|
||||
return baseIcon;
|
||||
case WalletType.none:
|
||||
return nonWalletTypeIcon;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ class MenuWidgetState extends State<MenuWidget> {
|
||||
this.bananoIcon = Image.asset('assets/images/crypto/nano.png'),
|
||||
this.bitcoinCashIcon = Image.asset('assets/images/crypto/bitcoin-cash.png'),
|
||||
this.polygonIcon = Image.asset('assets/images/crypto/polygon.png'),
|
||||
this.baseIcon = Image.asset('assets/images/base_icon.png'),
|
||||
this.solanaIcon = Image.asset('assets/images/crypto/solana.png'),
|
||||
this.tronIcon = Image.asset('assets/images/crypto/tron.png'),
|
||||
this.wowneroIcon = Image.asset('assets/images/crypto/wownero.png'),
|
||||
@@ -60,6 +61,7 @@ class MenuWidgetState extends State<MenuWidget> {
|
||||
Image nanoIcon;
|
||||
Image bananoIcon;
|
||||
Image polygonIcon;
|
||||
Image baseIcon;
|
||||
Image solanaIcon;
|
||||
Image tronIcon;
|
||||
Image wowneroIcon;
|
||||
@@ -247,6 +249,8 @@ class MenuWidgetState extends State<MenuWidget> {
|
||||
return polygonIcon;
|
||||
case WalletType.solana:
|
||||
return solanaIcon;
|
||||
case WalletType.base:
|
||||
return baseIcon;
|
||||
case WalletType.tron:
|
||||
return tronIcon;
|
||||
case WalletType.wownero:
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/address_text_field.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/address_text_field.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/new_wallet_type_view_model.dart';
|
||||
import 'package:cake_wallet/wallet_types.g.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/hardware/device_connection_type.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
@@ -142,9 +143,9 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
||||
child: SelectButton(
|
||||
key: ValueKey('new_wallet_type_${type.name}_button_key'),
|
||||
image: Image.asset(
|
||||
walletTypeToCryptoCurrency(type).iconPath ?? '',
|
||||
height: 28,
|
||||
width: 28,
|
||||
walletTypeToCryptoCurrency(type).normalizedIconPath ?? '',
|
||||
height: 24,
|
||||
width: 24,
|
||||
),
|
||||
text: walletTypeToDisplayName(type),
|
||||
showTrailingIcon: false,
|
||||
|
||||
@@ -7,7 +7,7 @@ import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/themes/core/material_base_theme.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_groups_display_view_model.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
||||
@@ -95,7 +95,7 @@ class WalletGroupsDisplayBody extends StatelessWidget {
|
||||
isSelected:
|
||||
walletGroupsDisplayViewModel.selectedSingleWallet == wallet,
|
||||
leadingWidget: Image.asset(
|
||||
walletTypeToCryptoCurrency(wallet.type).iconPath!,
|
||||
walletTypeToCryptoCurrency(wallet.type).normalizedIconPath!,
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
|
||||
|
||||
@@ -99,7 +99,6 @@ class GroupedWalletExpansionTile extends StatelessWidget {
|
||||
final currentColor = item.isCurrent
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Theme.of(context).colorScheme.surface;
|
||||
final walletTypeToCrypto = walletTypeToCryptoCurrency(item.type);
|
||||
return ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
key: ValueKey(item.name),
|
||||
@@ -124,7 +123,7 @@ class GroupedWalletExpansionTile extends StatelessWidget {
|
||||
: SizedBox(width: 7),
|
||||
SizedBox(width: 24),
|
||||
Image.asset(
|
||||
walletTypeToCrypto.iconPath!,
|
||||
walletTypeToCryptoCurrency(item.type).normalizedIconPath!,
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
|
||||
@@ -118,6 +118,14 @@ class PrivacyPage extends BasePage {
|
||||
_privacySettingsViewModel.setUsePolygonScan(value);
|
||||
},
|
||||
),
|
||||
if (_privacySettingsViewModel.canUseBaseScan)
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.basescan_history,
|
||||
value: _privacySettingsViewModel.useBaseScan,
|
||||
onValueChange: (BuildContext _, bool value) {
|
||||
_privacySettingsViewModel.setUseBaseScan(value);
|
||||
},
|
||||
),
|
||||
if (_privacySettingsViewModel.canUseTronGrid)
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.trongrid_history,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
enum EVMChainId {
|
||||
ethereum,
|
||||
polygon,
|
||||
base,
|
||||
goerli,
|
||||
mumbai,
|
||||
arbitrum,
|
||||
@@ -17,6 +18,9 @@ extension EVMChainIdX on EVMChainId {
|
||||
case EVMChainId.polygon:
|
||||
name = '137';
|
||||
break;
|
||||
case EVMChainId.base:
|
||||
name = '8453';
|
||||
break;
|
||||
case EVMChainId.goerli:
|
||||
name = '5';
|
||||
break;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cake_wallet/polygon/polygon.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
@@ -23,6 +24,8 @@ class KeyServiceImpl implements WalletConnectKeyService {
|
||||
return ethereum!.getPrivateKey(wallet);
|
||||
case WalletType.polygon:
|
||||
return polygon!.getPrivateKey(wallet);
|
||||
case WalletType.base:
|
||||
return base!.getPrivateKey(wallet);
|
||||
case WalletType.solana:
|
||||
return solana!.getPrivateKey(wallet);
|
||||
default:
|
||||
@@ -36,6 +39,8 @@ class KeyServiceImpl implements WalletConnectKeyService {
|
||||
return ethereum!.getPublicKey(wallet);
|
||||
case WalletType.polygon:
|
||||
return polygon!.getPublicKey(wallet);
|
||||
case WalletType.base:
|
||||
return base!.getPublicKey(wallet);
|
||||
case WalletType.solana:
|
||||
return solana!.getPublicKey(wallet);
|
||||
default:
|
||||
@@ -51,6 +56,7 @@ class KeyServiceImpl implements WalletConnectKeyService {
|
||||
'eip155:1',
|
||||
'eip155:5',
|
||||
'eip155:137',
|
||||
'eip155:8453',
|
||||
'eip155:42161',
|
||||
'eip155:80001',
|
||||
],
|
||||
|
||||
@@ -25,6 +25,7 @@ import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
|
||||
import 'package:cake_wallet/wallet_type_utils.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/utils/payment_request.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:cake_wallet/src/widgets/bottom_sheet/swap_details_bottom_sheet.d
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/utils/address_formatter.dart';
|
||||
import 'package:cake_wallet/utils/debounce.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/crypto_amount_format.dart';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -136,7 +137,7 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final detectedCurrencyName = walletTypeToCryptoCurrency(widget.paymentFlowResult.walletType!);
|
||||
final detectedCurrency = walletTypeToCryptoCurrency(widget.paymentFlowResult.walletType!);
|
||||
|
||||
return Form(
|
||||
key: _formKey,
|
||||
@@ -159,7 +160,7 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
|
||||
const SizedBox(width: 12),
|
||||
CakeImageWidget(
|
||||
imageUrl:
|
||||
walletTypeToCryptoCurrency(widget.paymentFlowResult.walletType!).iconPath!,
|
||||
detectedCurrency.iconPath!,
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
@@ -168,7 +169,7 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
|
||||
const SizedBox(height: 16),
|
||||
SwapConfirmationTextfield(
|
||||
key: ValueKey('swap_confirmation_bottomsheet_amount_textfield_key'),
|
||||
hintText: 'Amount (${detectedCurrencyName})',
|
||||
hintText: 'Amount (${detectedCurrency})',
|
||||
focusNode: _amountFocus,
|
||||
controller: _amountController,
|
||||
keyboardType: TextInputType.numberWithOptions(decimal: true, signed: false),
|
||||
@@ -226,7 +227,7 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
|
||||
min != null
|
||||
? Text(
|
||||
key: ValueKey('min_limit_text_key'),
|
||||
S.of(context).min_value(min, detectedCurrencyName.toString()),
|
||||
S.of(context).min_value(min, detectedCurrency.toString()),
|
||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||
fontSize: 10,
|
||||
height: 1.2,
|
||||
@@ -238,7 +239,7 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
|
||||
max != null
|
||||
? Text(
|
||||
key: ValueKey('max_limit_text_key'),
|
||||
S.of(context).max_value(max, detectedCurrencyName.toString()),
|
||||
S.of(context).max_value(max, detectedCurrency.toString()),
|
||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||
fontSize: 10,
|
||||
height: 1.2,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
@@ -94,7 +95,7 @@ class _WalletSwitcherContent extends StatelessWidget {
|
||||
child: Row(
|
||||
children: [
|
||||
Image.asset(
|
||||
walletTypeToCryptoCurrency(wallet.type).iconPath!,
|
||||
walletTypeToCryptoCurrency(wallet.type).normalizedIconPath!,
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/core/utilities.dart';
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
@@ -108,6 +109,7 @@ abstract class SettingsStoreBase with Store {
|
||||
required this.sortBalanceBy,
|
||||
required this.pinNativeTokenAtTop,
|
||||
required this.useEtherscan,
|
||||
required this.useBaseScan,
|
||||
required this.usePolygonScan,
|
||||
required this.useTronGrid,
|
||||
required this.useMempoolFeeAPI,
|
||||
@@ -139,6 +141,7 @@ abstract class SettingsStoreBase with Store {
|
||||
TransactionPriority? initialLitecoinTransactionPriority,
|
||||
TransactionPriority? initialEthereumTransactionPriority,
|
||||
TransactionPriority? initialPolygonTransactionPriority,
|
||||
TransactionPriority? initialBaseTransactionPriority,
|
||||
TransactionPriority? initialBitcoinCashTransactionPriority,
|
||||
TransactionPriority? initialZanoTransactionPriority,
|
||||
TransactionPriority? initialDecredTransactionPriority,
|
||||
@@ -224,6 +227,10 @@ abstract class SettingsStoreBase with Store {
|
||||
priority[WalletType.polygon] = initialPolygonTransactionPriority;
|
||||
}
|
||||
|
||||
if (initialBaseTransactionPriority != null) {
|
||||
priority[WalletType.base] = initialBaseTransactionPriority;
|
||||
}
|
||||
|
||||
if (initialBitcoinCashTransactionPriority != null) {
|
||||
priority[WalletType.bitcoinCash] = initialBitcoinCashTransactionPriority;
|
||||
}
|
||||
@@ -291,6 +298,9 @@ abstract class SettingsStoreBase with Store {
|
||||
case WalletType.polygon:
|
||||
key = PreferencesKey.polygonTransactionPriority;
|
||||
break;
|
||||
case WalletType.base:
|
||||
key = PreferencesKey.baseTransactionPriority;
|
||||
break;
|
||||
case WalletType.zano:
|
||||
key = PreferencesKey.zanoTransactionPriority;
|
||||
break;
|
||||
@@ -453,6 +463,11 @@ abstract class SettingsStoreBase with Store {
|
||||
(bool usePolygonScan) =>
|
||||
_sharedPreferences.setBool(PreferencesKey.usePolygonScan, usePolygonScan));
|
||||
|
||||
reaction(
|
||||
(_) => useBaseScan,
|
||||
(bool useBaseScan) =>
|
||||
_sharedPreferences.setBool(PreferencesKey.useBaseScan, useBaseScan));
|
||||
|
||||
reaction((_) => useTronGrid,
|
||||
(bool useTronGrid) => _sharedPreferences.setBool(PreferencesKey.useTronGrid, useTronGrid));
|
||||
|
||||
@@ -808,6 +823,9 @@ abstract class SettingsStoreBase with Store {
|
||||
@observable
|
||||
bool usePolygonScan;
|
||||
|
||||
@observable
|
||||
bool useBaseScan;
|
||||
|
||||
@observable
|
||||
bool useTronGrid;
|
||||
|
||||
@@ -949,6 +967,7 @@ abstract class SettingsStoreBase with Store {
|
||||
TransactionPriority? litecoinTransactionPriority;
|
||||
TransactionPriority? ethereumTransactionPriority;
|
||||
TransactionPriority? polygonTransactionPriority;
|
||||
TransactionPriority? baseTransactionPriority;
|
||||
TransactionPriority? bitcoinCashTransactionPriority;
|
||||
TransactionPriority? wowneroTransactionPriority;
|
||||
TransactionPriority? zanoTransactionPriority;
|
||||
@@ -970,6 +989,10 @@ abstract class SettingsStoreBase with Store {
|
||||
polygonTransactionPriority = polygon?.deserializePolygonTransactionPriority(
|
||||
sharedPreferences.getInt(PreferencesKey.polygonTransactionPriority)!);
|
||||
}
|
||||
if (sharedPreferences.getInt(PreferencesKey.baseTransactionPriority) != null) {
|
||||
baseTransactionPriority = base?.deserializeBaseTransactionPriority(
|
||||
sharedPreferences.getInt(PreferencesKey.baseTransactionPriority)!);
|
||||
}
|
||||
if (sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority) != null) {
|
||||
bitcoinCashTransactionPriority = bitcoinCash?.deserializeBitcoinCashTransactionPriority(
|
||||
sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority)!);
|
||||
@@ -996,6 +1019,7 @@ abstract class SettingsStoreBase with Store {
|
||||
wowneroTransactionPriority ??= wownero?.getDefaultTransactionPriority();
|
||||
decredTransactionPriority ??= decred?.getDecredTransactionPriorityMedium();
|
||||
polygonTransactionPriority ??= polygon?.getDefaultTransactionPriority();
|
||||
baseTransactionPriority ??= base?.getDefaultTransactionPriority();
|
||||
zanoTransactionPriority ??= zano?.getDefaultTransactionPriority();
|
||||
|
||||
final currentBalanceDisplayMode = BalanceDisplayMode.deserialize(
|
||||
@@ -1042,6 +1066,7 @@ abstract class SettingsStoreBase with Store {
|
||||
: defaultSeedPhraseLength;
|
||||
final useEtherscan = sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true;
|
||||
final usePolygonScan = sharedPreferences.getBool(PreferencesKey.usePolygonScan) ?? true;
|
||||
final useBaseScan = sharedPreferences.getBool(PreferencesKey.useBaseScan) ?? true;
|
||||
final useTronGrid = sharedPreferences.getBool(PreferencesKey.useTronGrid) ?? true;
|
||||
final useMempoolFeeAPI = sharedPreferences.getBool(PreferencesKey.useMempoolFeeAPI) ?? true;
|
||||
final defaultNanoRep = sharedPreferences.getString(PreferencesKey.defaultNanoRep) ?? "";
|
||||
@@ -1087,6 +1112,7 @@ abstract class SettingsStoreBase with Store {
|
||||
sharedPreferences.getInt(PreferencesKey.currentBitcoinCashNodeIdKey);
|
||||
final ethereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
|
||||
final polygonNodeId = sharedPreferences.getInt(PreferencesKey.currentPolygonNodeIdKey);
|
||||
final baseNodeId = sharedPreferences.getInt(PreferencesKey.currentBaseNodeIdKey);
|
||||
final nanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
|
||||
final nanoPowNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoPowNodeIdKey);
|
||||
final solanaNodeId = sharedPreferences.getInt(PreferencesKey.currentSolanaNodeIdKey);
|
||||
@@ -1107,6 +1133,8 @@ abstract class SettingsStoreBase with Store {
|
||||
nodeSource.values.firstWhereOrNull((e) => e.uriRaw == ethereumDefaultNodeUri);
|
||||
final polygonNode = nodeSource.get(polygonNodeId) ??
|
||||
nodeSource.values.firstWhereOrNull((e) => e.uriRaw == polygonDefaultNodeUri);
|
||||
final baseNode = nodeSource.get(baseNodeId) ??
|
||||
nodeSource.values.firstWhereOrNull((e) => e.uriRaw == baseDefaultNodeUri);
|
||||
final bitcoinCashElectrumServer = nodeSource.get(bitcoinCashElectrumServerId) ??
|
||||
nodeSource.values.firstWhereOrNull((e) => e.uriRaw == cakeWalletBitcoinCashDefaultNodeUri);
|
||||
final nanoNode = nodeSource.get(nanoNodeId) ??
|
||||
@@ -1180,6 +1208,10 @@ abstract class SettingsStoreBase with Store {
|
||||
nodes[WalletType.polygon] = polygonNode;
|
||||
}
|
||||
|
||||
if (baseNode != null) {
|
||||
nodes[WalletType.base] = baseNode;
|
||||
}
|
||||
|
||||
if (bitcoinCashElectrumServer != null) {
|
||||
nodes[WalletType.bitcoinCash] = bitcoinCashElectrumServer;
|
||||
}
|
||||
@@ -1358,6 +1390,7 @@ abstract class SettingsStoreBase with Store {
|
||||
pinNativeTokenAtTop: pinNativeTokenAtTop,
|
||||
useEtherscan: useEtherscan,
|
||||
usePolygonScan: usePolygonScan,
|
||||
useBaseScan: useBaseScan,
|
||||
useTronGrid: useTronGrid,
|
||||
useMempoolFeeAPI: useMempoolFeeAPI,
|
||||
defaultNanoRep: defaultNanoRep,
|
||||
@@ -1404,6 +1437,7 @@ abstract class SettingsStoreBase with Store {
|
||||
shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||
initialEthereumTransactionPriority: ethereumTransactionPriority,
|
||||
initialPolygonTransactionPriority: polygonTransactionPriority,
|
||||
initialBaseTransactionPriority: baseTransactionPriority,
|
||||
initialSyncMode: savedSyncMode,
|
||||
initialSyncAll: savedSyncAll,
|
||||
shouldShowYatPopup: shouldShowYatPopup,
|
||||
@@ -1455,6 +1489,11 @@ abstract class SettingsStoreBase with Store {
|
||||
priority[WalletType.polygon] = polygon!.deserializePolygonTransactionPriority(
|
||||
sharedPreferences.getInt(PreferencesKey.polygonTransactionPriority)!);
|
||||
}
|
||||
if (base != null &&
|
||||
sharedPreferences.getInt(PreferencesKey.baseTransactionPriority) != null) {
|
||||
priority[WalletType.base] = base!.deserializeBaseTransactionPriority(
|
||||
sharedPreferences.getInt(PreferencesKey.baseTransactionPriority)!);
|
||||
}
|
||||
if (bitcoinCash != null &&
|
||||
sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority) != null) {
|
||||
priority[WalletType.bitcoinCash] = bitcoinCash!.deserializeBitcoinCashTransactionPriority(
|
||||
@@ -1548,6 +1587,7 @@ abstract class SettingsStoreBase with Store {
|
||||
pinNativeTokenAtTop = sharedPreferences.getBool(PreferencesKey.pinNativeTokenAtTop) ?? true;
|
||||
useEtherscan = sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true;
|
||||
usePolygonScan = sharedPreferences.getBool(PreferencesKey.usePolygonScan) ?? true;
|
||||
useBaseScan = sharedPreferences.getBool(PreferencesKey.useBaseScan) ?? true;
|
||||
useTronGrid = sharedPreferences.getBool(PreferencesKey.useTronGrid) ?? true;
|
||||
useMempoolFeeAPI = sharedPreferences.getBool(PreferencesKey.useMempoolFeeAPI) ?? true;
|
||||
defaultNanoRep = sharedPreferences.getString(PreferencesKey.defaultNanoRep) ?? "";
|
||||
@@ -1578,6 +1618,7 @@ abstract class SettingsStoreBase with Store {
|
||||
final havenNodeId = sharedPreferences.getInt(PreferencesKey.currentHavenNodeIdKey);
|
||||
final ethereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
|
||||
final polygonNodeId = sharedPreferences.getInt(PreferencesKey.currentPolygonNodeIdKey);
|
||||
final baseNodeId = sharedPreferences.getInt(PreferencesKey.currentBaseNodeIdKey);
|
||||
final nanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
|
||||
final solanaNodeId = sharedPreferences.getInt(PreferencesKey.currentSolanaNodeIdKey);
|
||||
final tronNodeId = sharedPreferences.getInt(PreferencesKey.currentTronNodeIdKey);
|
||||
@@ -1591,6 +1632,7 @@ abstract class SettingsStoreBase with Store {
|
||||
final havenNode = nodeSource.get(havenNodeId);
|
||||
final ethereumNode = nodeSource.get(ethereumNodeId);
|
||||
final polygonNode = nodeSource.get(polygonNodeId);
|
||||
final baseNode = nodeSource.get(baseNodeId);
|
||||
final bitcoinCashNode = nodeSource.get(bitcoinCashElectrumServerId);
|
||||
final nanoNode = nodeSource.get(nanoNodeId);
|
||||
final solanaNode = nodeSource.get(solanaNodeId);
|
||||
@@ -1624,6 +1666,10 @@ abstract class SettingsStoreBase with Store {
|
||||
nodes[WalletType.polygon] = polygonNode;
|
||||
}
|
||||
|
||||
if (baseNode != null) {
|
||||
nodes[WalletType.base] = baseNode;
|
||||
}
|
||||
|
||||
if (bitcoinCashNode != null) {
|
||||
nodes[WalletType.bitcoinCash] = bitcoinCashNode;
|
||||
}
|
||||
@@ -1784,6 +1830,9 @@ abstract class SettingsStoreBase with Store {
|
||||
case WalletType.polygon:
|
||||
await _sharedPreferences.setInt(PreferencesKey.currentPolygonNodeIdKey, node.key as int);
|
||||
break;
|
||||
case WalletType.base:
|
||||
await _sharedPreferences.setInt(PreferencesKey.currentBaseNodeIdKey, node.key as int);
|
||||
break;
|
||||
case WalletType.solana:
|
||||
await _sharedPreferences.setInt(PreferencesKey.currentSolanaNodeIdKey, node.key as int);
|
||||
break;
|
||||
|
||||
@@ -28,9 +28,11 @@ String getQrImage(WalletType type) {
|
||||
return 'assets/images/dcr_chain_qr.svg';
|
||||
case WalletType.dogecoin:
|
||||
return 'assets/images/doge_chain_qr.svg';
|
||||
case WalletType.base:
|
||||
return 'assets/images/base_chain_QR.svg';
|
||||
case WalletType.banano:
|
||||
case WalletType.haven:
|
||||
case WalletType.none:
|
||||
return 'assets/images/qr-cake.svg';
|
||||
return 'assets/images/qr-cake.png';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:cw_core/cake_hive.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
@@ -13,14 +14,14 @@ class TokenUtilities {
|
||||
Box<WalletInfo> walletInfoSource,
|
||||
) async {
|
||||
final evmWallets = walletInfoSource.values.where(
|
||||
(w) => w.type == WalletType.ethereum || w.type == WalletType.polygon,
|
||||
(w) => isEVMCompatibleChain(w.type),
|
||||
);
|
||||
|
||||
final seen = <String>{};
|
||||
final unique = <Erc20Token>[];
|
||||
|
||||
for (final wallet in evmWallets) {
|
||||
final chain = wallet.type == WalletType.ethereum ? 'ETH' : 'POL';
|
||||
final chain = getTokenNameBasedOnWalletType(wallet.type);
|
||||
final box = await _openEvmTokensBoxFor(wallet);
|
||||
|
||||
for (final t in box.values.where((t) => t.enabled)) {
|
||||
@@ -88,6 +89,7 @@ class TokenUtilities {
|
||||
switch (walletType) {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
final tokens = await loadAllUniqueEvmTokens(walletInfoSource);
|
||||
for (final t in tokens) {
|
||||
if (t.contractAddress.toLowerCase() == lower) return t;
|
||||
@@ -117,6 +119,7 @@ class TokenUtilities {
|
||||
final boxName = switch (walletInfo.type) {
|
||||
WalletType.ethereum => '${walletKey}_${Erc20Token.ethereumBoxName}',
|
||||
WalletType.polygon => '${walletKey}_${Erc20Token.polygonBoxName}',
|
||||
WalletType.base => '${walletKey}_${Erc20Token.baseBoxName}',
|
||||
_ => '${walletKey}_${Erc20Token.ethereumBoxName}',
|
||||
};
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
|
||||
case WalletType.bitcoinCash:
|
||||
case WalletType.dogecoin:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
return true;
|
||||
@@ -79,6 +80,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
|
||||
WalletType.bitcoinCash,
|
||||
WalletType.ethereum,
|
||||
WalletType.polygon,
|
||||
WalletType.base,
|
||||
WalletType.tron,
|
||||
WalletType.solana,
|
||||
WalletType.monero,
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||
import 'package:cake_wallet/utils/qr_util.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/receive_page_option.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
|
||||
import 'package:cake_wallet/buy/wyre/wyre_buy_provider.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/utils/mobx.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
@@ -166,6 +166,7 @@ abstract class BalanceViewModelBase with Store {
|
||||
case WalletType.haven:
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
return S.current.xmr_full_balance;
|
||||
|
||||
@@ -873,6 +873,7 @@ abstract class DashboardViewModelBase with Store {
|
||||
case WalletType.bitcoinCash:
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.solana:
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/core/fiat_conversion_service.dart';
|
||||
import 'package:cake_wallet/entities/fiat_api_mode.dart';
|
||||
import 'package:cake_wallet/entities/erc20_token_info_moralis.dart';
|
||||
@@ -99,6 +100,18 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
await polygon!.addErc20Token(_balanceViewModel.wallet, polygonToken);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.base) {
|
||||
final baseToken = Erc20Token(
|
||||
name: token.name,
|
||||
symbol: token.title,
|
||||
decimal: token.decimals,
|
||||
contractAddress: contractAddress.toLowerCase(),
|
||||
iconPath: token.iconPath,
|
||||
isPotentialScam: token.isPotentialScam,
|
||||
);
|
||||
await base!.addErc20Token(_balanceViewModel.wallet, baseToken);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.solana) {
|
||||
final splToken = token.copyWith(enabled: true);
|
||||
await solana!.addSPLToken(
|
||||
@@ -136,6 +149,10 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
return polygon!.isTokenAlreadyAdded(_balanceViewModel.wallet, contractAddress);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.base) {
|
||||
return base!.isTokenAlreadyAdded(_balanceViewModel.wallet, contractAddress);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.solana) {
|
||||
return solana!.isTokenAlreadyAdded(_balanceViewModel.wallet, contractAddress);
|
||||
}
|
||||
@@ -163,6 +180,10 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
await polygon!.deleteErc20Token(_balanceViewModel.wallet, token as Erc20Token);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.base) {
|
||||
await base!.deleteErc20Token(_balanceViewModel.wallet, token as Erc20Token);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.solana) {
|
||||
await solana!.deleteSPLToken(_balanceViewModel.wallet, token);
|
||||
}
|
||||
@@ -189,16 +210,14 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isEthereum = _balanceViewModel.wallet.type == WalletType.ethereum;
|
||||
|
||||
bool isPotentialScamViaMoralis = await _isPotentialScamTokenViaMoralis(
|
||||
contractAddress,
|
||||
isEthereum ? 'eth' : 'polygon',
|
||||
getChainNameBasedOnWalletType(_balanceViewModel.wallet.type),
|
||||
);
|
||||
|
||||
bool isUnverifiedContract = await _isContractUnverified(
|
||||
contractAddress,
|
||||
isEthereum: isEthereum,
|
||||
chainId: getChainIdBasedOnWalletType(_balanceViewModel.wallet.type).toString(),
|
||||
);
|
||||
|
||||
final showWarningForContractAddress = isPotentialScamViaMoralis || isUnverifiedContract;
|
||||
@@ -219,6 +238,9 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
case WalletType.polygon:
|
||||
defaultTokenAddresses = polygon!.getDefaultTokenContractAddresses();
|
||||
break;
|
||||
case WalletType.base:
|
||||
defaultTokenAddresses = base!.getDefaultTokenContractAddresses();
|
||||
break;
|
||||
case WalletType.solana:
|
||||
defaultTokenAddresses = solana!.getDefaultTokenContractAddresses();
|
||||
break;
|
||||
@@ -306,13 +328,13 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
|
||||
Future<bool> _isContractUnverified(
|
||||
String contractAddress, {
|
||||
required bool isEthereum,
|
||||
required String chainId,
|
||||
}) async {
|
||||
final uri = Uri.https(
|
||||
"api.etherscan.io",
|
||||
"/v2/api",
|
||||
{
|
||||
"chainid": isEthereum ? "1" : "137",
|
||||
"chainid": chainId,
|
||||
"module": "contract",
|
||||
"action": "getsourcecode",
|
||||
"address": contractAddress,
|
||||
@@ -354,6 +376,10 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
return await polygon!.getErc20Token(_balanceViewModel.wallet, contractAddress);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.base) {
|
||||
return await base!.getErc20Token(_balanceViewModel.wallet, contractAddress);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.solana) {
|
||||
return await solana!.getSPLToken(_balanceViewModel.wallet, contractAddress);
|
||||
}
|
||||
@@ -395,6 +421,11 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
if (!value) polygon!.removeTokenTransactionsInHistory(_balanceViewModel.wallet, token);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.base) {
|
||||
base!.addErc20Token(_balanceViewModel.wallet, token as Erc20Token);
|
||||
if (!value) base!.removeTokenTransactionsInHistory(_balanceViewModel.wallet, token);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.solana) {
|
||||
final address = solana!.getTokenAddress(token);
|
||||
solana!.addSPLToken(_balanceViewModel.wallet, token, address);
|
||||
@@ -448,6 +479,14 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
..sort(_sortFunc));
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.base) {
|
||||
tokens.addAll(base!
|
||||
.getERC20Currencies(_balanceViewModel.wallet)
|
||||
.where((element) => _matchesSearchText(element))
|
||||
.toList()
|
||||
..sort(_sortFunc));
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.solana) {
|
||||
tokens.addAll(solana!
|
||||
.getSPLTokenCurrencies(_balanceViewModel.wallet)
|
||||
@@ -489,7 +528,7 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
bool _matchesSearchText(CryptoCurrency asset) {
|
||||
final address = getTokenAddressBasedOnWallet(asset);
|
||||
|
||||
// The homes settings would only be displayed for either of Tron, Ethereum, Polygon or Solana Wallets.
|
||||
// The homes settings would only be displayed for either of Tron, Ethereum, Polygon, Base or Solana Wallets.
|
||||
if (address == null) return false;
|
||||
|
||||
return searchText.isEmpty ||
|
||||
@@ -515,11 +554,15 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
return polygon!.getTokenAddress(asset);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.base) {
|
||||
return base!.getTokenAddress(asset);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.zano) {
|
||||
return zano!.getZanoAssetAddress(asset);
|
||||
}
|
||||
|
||||
// We return null if it's neither Tron, Polygon, Ethereum or Solana wallet (which is actually impossible because we only display home settings for either of these three wallets).
|
||||
// We return null if it's neither Tron, Polygon, Base, Ethereum or Solana wallet (which is actually impossible because we only display home settings for either of these four wallets).
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
import 'package:cake_wallet/entities/balance_display_mode.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
@@ -134,6 +135,11 @@ class TransactionListItem extends ActionListItem with Keyable {
|
||||
return asset;
|
||||
}
|
||||
|
||||
if (balanceViewModel.wallet.type == WalletType.base) {
|
||||
final asset = base!.assetOfTransaction(balanceViewModel.wallet, transaction);
|
||||
return asset;
|
||||
}
|
||||
|
||||
if (balanceViewModel.wallet.type == WalletType.solana) {
|
||||
final asset = solana!.assetOfTransaction(balanceViewModel.wallet, transaction);
|
||||
return asset;
|
||||
@@ -186,6 +192,13 @@ class TransactionListItem extends ActionListItem with Keyable {
|
||||
cryptoAmount: polygon!.formatterPolygonAmountToDouble(transaction: transaction),
|
||||
price: price);
|
||||
break;
|
||||
case WalletType.base:
|
||||
final asset = base!.assetOfTransaction(balanceViewModel.wallet, transaction);
|
||||
final price = balanceViewModel.fiatConvertationStore.prices[asset];
|
||||
amount = calculateFiatAmountRaw(
|
||||
cryptoAmount: base!.formatterBaseAmountToDouble(transaction: transaction),
|
||||
price: price);
|
||||
break;
|
||||
case WalletType.nano:
|
||||
amount = calculateFiatAmountRaw(
|
||||
cryptoAmount: double.parse(nanoUtil!.getRawAsUsableString(
|
||||
|
||||
@@ -17,6 +17,7 @@ import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/xoswap_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_item.dart';
|
||||
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||
import 'package:cake_wallet/store/dashboard/trades_store.dart';
|
||||
@@ -306,6 +307,9 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
wallet.currency == CryptoCurrency.maticpoly &&
|
||||
tradeFrom?.tag == CryptoCurrency.maticpoly.tag;
|
||||
|
||||
bool _isBaseToken() =>
|
||||
wallet.currency == CryptoCurrency.baseEth && tradeFrom?.tag == CryptoCurrency.baseEth.title;
|
||||
|
||||
bool _isTronToken() =>
|
||||
wallet.currency == CryptoCurrency.trx && tradeFrom?.tag == CryptoCurrency.trx.title;
|
||||
|
||||
@@ -317,7 +321,8 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
_isEthToken() ||
|
||||
_isPolygonToken() ||
|
||||
_isSplToken() ||
|
||||
_isTronToken();
|
||||
_isTronToken() ||
|
||||
_isBaseToken();
|
||||
}
|
||||
|
||||
PaymentURI? get paymentUri {
|
||||
@@ -343,6 +348,8 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
// TODO: Expand ERC681URI support to Polygon(modify decoding flow for QRs, pay anything, and deep link handling)
|
||||
case WalletType.polygon:
|
||||
return PolygonURI(amount: amount, address: inputAddress);
|
||||
case WalletType.base:
|
||||
return BaseURI(amount: amount, address: inputAddress);
|
||||
case WalletType.solana:
|
||||
return SolanaURI(amount: amount, address: inputAddress);
|
||||
case WalletType.tron:
|
||||
@@ -377,7 +384,7 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
contractAddress: null,
|
||||
);
|
||||
} else {
|
||||
if (wallet.type == WalletType.polygon || wallet.type == WalletType.ethereum) {
|
||||
if (isEVMCompatibleChain(wallet.type)) {
|
||||
final erc20Token = TokenUtilities.findErc20Token(currency, wallet);
|
||||
|
||||
if (erc20Token != null) {
|
||||
|
||||
@@ -878,6 +878,10 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
depositCurrency = CryptoCurrency.maticpoly;
|
||||
receiveCurrency = CryptoCurrency.xmr;
|
||||
break;
|
||||
case WalletType.base:
|
||||
depositCurrency = CryptoCurrency.baseEth;
|
||||
receiveCurrency = CryptoCurrency.xmr;
|
||||
break;
|
||||
case WalletType.solana:
|
||||
depositCurrency = CryptoCurrency.sol;
|
||||
receiveCurrency = CryptoCurrency.xmr;
|
||||
|
||||
@@ -80,6 +80,7 @@ abstract class NodeCreateOrEditViewModelBase with Store {
|
||||
switch (_walletType) {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.solana:
|
||||
case WalletType.banano:
|
||||
case WalletType.nano:
|
||||
|
||||
@@ -3,11 +3,13 @@ import 'dart:convert';
|
||||
import 'package:cake_wallet/core/seed_validator.dart';
|
||||
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
|
||||
import 'package:cake_wallet/entities/qr_scanner.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/restore/restore_mode.dart';
|
||||
import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
@@ -29,6 +31,7 @@ class WalletRestoreFromQRCode {
|
||||
'litecoin_wallet': WalletType.litecoin,
|
||||
'ethereum-wallet': WalletType.ethereum,
|
||||
'polygon-wallet': WalletType.polygon,
|
||||
'base-wallet': WalletType.base,
|
||||
'nano-wallet': WalletType.nano,
|
||||
'nano_wallet': WalletType.nano,
|
||||
'bitcoincash': WalletType.bitcoinCash,
|
||||
@@ -206,15 +209,7 @@ class WalletRestoreFromQRCode {
|
||||
: throw Exception('Unexpected restore mode: spend_key or view_key is invalid');
|
||||
}
|
||||
|
||||
if (type == WalletType.ethereum && credentials.containsKey('private_key')) {
|
||||
final privateKey = credentials['private_key'] as String;
|
||||
if (privateKey.isEmpty) {
|
||||
throw Exception('Unexpected restore mode: private_key');
|
||||
}
|
||||
return WalletRestoreMode.keys;
|
||||
}
|
||||
|
||||
if (type == WalletType.polygon && credentials.containsKey('private_key')) {
|
||||
if (isEVMCompatibleChain(type) && credentials.containsKey('private_key')) {
|
||||
final privateKey = credentials['private_key'] as String;
|
||||
if (privateKey.isEmpty) {
|
||||
throw Exception('Unexpected restore mode: private_key');
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
import 'package:cake_wallet/dogecoin/dogecoin.dart';
|
||||
@@ -90,6 +91,8 @@ abstract class FeesViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
return transactionPriority == bitcoinCash!.getBitcoinCashTransactionPrioritySlow();
|
||||
case WalletType.polygon:
|
||||
return transactionPriority == polygon!.getPolygonTransactionPrioritySlow();
|
||||
case WalletType.base:
|
||||
return transactionPriority == base!.getBaseTransactionPrioritySlow();
|
||||
case WalletType.decred:
|
||||
return transactionPriority == decred!.getDecredTransactionPrioritySlow();
|
||||
case WalletType.dogecoin:
|
||||
@@ -198,6 +201,9 @@ abstract class FeesViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
case WalletType.polygon:
|
||||
_settingsStore.priority[wallet.type] = polygon!.getDefaultTransactionPriority();
|
||||
break;
|
||||
case WalletType.base:
|
||||
_settingsStore.priority[wallet.type] = base!.getDefaultTransactionPriority();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart';
|
||||
@@ -114,6 +115,9 @@ abstract class OutputBase with Store {
|
||||
case WalletType.polygon:
|
||||
_amount = polygon!.formatterPolygonParseAmount(_cryptoAmount);
|
||||
break;
|
||||
case WalletType.base:
|
||||
_amount = base!.formatterBaseParseAmount(_cryptoAmount);
|
||||
break;
|
||||
case WalletType.wownero:
|
||||
_amount = wownero!.formatterWowneroParseAmount(amount: _cryptoAmount);
|
||||
break;
|
||||
@@ -196,6 +200,10 @@ abstract class OutputBase with Store {
|
||||
return polygon!.formatterPolygonAmountToDouble(amount: BigInt.from(fee));
|
||||
}
|
||||
|
||||
if (_wallet.type == WalletType.base) {
|
||||
return base!.formatterBaseAmountToDouble(amount: BigInt.from(fee));
|
||||
}
|
||||
|
||||
if (_wallet.type == WalletType.zano) {
|
||||
return zano!.formatterIntAmountToDouble(
|
||||
amount: fee, currency: cryptoCurrencyHandler(), forFee: true);
|
||||
@@ -328,6 +336,7 @@ abstract class OutputBase with Store {
|
||||
case WalletType.monero:
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
case WalletType.haven:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:cake_wallet/view_model/send/template_view_model.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/entities/template.dart';
|
||||
@@ -55,6 +56,7 @@ abstract class SendTemplateViewModelBase with Store {
|
||||
_wallet.type != WalletType.haven &&
|
||||
_wallet.type != WalletType.ethereum &&
|
||||
_wallet.type != WalletType.polygon &&
|
||||
_wallet.type != WalletType.base &&
|
||||
_wallet.type != WalletType.solana &&
|
||||
_wallet.type != WalletType.tron;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/core/address_validator.dart';
|
||||
import 'package:cake_wallet/core/amount_validator.dart';
|
||||
@@ -45,6 +46,7 @@ import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_mod
|
||||
import 'package:cake_wallet/wownero/wownero.dart';
|
||||
import 'package:cake_wallet/zano/zano.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/exceptions.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_core/sync_status.dart';
|
||||
@@ -215,6 +217,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
switch (type) {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.tron:
|
||||
case WalletType.solana:
|
||||
return wallet.currency;
|
||||
@@ -708,6 +711,9 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
case WalletType.polygon:
|
||||
return polygon!.createPolygonTransactionCredentials(outputs,
|
||||
priority: priority!, currency: selectedCryptoCurrency);
|
||||
case WalletType.base:
|
||||
return base!.createBaseTransactionCredentials(outputs,
|
||||
priority: priority!, currency: selectedCryptoCurrency);
|
||||
case WalletType.solana:
|
||||
return solana!
|
||||
.createSolanaTransactionCredentials(outputs, currency: selectedCryptoCurrency);
|
||||
@@ -833,6 +839,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
}
|
||||
if (walletType == WalletType.ethereum ||
|
||||
walletType == WalletType.polygon ||
|
||||
walletType == WalletType.base ||
|
||||
walletType == WalletType.haven) {
|
||||
if (errorMessage.contains('gas required exceeds allowance')) {
|
||||
return S.current.gas_exceeds_allowance;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
|
||||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||
@@ -75,6 +76,9 @@ abstract class PrivacySettingsViewModelBase with Store {
|
||||
@computed
|
||||
bool get usePolygonScan => _settingsStore.usePolygonScan;
|
||||
|
||||
@computed
|
||||
bool get useBaseScan => _settingsStore.useBaseScan;
|
||||
|
||||
@computed
|
||||
bool get useTronGrid => _settingsStore.useTronGrid;
|
||||
|
||||
@@ -112,6 +116,8 @@ abstract class PrivacySettingsViewModelBase with Store {
|
||||
|
||||
bool get canUsePolygonScan => _wallet.type == WalletType.polygon;
|
||||
|
||||
bool get canUseBaseScan => _wallet.type == WalletType.base;
|
||||
|
||||
bool get canUseTronGrid => _wallet.type == WalletType.tron;
|
||||
|
||||
bool get canUseMempoolFeeAPI => _wallet.type == WalletType.bitcoin;
|
||||
@@ -177,6 +183,12 @@ abstract class PrivacySettingsViewModelBase with Store {
|
||||
polygon!.updatePolygonScanUsageState(_wallet, value);
|
||||
}
|
||||
|
||||
@action
|
||||
void setUseBaseScan(bool value) {
|
||||
_settingsStore.useBaseScan = value;
|
||||
base!.updateBaseScanUsageState(_wallet, value);
|
||||
}
|
||||
|
||||
@action
|
||||
void setUseTronGrid(bool value) {
|
||||
_settingsStore.useTronGrid = value;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:cake_wallet/tron/tron.dart';
|
||||
import 'package:cake_wallet/wownero/wownero.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
@@ -73,6 +74,9 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
case WalletType.polygon:
|
||||
_addPolygonListItems(tx, dateFormat);
|
||||
break;
|
||||
case WalletType.base:
|
||||
_addBaseListItems(tx, dateFormat);
|
||||
break;
|
||||
case WalletType.solana:
|
||||
_addSolanaListItems(tx, dateFormat);
|
||||
break;
|
||||
@@ -198,6 +202,8 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
return 'https://${wallet.isTestnet ? "testnet" : "dcrdata"}.decred.org/tx/${txId.split(':')[0]}';
|
||||
case WalletType.dogecoin:
|
||||
return 'https://blockchair.com/dogecoin/transaction/${txId}';
|
||||
case WalletType.base:
|
||||
return 'https://basescan.org/tx/${txId}';
|
||||
case WalletType.none:
|
||||
return '';
|
||||
}
|
||||
@@ -233,6 +239,8 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
return S.current.view_transaction_on + 'explorer.zano.org';
|
||||
case WalletType.decred:
|
||||
return S.current.view_transaction_on + 'dcrdata.decred.org';
|
||||
case WalletType.base:
|
||||
return S.current.view_transaction_on + 'basescan.org';
|
||||
case WalletType.none:
|
||||
return '';
|
||||
}
|
||||
@@ -548,6 +556,56 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
items.addAll(_items);
|
||||
}
|
||||
|
||||
void _addBaseListItems(TransactionInfo tx, DateFormat dateFormat) {
|
||||
final _items = [
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_transaction_id,
|
||||
value: tx.txHash,
|
||||
key: ValueKey('standard_list_item_transaction_details_id_key'),
|
||||
),
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_date,
|
||||
value: dateFormat.format(tx.date),
|
||||
key: ValueKey('standard_list_item_transaction_details_date_key'),
|
||||
),
|
||||
StandartListItem(
|
||||
title: S.current.confirmations,
|
||||
value: tx.confirmations.toString(),
|
||||
key: ValueKey('standard_list_item_transaction_confirmations_key'),
|
||||
),
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_height,
|
||||
value: '${tx.height}',
|
||||
key: ValueKey('standard_list_item_transaction_details_height_key'),
|
||||
),
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_amount,
|
||||
value: tx.amountFormatted(),
|
||||
key: ValueKey('standard_list_item_transaction_details_amount_key'),
|
||||
),
|
||||
if (tx.feeFormatted()?.isNotEmpty ?? false)
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_fee,
|
||||
value: tx.feeFormatted()!,
|
||||
key: ValueKey('standard_list_item_transaction_details_fee_key'),
|
||||
),
|
||||
if (showRecipientAddress && tx.to != null && tx.direction == TransactionDirection.outgoing)
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_recipient_address,
|
||||
value: tx.to!,
|
||||
key: ValueKey('standard_list_item_transaction_details_recipient_address_key'),
|
||||
),
|
||||
if (tx.direction == TransactionDirection.incoming && tx.from != null)
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_source_address,
|
||||
value: tx.from!,
|
||||
key: ValueKey('standard_list_item_transaction_details_source_address_key'),
|
||||
),
|
||||
];
|
||||
|
||||
items.addAll(_items);
|
||||
}
|
||||
|
||||
void _addSolanaListItems(TransactionInfo tx, DateFormat dateFormat) {
|
||||
final _items = [
|
||||
StandartListItem(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:developer' as dev;
|
||||
import 'dart:core';
|
||||
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/core/fiat_conversion_service.dart';
|
||||
import 'package:cake_wallet/core/payment_uris.dart';
|
||||
@@ -30,6 +31,7 @@ import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_i
|
||||
import 'package:cake_wallet/wownero/wownero.dart';
|
||||
import 'package:cw_core/amount_converter.dart';
|
||||
import 'package:cw_core/currency.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
@@ -38,7 +40,6 @@ part 'wallet_address_list_view_model.g.dart';
|
||||
|
||||
class WalletAddressListViewModel = WalletAddressListViewModelBase with _$WalletAddressListViewModel;
|
||||
|
||||
|
||||
abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewModel with Store {
|
||||
WalletAddressListViewModelBase({
|
||||
required AppStore appStore,
|
||||
@@ -143,6 +144,8 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
return DecredURI(amount: amount, address: address.address);
|
||||
case WalletType.dogecoin:
|
||||
return DogeURI(amount: amount, address: address.address);
|
||||
case WalletType.base:
|
||||
return BaseURI(amount: amount, address: address.address);
|
||||
case WalletType.none:
|
||||
throw Exception('Unexpected type: ${type.toString()}');
|
||||
}
|
||||
@@ -263,6 +266,12 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.base) {
|
||||
final primaryAddress = base!.getAddress(wallet);
|
||||
|
||||
addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.solana) {
|
||||
final primaryAddress = solana!.getAddress(wallet);
|
||||
|
||||
@@ -435,7 +444,12 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
'assets/images/zano_icon.svg',
|
||||
'assets/images/more_tokens.svg',
|
||||
];
|
||||
|
||||
case WalletType.base:
|
||||
return [
|
||||
'assets/images/eth_icon.svg',
|
||||
'assets/images/usdc_icon.svg',
|
||||
'assets/images/more_tokens.svg',
|
||||
];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
@@ -457,6 +471,8 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
return 'assets/images/trx_chain_mono.svg';
|
||||
case WalletType.zano:
|
||||
return 'assets/images/zano_chain_mono.svg';
|
||||
case WalletType.base:
|
||||
return 'assets/images/base_chain_mono.svg';
|
||||
default:
|
||||
return 'assets/images/eth_chain_mono.svg';
|
||||
}
|
||||
|
||||
@@ -126,6 +126,7 @@ abstract class WalletKeysViewModelBase with Store {
|
||||
break;
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
items.addAll([
|
||||
@@ -252,6 +253,8 @@ abstract class WalletKeysViewModelBase with Store {
|
||||
return 'banano-wallet';
|
||||
case WalletType.polygon:
|
||||
return 'polygon-wallet';
|
||||
case WalletType.base:
|
||||
return 'base-wallet';
|
||||
case WalletType.solana:
|
||||
return 'solana-wallet';
|
||||
case WalletType.tron:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user