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>

David Adegoke committed Oct 11, 2025 at 02:24 UTC 2a74317aff2357a80494f88e2642d1e8383f2e8a
139 files changed +1828 -197
.github/workflows/automated_integration_test.yml
+2
@@ -163,6 +163,7 @@ jobs:
163 echo "const litecoinTestWalletSeeds = '${{ secrets.LITECOIN_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
164 echo "const bitcoinCashTestWalletSeeds = '${{ secrets.BITCOIN_CASH_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
165 echo "const polygonTestWalletSeeds = '${{ secrets.POLYGON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
166 + echo "const baseTestWalletSeeds = '${{ secrets.BASE_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
167 echo "const solanaTestWalletSeeds = '${{ secrets.SOLANA_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
168 echo "const solanaTestWalletSeeds2 = '${{ secrets.SOLANA_TEST_WALLET_SEEDS2 }}';" >> lib/.secrets.g.dart
169 echo "const tronTestWalletSeeds = '${{ secrets.TRON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
@@ -176,6 +177,7 @@ jobs:
177 echo "const litecoinTestWalletReceiveAddress = '${{ secrets.LITECOIN_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
178 echo "const bitcoinCashTestWalletReceiveAddress = '${{ secrets.BITCOIN_CASH_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
179 echo "const polygonTestWalletReceiveAddress = '${{ secrets.POLYGON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
180 + echo "const baseTestWalletReceiveAddress = '${{ secrets.BASE_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
181 echo "const solanaTestWalletReceiveAddress = '${{ secrets.SOLANA_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
182 echo "const tronTestWalletReceiveAddress = '${{ secrets.TRON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
183 echo "const nanoTestWalletReceiveAddress = '${{ secrets.NANO_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
.github/workflows/no_restricted_imports.yaml
+1
@@ -20,6 +20,7 @@ jobs:
20 "cw_mweb"
21 "cw_nano"
22 "cw_polygon"
23 + "cw_base"
24 "cw_solana"
25 "cw_tron"
26 "cw_wownero"
.github/workflows/pr_test_build_android.yml
+2
@@ -157,6 +157,7 @@ jobs:
157 echo "const litecoinTestWalletSeeds = '${{ secrets.LITECOIN_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
158 echo "const bitcoinCashTestWalletSeeds = '${{ secrets.BITCOIN_CASH_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
159 echo "const polygonTestWalletSeeds = '${{ secrets.POLYGON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
160 + echo "const baseTestWalletSeeds = '${{ secrets.BASE_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
161 echo "const solanaTestWalletSeeds = '${{ secrets.SOLANA_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
162 echo "const tronTestWalletSeeds = '${{ secrets.TRON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
163 echo "const nanoTestWalletSeeds = '${{ secrets.NANO_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
@@ -168,6 +169,7 @@ jobs:
169 echo "const litecoinTestWalletReceiveAddress = '${{ secrets.LITECOIN_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
170 echo "const bitcoinCashTestWalletReceiveAddress = '${{ secrets.BITCOIN_CASH_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
171 echo "const polygonTestWalletReceiveAddress = '${{ secrets.POLYGON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
172 + echo "const baseTestWalletReceiveAddress = '${{ secrets.BASE_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
173 echo "const solanaTestWalletReceiveAddress = '${{ secrets.SOLANA_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
174 echo "const tronTestWalletReceiveAddress = '${{ secrets.TRON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
175 echo "const nanoTestWalletReceiveAddress = '${{ secrets.NANO_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
.github/workflows/pr_test_build_linux.yml
+2
@@ -150,6 +150,7 @@ jobs:
150 echo "const litecoinTestWalletSeeds = '${{ secrets.LITECOIN_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
151 echo "const bitcoinCashTestWalletSeeds = '${{ secrets.BITCOIN_CASH_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
152 echo "const polygonTestWalletSeeds = '${{ secrets.POLYGON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
153 + echo "const baseTestWalletSeeds = '${{ secrets.BASE_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
154 echo "const solanaTestWalletSeeds = '${{ secrets.SOLANA_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
155 echo "const tronTestWalletSeeds = '${{ secrets.TRON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
156 echo "const nanoTestWalletSeeds = '${{ secrets.NANO_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
@@ -162,6 +163,7 @@ jobs:
163 echo "const litecoinTestWalletReceiveAddress = '${{ secrets.LITECOIN_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
164 echo "const bitcoinCashTestWalletReceiveAddress = '${{ secrets.BITCOIN_CASH_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
165 echo "const polygonTestWalletReceiveAddress = '${{ secrets.POLYGON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
166 + echo "const baseTestWalletReceiveAddress = '${{ secrets.BASE_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
167 echo "const solanaTestWalletReceiveAddress = '${{ secrets.SOLANA_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
168 echo "const tronTestWalletReceiveAddress = '${{ secrets.TRON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
169 echo "const nanoTestWalletReceiveAddress = '${{ secrets.NANO_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
.gitignore
+1
@@ -141,6 +141,7 @@ lib/wownero/wownero.dart
141 lib/zano/zano.dart
142 lib/decred/decred.dart
143 lib/dogecoin/dogecoin.dart
144 +lib/base/base.dart
145
146 ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x.png
147 ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x~ipad.png
README.md
+1
@@ -25,6 +25,7 @@ Cake Wallet includes support for several cryptocurrencies, including:
25 * Litecoin (LTC)
26 * Bitcoin Cash (BCH)
27 * Polygon (POL)
28 +* Base (BASE)
29 * Solana (SOL)
30 * Tron (TRX)
31 * Nano (XNO)
analysis_options.yaml
+1
@@ -23,6 +23,7 @@ analyzer:
23 lib/tron/cw_tron.dart,
24 lib/wownero/cw_wownero.dart,
25 lib/zano/cw_zano.dart,
26 + lib/base/cw_base.dart,
27 ]
28 language:
29 strict-casts: true
android/app/src/main/AndroidManifestBase.xml
+3
@@ -106,6 +106,9 @@
106 <data android:scheme="dogecoin" />
107 <data android:scheme="dogecoin-wallet" />
108 <data android:scheme="dogecoin_wallet" />
109 + <data android:scheme="base" />
110 + <data android:scheme="base-wallet" />
111 + <data android:scheme="base_wallet" />
112 </intent-filter>
113 <!-- nano-gpt link scheme -->
114 <intent-filter android:autoVerify="true">
assets/base_node_list.yml new
+15
@@ -0,0 +1,15 @@
1 +-
2 + uri: base.nownodes.io
3 + is_default: true
4 + useSSL: true
5 + isEnabledForAutoSwitching: true
6 +-
7 + uri: base.llamarpc.com
8 + useSSL: true
9 +-
10 + uri: base-rpc.publicnode.com
11 + useSSL: true
12 + isEnabledForAutoSwitching: true
13 +-
14 + uri: 1rpc.io/base
15 + useSSL: true
\ No newline at end of file
assets/images/base_chain_QR.svg new
+5
@@ -0,0 +1,5 @@
1 +<svg width="45" height="45" viewBox="0 0 45 45" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<rect x="1" y="1" width="43" height="43" rx="6.03125" fill="white"/>
3 +<rect x="1" y="1" width="43" height="43" rx="6.03125" stroke="black" stroke-width="2"/>
4 +<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"/>
5 +</svg>
assets/images/base_chain_mono.svg new
+3
@@ -0,0 +1,3 @@
1 +<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<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"/>
3 +</svg>
assets/images/base_icon.png
Binary files /dev/null and b/assets/images/base_icon.png differ
cakewallet.bat
+2 -2
@@ -1,5 +1,5 @@
1 @echo off
2 -set cw_win_app_config=--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --dogecoin
2 +set cw_win_app_config=--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --dogecoin --base
3 set cw_root=%cd%
4 set cw_archive_name=Cake Wallet.zip
5 set cw_archive_path=%cw_root%\%cw_archive_name%
@@ -24,7 +24,7 @@ IF NOT EXIST "%secrets_file_path%" (
24 ) ELSE (echo === Using previously/already generated secrets file: %secrets_file_path% ===)
25
26 echo === Generating mobx models ===
27 -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 (
27 +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 (
28 cd %%i
29 call flutter pub get > nul
30 call dart run build_runner build --delete-conflicting-outputs > nul
cw_base/.gitignore new
+31
@@ -0,0 +1,31 @@
1 +# Miscellaneous
2 +*.class
3 +*.log
4 +*.pyc
5 +*.swp
6 +.DS_Store
7 +.atom/
8 +.buildlog/
9 +.history
10 +.svn/
11 +migrate_working_dir/
12 +
13 +# IntelliJ related
14 +*.iml
15 +*.ipr
16 +*.iws
17 +.idea/
18 +
19 +# The .vscode folder contains launch configuration and tasks you configure in
20 +# VS Code which you may wish to be included in version control, so this line
21 +# is commented out by default.
22 +#.vscode/
23 +
24 +# Flutter/Dart/Pub related
25 +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
26 +/pubspec.lock
27 +**/doc/api/
28 +.dart_tool/
29 +.flutter-plugins
30 +.flutter-plugins-dependencies
31 +build/
cw_base/.metadata new
+10
@@ -0,0 +1,10 @@
1 +# This file tracks properties of this Flutter project.
2 +# Used by Flutter tool to assess capabilities and perform upgrades etc.
3 +#
4 +# This file should be version controlled and should not be manually edited.
5 +
6 +version:
7 + revision: "fcf2c11572af6f390246c056bc905eca609533a0"
8 + channel: "[user-branch]"
9 +
10 +project_type: package
cw_base/CHANGELOG.md new
+3
@@ -0,0 +1,3 @@
1 +## 0.0.1
2 +
3 +* TODO: Describe initial release.
cw_base/LICENSE new
+1
@@ -0,0 +1 @@
1 +TODO: Add your license here.
cw_base/README.md new
+39
@@ -0,0 +1,39 @@
1 +<!--
2 +This README describes the package. If you publish this package to pub.dev,
3 +this README's contents appear on the landing page for your package.
4 +
5 +For information about how to write a good package README, see the guide for
6 +[writing package pages](https://dart.dev/tools/pub/writing-package-pages).
7 +
8 +For general information about developing packages, see the Dart guide for
9 +[creating packages](https://dart.dev/guides/libraries/create-packages)
10 +and the Flutter guide for
11 +[developing packages and plugins](https://flutter.dev/to/develop-packages).
12 +-->
13 +
14 +TODO: Put a short description of the package here that helps potential users
15 +know whether this package might be useful for them.
16 +
17 +## Features
18 +
19 +TODO: List what your package can do. Maybe include images, gifs, or videos.
20 +
21 +## Getting started
22 +
23 +TODO: List prerequisites and provide or point to information on how to
24 +start using the package.
25 +
26 +## Usage
27 +
28 +TODO: Include short and useful examples for package users. Add longer examples
29 +to `/example` folder.
30 +
31 +```dart
32 +const like = 'sample';
33 +```
34 +
35 +## Additional information
36 +
37 +TODO: Tell users more about the package: where to find more information, how to
38 +contribute to the package, how to file issues, what response they can expect
39 +from the package authors, and more.
cw_base/analysis_options.yaml new
+4
@@ -0,0 +1,4 @@
1 +include: package:flutter_lints/flutter.yaml
2 +
3 +# Additional information about this file can be found at
4 +# https://dart.dev/guides/language/analysis-options
cw_base/lib/base_client.dart new
+103
@@ -0,0 +1,103 @@
1 +import 'dart:convert';
2 +
3 +import 'package:cw_evm/evm_chain_client.dart';
4 +import 'package:cw_evm/.secrets.g.dart' as secrets;
5 +import 'package:cw_evm/evm_chain_transaction_model.dart';
6 +import 'package:flutter/foundation.dart';
7 +import 'package:web3dart/web3dart.dart';
8 +
9 +class BaseClient extends EVMChainClient {
10 + @override
11 + Transaction createTransaction({
12 + required EthereumAddress from,
13 + required EthereumAddress to,
14 + required EtherAmount amount,
15 + EtherAmount? maxPriorityFeePerGas,
16 + Uint8List? data,
17 + int? maxGas,
18 + EtherAmount? gasPrice,
19 + EtherAmount? maxFeePerGas,
20 + }) {
21 + EtherAmount? finalGasPrice = gasPrice;
22 +
23 + if (gasPrice == null && maxFeePerGas != null) {
24 + // If we have EIP-1559 parameters but no legacy gasPrice, then use maxFeePerGas as gasPrice
25 + finalGasPrice = maxFeePerGas;
26 + }
27 +
28 + return Transaction(
29 + from: from,
30 + to: to,
31 + value: amount,
32 + data: data,
33 + maxGas: maxGas,
34 + gasPrice: finalGasPrice,
35 + // maxFeePerGas: maxFeePerGas,
36 + // maxPriorityFeePerGas: maxPriorityFeePerGas,
37 + );
38 + }
39 +
40 + @override
41 + Uint8List prepareSignedTransactionForSending(Uint8List signedTransaction) => signedTransaction;
42 +
43 + @override
44 + int get chainId => 8453;
45 +
46 + @override
47 + Future<List<EVMChainTransactionModel>> fetchTransactions(String address,
48 + {String? contractAddress}) async {
49 + try {
50 + final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
51 + "chainid": "$chainId",
52 + "module": "account",
53 + "action": contractAddress != null ? "tokentx" : "txlist",
54 + if (contractAddress != null) "contractaddress": contractAddress,
55 + "address": address,
56 + "apikey": secrets.etherScanApiKey,
57 + }));
58 +
59 + final jsonResponse = json.decode(response.body) as Map<String, dynamic>;
60 +
61 + if (response.statusCode >= 200 && response.statusCode < 300 && jsonResponse['status'] != 0) {
62 + final res = (jsonResponse['result'] as List);
63 +
64 + res.removeWhere((e) => e['value'] == '0');
65 +
66 + return res
67 + .map(
68 + (e) => EVMChainTransactionModel.fromJson(e as Map<String, dynamic>, 'ETH'),
69 + )
70 + .toList();
71 + }
72 +
73 + return [];
74 + } catch (e) {
75 + return [];
76 + }
77 + }
78 +
79 + @override
80 + Future<List<EVMChainTransactionModel>> fetchInternalTransactions(String address) async {
81 + try {
82 + final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
83 + "chainid": "$chainId",
84 + "module": "account",
85 + "action": "txlistinternal",
86 + "address": address,
87 + "apikey": secrets.etherScanApiKey,
88 + }));
89 +
90 + final jsonResponse = json.decode(response.body) as Map<String, dynamic>;
91 +
92 + if (response.statusCode >= 200 && response.statusCode < 300 && jsonResponse['status'] != 0) {
93 + return (jsonResponse['result'] as List)
94 + .map((e) => EVMChainTransactionModel.fromJson(e as Map<String, dynamic>, 'ETH'))
95 + .toList();
96 + }
97 +
98 + return [];
99 + } catch (_) {
100 + return [];
101 + }
102 + }
103 +}
cw_base/lib/base_mnemonics_exception.dart new
+5
@@ -0,0 +1,5 @@
1 +class BaseMnemonicIsIncorrectException implements Exception {
2 + @override
3 + String toString() =>
4 + 'Base mnemonic has incorrect format. Mnemonic should contain 12 or 24 words separated by space.';
5 +}
cw_base/lib/base_transaction_history.dart new
+20
@@ -0,0 +1,20 @@
1 +import 'dart:core';
2 +
3 +import 'package:cw_evm/evm_chain_transaction_history.dart';
4 +import 'package:cw_evm/evm_chain_transaction_info.dart';
5 +import 'package:cw_base/base_transaction_info.dart';
6 +
7 +class BaseTransactionHistory extends EVMChainTransactionHistory {
8 + BaseTransactionHistory({
9 + required super.walletInfo,
10 + required super.password,
11 + required super.encryptionFileUtils,
12 + });
13 +
14 + @override
15 + String getTransactionHistoryFileName() => 'base_transactions.json';
16 +
17 + @override
18 + EVMChainTransactionInfo getTransactionInfo(Map<String, dynamic> val) =>
19 + BaseTransactionInfo.fromJson(val);
20 +}
cw_base/lib/base_transaction_info.dart new
+41
@@ -0,0 +1,41 @@
1 +import 'package:cw_core/transaction_direction.dart';
2 +import 'package:cw_evm/evm_chain_transaction_info.dart';
3 +
4 +class BaseTransactionInfo extends EVMChainTransactionInfo {
5 + BaseTransactionInfo({
6 + required super.id,
7 + required super.height,
8 + required super.ethAmount,
9 + required super.ethFee,
10 + required super.tokenSymbol,
11 + required super.direction,
12 + required super.isPending,
13 + required super.date,
14 + required super.confirmations,
15 + required super.to,
16 + required super.from,
17 + super.contractAddress,
18 + super.exponent,
19 + });
20 +
21 + factory BaseTransactionInfo.fromJson(Map<String, dynamic> data) {
22 + return BaseTransactionInfo(
23 + id: data['id'] as String,
24 + height: data['height'] as int,
25 + ethAmount: BigInt.parse(data['amount']),
26 + exponent: data['exponent'] as int,
27 + ethFee: BigInt.parse(data['fee']),
28 + direction: parseTransactionDirectionFromInt(data['direction'] as int),
29 + date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
30 + isPending: data['isPending'] as bool,
31 + confirmations: data['confirmations'] as int,
32 + tokenSymbol: data['tokenSymbol'] as String,
33 + to: data['to'],
34 + from: data['from'],
35 + contractAddress: data['contractAddress'],
36 + );
37 + }
38 +
39 + @override
40 + String get feeCurrency => 'ETH';
41 +}
cw_base/lib/base_wallet.dart new
+165
@@ -0,0 +1,165 @@
1 +import 'dart:convert';
2 +
3 +import 'package:cw_base/base_client.dart';
4 +import 'package:cw_base/base_transaction_history.dart';
5 +import 'package:cw_base/base_transaction_info.dart';
6 +import 'package:cw_base/default_base_erc20_tokens.dart';
7 +import 'package:cw_core/cake_hive.dart';
8 +import 'package:cw_core/crypto_currency.dart';
9 +import 'package:cw_core/encryption_file_utils.dart';
10 +import 'package:cw_core/erc20_token.dart';
11 +import 'package:cw_core/pathForWallet.dart';
12 +import 'package:cw_core/transaction_direction.dart';
13 +import 'package:cw_core/wallet_info.dart';
14 +import 'package:cw_core/wallet_keys_file.dart';
15 +import 'package:cw_evm/evm_chain_transaction_history.dart';
16 +import 'package:cw_evm/evm_chain_transaction_info.dart';
17 +import 'package:cw_evm/evm_chain_transaction_model.dart';
18 +import 'package:cw_evm/evm_chain_wallet.dart';
19 +import 'package:cw_evm/evm_erc20_balance.dart';
20 +
21 +class BaseWallet extends EVMChainWallet {
22 + BaseWallet({
23 + required super.walletInfo,
24 + required super.password,
25 + super.mnemonic,
26 + super.initialBalance,
27 + super.privateKey,
28 + required super.client,
29 + required super.encryptionFileUtils,
30 + super.passphrase,
31 + }) : super(nativeCurrency: CryptoCurrency.baseEth);
32 +
33 + @override
34 + Future<void> initErc20TokensBox() async {
35 + final boxName = "${walletInfo.name.replaceAll(" ", "_")}_ ${Erc20Token.baseBoxName}";
36 +
37 + evmChainErc20TokensBox = await CakeHive.openBox<Erc20Token>(boxName);
38 + }
39 +
40 + @override
41 + void addInitialTokens([bool isMigration = false]) {
42 + final initialErc20Tokens = DefaultBaseErc20Tokens().initialBaseErc20Tokens;
43 +
44 + for (final token in initialErc20Tokens) {
45 + if (evmChainErc20TokensBox.containsKey(token.contractAddress)) {
46 + final existingToken = evmChainErc20TokensBox.get(token.contractAddress);
47 + if (existingToken?.tag != token.tag) {
48 + evmChainErc20TokensBox.put(token.contractAddress, token);
49 + }
50 + } else {
51 + if (isMigration) token.enabled = false;
52 + evmChainErc20TokensBox.put(token.contractAddress, token);
53 + }
54 + }
55 + }
56 +
57 + @override
58 + List<String> get getDefaultTokenContractAddresses =>
59 + DefaultBaseErc20Tokens().initialBaseErc20Tokens.map((e) => e.contractAddress).toList();
60 +
61 + @override
62 + Future<bool> checkIfScanProviderIsEnabled() async {
63 + return (await sharedPrefs.future).getBool("use_basescan") ?? true;
64 + }
65 +
66 + @override
67 + String getTransactionHistoryFileName() => 'base_transactions.json';
68 +
69 + @override
70 + Erc20Token createNewErc20TokenObject(Erc20Token token, String? iconPath) {
71 + return Erc20Token(
72 + name: token.name,
73 + symbol: token.symbol,
74 + contractAddress: token.contractAddress,
75 + decimal: token.decimal,
76 + enabled: token.enabled,
77 + tag: token.tag ?? 'ETH',
78 + iconPath: iconPath,
79 + isPotentialScam: token.isPotentialScam,
80 + );
81 + }
82 +
83 + @override
84 + EVMChainTransactionInfo getTransactionInfo(
85 + EVMChainTransactionModel transactionModel, String address) {
86 + final model = BaseTransactionInfo(
87 + id: transactionModel.hash,
88 + height: transactionModel.blockNumber,
89 + ethAmount: transactionModel.amount,
90 + direction: transactionModel.from == address
91 + ? TransactionDirection.outgoing
92 + : TransactionDirection.incoming,
93 + isPending: false,
94 + date: transactionModel.date,
95 + confirmations: transactionModel.confirmations,
96 + ethFee: BigInt.from(transactionModel.gasUsed) * transactionModel.gasPrice,
97 + exponent: transactionModel.tokenDecimal ?? 18,
98 + tokenSymbol: transactionModel.tokenSymbol ?? "ETH",
99 + to: transactionModel.to,
100 + from: transactionModel.from,
101 + contractAddress: transactionModel.contractAddress,
102 + );
103 + return model;
104 + }
105 +
106 + @override
107 + EVMChainTransactionHistory setUpTransactionHistory(
108 + WalletInfo walletInfo, String password, EncryptionFileUtils encryptionFileUtils) {
109 + return BaseTransactionHistory(
110 + walletInfo: walletInfo,
111 + password: password,
112 + encryptionFileUtils: encryptionFileUtils,
113 + );
114 + }
115 +
116 + static Future<BaseWallet> open({
117 + required String name,
118 + required String password,
119 + required WalletInfo walletInfo,
120 + required EncryptionFileUtils encryptionFileUtils,
121 + }) async {
122 + final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type);
123 + final path = await pathForWallet(name: name, type: walletInfo.type);
124 +
125 + Map<String, dynamic>? data;
126 + try {
127 + final jsonSource = await encryptionFileUtils.read(path: path, password: password);
128 +
129 + data = json.decode(jsonSource) as Map<String, dynamic>;
130 + } catch (e) {
131 + if (!hasKeysFile) rethrow;
132 + }
133 +
134 + final balance = EVMChainERC20Balance.fromJSON(data?['balance'] as String?) ??
135 + EVMChainERC20Balance(BigInt.zero);
136 +
137 + final WalletKeysData keysData;
138 + // Migrate wallet from the old scheme to then new .keys file scheme
139 + if (!hasKeysFile) {
140 + final mnemonic = data!['mnemonic'] as String?;
141 + final privateKey = data['private_key'] as String?;
142 + final passphrase = data['passphrase'] as String?;
143 +
144 + keysData = WalletKeysData(mnemonic: mnemonic, privateKey: privateKey, passphrase: passphrase);
145 + } else {
146 + keysData = await WalletKeysFile.readKeysFile(
147 + name,
148 + walletInfo.type,
149 + password,
150 + encryptionFileUtils,
151 + );
152 + }
153 +
154 + return BaseWallet(
155 + walletInfo: walletInfo,
156 + password: password,
157 + mnemonic: keysData.mnemonic,
158 + privateKey: keysData.privateKey,
159 + passphrase: keysData.passphrase,
160 + initialBalance: balance,
161 + client: BaseClient(),
162 + encryptionFileUtils: encryptionFileUtils,
163 + );
164 + }
165 +}
cw_base/lib/base_wallet_service.dart new
+163
@@ -0,0 +1,163 @@
1 +import 'package:bip39/bip39.dart' as bip39;
2 +import 'package:cw_core/encryption_file_utils.dart';
3 +import 'package:cw_core/wallet_base.dart';
4 +import 'package:cw_core/wallet_info.dart';
5 +import 'package:cw_core/wallet_type.dart';
6 +import 'package:cw_evm/evm_chain_wallet_creation_credentials.dart';
7 +import 'package:cw_evm/evm_chain_wallet_service.dart';
8 +import 'package:cw_base/base_wallet.dart';
9 +import 'package:cw_base/base_client.dart';
10 +import 'package:cw_base/base_mnemonics_exception.dart';
11 +
12 +class BaseWalletService extends EVMChainWalletService<BaseWallet> {
13 + BaseWalletService(
14 + super.walletInfoSource,
15 + super.isDirect, {
16 + required this.client,
17 + });
18 +
19 + late BaseClient client;
20 +
21 + @override
22 + WalletType getType() => WalletType.base;
23 +
24 + @override
25 + Future<BaseWallet> create(EVMChainNewWalletCredentials credentials, {bool? isTestnet}) async {
26 + final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
27 +
28 + final mnemonic = credentials.mnemonic ?? bip39.generateMnemonic(strength: strength);
29 +
30 + final wallet = BaseWallet(
31 + walletInfo: credentials.walletInfo!,
32 + mnemonic: mnemonic,
33 + password: credentials.password!,
34 + passphrase: credentials.passphrase,
35 + client: client,
36 + encryptionFileUtils: encryptionFileUtilsFor(isDirect),
37 + );
38 +
39 + await wallet.init();
40 + wallet.addInitialTokens();
41 + await wallet.save();
42 + return wallet;
43 + }
44 +
45 + @override
46 + Future<BaseWallet> openWallet(String name, String password) async {
47 + final walletInfo =
48 + walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
49 +
50 + try {
51 + final wallet = await BaseWallet.open(
52 + name: name,
53 + password: password,
54 + walletInfo: walletInfo,
55 + encryptionFileUtils: encryptionFileUtilsFor(isDirect),
56 + );
57 +
58 + await wallet.init();
59 + wallet.addInitialTokens(true);
60 + await wallet.save();
61 + saveBackup(name);
62 + return wallet;
63 + } catch (_) {
64 + await restoreWalletFilesFromBackup(name);
65 +
66 + final wallet = await BaseWallet.open(
67 + name: name,
68 + password: password,
69 + walletInfo: walletInfo,
70 + encryptionFileUtils: encryptionFileUtilsFor(isDirect),
71 + );
72 +
73 + await wallet.init();
74 + wallet.addInitialTokens(true);
75 + await wallet.save();
76 + return wallet;
77 + }
78 + }
79 +
80 + @override
81 + Future<BaseWallet> restoreFromKeys(EVMChainRestoreWalletFromPrivateKey credentials,
82 + {bool? isTestnet}) async {
83 + final wallet = BaseWallet(
84 + password: credentials.password!,
85 + privateKey: credentials.privateKey,
86 + walletInfo: credentials.walletInfo!,
87 + client: client,
88 + encryptionFileUtils: encryptionFileUtilsFor(isDirect),
89 + );
90 +
91 + await wallet.init();
92 + wallet.addInitialTokens();
93 + await wallet.save();
94 + return wallet;
95 + }
96 +
97 + @override
98 + Future<BaseWallet> restoreFromHardwareWallet(
99 + EVMChainRestoreWalletFromHardware credentials) async {
100 + credentials.walletInfo!.derivationInfo = DerivationInfo(
101 + derivationType: DerivationType.bip39,
102 + derivationPath: "m/44'/60'/${credentials.hwAccountData.accountIndex}'/0/0");
103 + credentials.walletInfo!.hardwareWalletType = credentials.hardwareWalletType;
104 + credentials.walletInfo!.address = credentials.hwAccountData.address;
105 +
106 + final wallet = BaseWallet(
107 + walletInfo: credentials.walletInfo!,
108 + password: credentials.password!,
109 + client: client,
110 + encryptionFileUtils: encryptionFileUtilsFor(isDirect),
111 + );
112 +
113 + await wallet.init();
114 + wallet.addInitialTokens();
115 + await wallet.save();
116 +
117 + return wallet;
118 + }
119 +
120 + @override
121 + Future<BaseWallet> restoreFromSeed(EVMChainRestoreWalletFromSeedCredentials credentials,
122 + {bool? isTestnet}) async {
123 + if (!bip39.validateMnemonic(credentials.mnemonic)) {
124 + throw BaseMnemonicIsIncorrectException();
125 + }
126 +
127 + final wallet = BaseWallet(
128 + password: credentials.password!,
129 + mnemonic: credentials.mnemonic,
130 + walletInfo: credentials.walletInfo!,
131 + passphrase: credentials.passphrase,
132 + client: client,
133 + encryptionFileUtils: encryptionFileUtilsFor(isDirect),
134 + );
135 +
136 + await wallet.init();
137 + wallet.addInitialTokens();
138 + await wallet.save();
139 +
140 + return wallet;
141 + }
142 +
143 + @override
144 + Future<void> rename(String currentName, String password, String newName) async {
145 + final currentWalletInfo = walletInfoSource.values
146 + .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
147 + final currentWallet = await BaseWallet.open(
148 + password: password,
149 + name: currentName,
150 + walletInfo: currentWalletInfo,
151 + encryptionFileUtils: encryptionFileUtilsFor(isDirect),
152 + );
153 +
154 + await currentWallet.renameWalletFiles(newName);
155 + await saveBackup(newName);
156 +
157 + final newWalletInfo = currentWalletInfo;
158 + newWalletInfo.id = WalletBase.idFor(newName, getType());
159 + newWalletInfo.name = newName;
160 +
161 + await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
162 + }
163 +}
cw_base/lib/cw_base.dart new
+5
@@ -0,0 +1,5 @@
1 +/// A Calculator.
2 +class Calculator {
3 + /// Returns [value] plus 1.
4 + int addOne(int value) => value + 1;
5 +}
cw_base/lib/default_base_erc20_tokens.dart new
+67
@@ -0,0 +1,67 @@
1 +import 'package:cw_core/crypto_currency.dart';
2 +import 'package:cw_core/erc20_token.dart';
3 +
4 +class DefaultBaseErc20Tokens {
5 + final List<Erc20Token> _defaultTokens = [
6 + Erc20Token(
7 + name: "USD Coin",
8 + symbol: "USDC",
9 + contractAddress: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
10 + decimal: 6,
11 + enabled: true,
12 + ),
13 + Erc20Token(
14 + name: "USDe",
15 + symbol: "USDe",
16 + contractAddress: "0x5d3a1Ff2b6BAb83b63cd9AD0787074081a52ef34",
17 + decimal: 18,
18 + enabled: true,
19 + ),
20 + Erc20Token(
21 + name: "Dai",
22 + symbol: "DAI",
23 + contractAddress: "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
24 + decimal: 18,
25 + enabled: true,
26 + ),
27 + Erc20Token(
28 + name: "Bridged Tether USD",
29 + symbol: "USDT",
30 + contractAddress: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2",
31 + decimal: 6,
32 + enabled: true,
33 + ),
34 + Erc20Token(
35 + name: "Wrapped Ether",
36 + symbol: "WETH",
37 + contractAddress: "0x4200000000000000000000000000000000000006",
38 + decimal: 18,
39 + enabled: false,
40 + ),
41 + Erc20Token(
42 + name: "Wrapped BTC",
43 + symbol: "WBTC",
44 + contractAddress: "0x0555E30da8f98308EdB960aa94C0Db47230d2B9c",
45 + decimal: 8,
46 + enabled: false,
47 + ),
48 + Erc20Token(
49 + name: "SPX6900",
50 + symbol: "SPX",
51 + contractAddress: "0x50dA645f148798F68EF2d7dB7C1CB22A6819bb2C",
52 + decimal: 8,
53 + enabled: false,
54 + ),
55 + ];
56 +
57 + List<Erc20Token> get initialBaseErc20Tokens => _defaultTokens.map((token) {
58 + String? iconPath;
59 + try {
60 + iconPath = CryptoCurrency.all
61 + .firstWhere((element) => element.title.toUpperCase() == token.symbol.toUpperCase())
62 + .iconPath;
63 + } catch (_) {}
64 +
65 + return Erc20Token.copyWith(token, iconPath, 'BASE');
66 + }).toList();
67 +}
cw_base/pubspec.yaml new
+73
@@ -0,0 +1,73 @@
1 +name: cw_base
2 +description: "Base package for Cake Wallet"
3 +version: 0.0.1
4 +publish_to: none
5 +homepage: https://cakewallet.com
6 +
7 +environment:
8 + sdk: '>=3.0.6 <4.0.0'
9 + flutter: ">=1.17.0"
10 +
11 +dependencies:
12 + flutter:
13 + sdk: flutter
14 + cw_core:
15 + path: ../cw_core
16 + cw_ethereum:
17 + path: ../cw_ethereum
18 + cw_evm:
19 + path: ../cw_evm
20 + web3dart: ^2.7.1
21 + hive: ^2.2.3
22 + bip39: ^1.0.6
23 + collection: ^1.17.1
24 +
25 +dependency_overrides:
26 + web3dart:
27 + git:
28 + url: https://github.com/cake-tech/web3dart.git
29 + ref: cake
30 + watcher: ^1.1.0
31 +
32 +dev_dependencies:
33 + flutter_test:
34 + sdk: flutter
35 + flutter_lints: ^2.0.0
36 + build_runner: ^2.4.15
37 +
38 +# For information on the generic Dart part of this file, see the
39 +# following page: https://dart.dev/tools/pub/pubspec
40 +
41 +# The following section is specific to Flutter packages.
42 +flutter:
43 +
44 + # To add assets to your package, add an assets section, like this:
45 + # assets:
46 + # - images/a_dot_burr.jpeg
47 + # - images/a_dot_ham.jpeg
48 + #
49 + # For details regarding assets in packages, see
50 + # https://flutter.dev/to/asset-from-package
51 + #
52 + # An image asset can refer to one or more resolution-specific "variants", see
53 + # https://flutter.dev/to/resolution-aware-images
54 +
55 + # To add custom fonts to your package, add a fonts section here,
56 + # in this "flutter" section. Each entry in this list should have a
57 + # "family" key with the font family name, and a "fonts" key with a
58 + # list giving the asset and other descriptors for the font. For
59 + # example:
60 + # fonts:
61 + # - family: Schyler
62 + # fonts:
63 + # - asset: fonts/Schyler-Regular.ttf
64 + # - asset: fonts/Schyler-Italic.ttf
65 + # style: italic
66 + # - family: Trajan Pro
67 + # fonts:
68 + # - asset: fonts/TrajanPro.ttf
69 + # - asset: fonts/TrajanPro_Bold.ttf
70 + # weight: 700
71 + #
72 + # For details regarding fonts in packages, see
73 + # https://flutter.dev/to/font-from-package
cw_base/test/cw_base_test.dart new
+12
@@ -0,0 +1,12 @@
1 +import 'package:flutter_test/flutter_test.dart';
2 +
3 +import 'package:cw_base/cw_base.dart';
4 +
5 +void main() {
6 + test('adds one to input values', () {
7 + final calculator = Calculator();
8 + expect(calculator.addOne(2), 3);
9 + expect(calculator.addOne(-7), -6);
10 + expect(calculator.addOne(0), 1);
11 + });
12 +}
cw_bitcoin/lib/electrum_transaction_info.dart
+1
@@ -5,6 +5,7 @@ import 'package:cw_bitcoin/address_from_output.dart';
5 import 'package:cw_bitcoin/bitcoin_address_record.dart';
6 import 'package:cw_bitcoin/bitcoin_amount_format.dart';
7 import 'package:cw_bitcoin/bitcoin_unspent.dart';
8 +import 'package:cw_core/currency_for_wallet_type.dart';
9 import 'package:cw_core/transaction_direction.dart';
10 import 'package:cw_core/transaction_info.dart';
11 import 'package:cw_core/format_amount.dart';
cw_core/lib/crypto_currency.dart
+2
@@ -115,6 +115,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
115 CryptoCurrency.flip,
116 CryptoCurrency.ndeps,
117 CryptoCurrency.deps,
118 + CryptoCurrency.baseEth,
119 ];
120
121 static const havenCurrencies = [
@@ -241,6 +242,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
242 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);
243 static const kbtc = CryptoCurrency(title: 'KBTC', tag: 'ETH', fullName: 'Kraken Wrapped Bitcoin', raw: 102, name: 'kbtc', iconPath: 'assets/images/kbtc_icon.png', decimals: 8);
244 static const cbbtc = CryptoCurrency(title: 'CBBTC', tag: 'ETH', fullName: 'Coinbase Wrapped BTC', raw: 103, name: 'cbbtc', iconPath: 'assets/images/cbbtc_icon.png', decimals: 8);
245 + static const baseEth = CryptoCurrency(title: 'ETH', tag: 'BASE', fullName: 'Ethereum', raw: 104, name: 'baseth', iconPath: 'assets/images/eth_icon.png', decimals: 18);
246
247 static final Map<int, CryptoCurrency> _rawCurrencyMap =
248 [...all, ...havenCurrencies].fold<Map<int, CryptoCurrency>>(<int, CryptoCurrency>{}, (acc, item) {
cw_core/lib/currency_for_wallet_type.dart
+13 -37
@@ -1,21 +1,23 @@
1 import 'package:cw_core/crypto_currency.dart';
2 import 'package:cw_core/wallet_type.dart';
3
4 -CryptoCurrency currencyForWalletType(WalletType type, {bool? isTestnet}) {
4 +CryptoCurrency walletTypeToCryptoCurrency(WalletType type, {bool isTestnet = false}) {
5 switch (type) {
6 + case WalletType.monero:
7 + return CryptoCurrency.xmr;
8 case WalletType.bitcoin:
7 - if (isTestnet == true) {
9 + if (isTestnet) {
10 return CryptoCurrency.tbtc;
11 }
12 return CryptoCurrency.btc;
11 - case WalletType.monero:
12 - return CryptoCurrency.xmr;
13 case WalletType.litecoin:
14 return CryptoCurrency.ltc;
15 case WalletType.haven:
16 return CryptoCurrency.xhv;
17 case WalletType.ethereum:
18 return CryptoCurrency.eth;
19 + case WalletType.base:
20 + return CryptoCurrency.baseEth;
21 case WalletType.bitcoinCash:
22 return CryptoCurrency.bch;
23 case WalletType.nano:
@@ -38,41 +40,15 @@ CryptoCurrency currencyForWalletType(WalletType type, {bool? isTestnet}) {
40 return CryptoCurrency.doge;
41 case WalletType.none:
42 throw Exception(
41 - 'Unexpected wallet type: ${type.toString()} for CryptoCurrency currencyForWalletType');
43 + 'Unexpected wallet type: ${type.toString()} for CryptoCurrency walletTypeToCryptoCurrency');
44 }
45 }
46
45 -WalletType? walletTypeForCurrency(CryptoCurrency currency) {
46 - switch (currency) {
47 - case CryptoCurrency.btc:
48 - return WalletType.bitcoin;
49 - case CryptoCurrency.xmr:
50 - return WalletType.monero;
51 - case CryptoCurrency.ltc:
52 - return WalletType.litecoin;
53 - case CryptoCurrency.xhv:
54 - return WalletType.haven;
55 - case CryptoCurrency.eth:
56 - return WalletType.ethereum;
57 - case CryptoCurrency.bch:
58 - return WalletType.bitcoinCash;
59 - case CryptoCurrency.nano:
60 - return WalletType.nano;
61 - case CryptoCurrency.banano:
62 - return WalletType.banano;
63 - case CryptoCurrency.maticpoly:
64 - return WalletType.polygon;
65 - case CryptoCurrency.sol:
66 - return WalletType.solana;
67 - case CryptoCurrency.trx:
68 - return WalletType.tron;
69 - case CryptoCurrency.wow:
70 - return WalletType.wownero;
71 - case CryptoCurrency.zano:
72 - return WalletType.zano;
73 - case CryptoCurrency.dcr:
74 - return WalletType.decred;
75 - default:
76 - return null;
47 +extension CryptoCurrencyForWalletType on CryptoCurrency {
48 + String? get normalizedIconPath {
49 + if (this == CryptoCurrency.baseEth) {
50 + return 'assets/images/base_icon.png';
51 + }
52 + return iconPath;
53 }
54 }
cw_core/lib/erc20_token.dart
+1
@@ -70,6 +70,7 @@ class Erc20Token extends CryptoCurrency with HiveObjectMixin {
70 static const boxName = 'Erc20Tokens';
71 static const ethereumBoxName = 'EthereumErc20Tokens';
72 static const polygonBoxName = 'PolygonErc20Tokens';
73 + static const baseBoxName = 'BaseErc20Tokens';
74
75 @override
76 bool operator ==(other) =>
cw_core/lib/node.dart
+2
@@ -106,6 +106,7 @@ class Node extends HiveObject with Keyable {
106 case WalletType.banano:
107 case WalletType.ethereum:
108 case WalletType.polygon:
109 + case WalletType.base:
110 case WalletType.solana:
111 case WalletType.tron:
112 case WalletType.zano:
@@ -169,6 +170,7 @@ class Node extends HiveObject with Keyable {
170 case WalletType.bitcoinCash:
171 case WalletType.ethereum:
172 case WalletType.polygon:
173 + case WalletType.base:
174 case WalletType.solana:
175 case WalletType.tron:
176 case WalletType.dogecoin:
cw_core/lib/wallet_base.dart
+1 -1
@@ -24,7 +24,7 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
24
25 WalletType get type => walletInfo.type;
26
27 - CryptoCurrency get currency => currencyForWalletType(type, isTestnet: isTestnet);
27 + CryptoCurrency get currency => walletTypeToCryptoCurrency(type, isTestnet: isTestnet);
28
29 String get id => walletInfo.id;
30
cw_core/lib/wallet_type.dart
+16 -44
@@ -19,6 +19,7 @@ const walletTypes = [
19 WalletType.zano,
20 WalletType.decred,
21 WalletType.dogecoin,
22 + WalletType.base,
23 ];
24
25 @HiveType(typeId: WALLET_TYPE_TYPE_ID)
@@ -69,7 +70,10 @@ enum WalletType {
70 decred,
71
72 @HiveField(15)
72 - dogecoin
73 + dogecoin,
74 +
75 + @HiveField(16)
76 + base,
77 }
78
79 int serializeToInt(WalletType type) {
@@ -104,6 +108,8 @@ int serializeToInt(WalletType type) {
108 return 13;
109 case WalletType.dogecoin:
110 return 14;
111 + case WalletType.base:
112 + return 15;
113 case WalletType.none:
114 return -1;
115 }
@@ -141,9 +147,10 @@ WalletType deserializeFromInt(int raw) {
147 return WalletType.decred;
148 case 14:
149 return WalletType.dogecoin;
150 + case 15:
151 + return WalletType.base;
152 default:
145 - throw Exception(
146 - 'Unexpected token: $raw for WalletType deserializeFromInt');
153 + throw Exception('Unexpected token: $raw for WalletType deserializeFromInt');
154 }
155 }
156
@@ -179,6 +186,8 @@ String walletTypeToString(WalletType type) {
186 return 'Decred';
187 case WalletType.dogecoin:
188 return 'Dogecoin';
189 + case WalletType.base:
190 + return 'Base';
191 case WalletType.none:
192 return '';
193 }
@@ -216,52 +225,13 @@ String walletTypeToDisplayName(WalletType type) {
225 return 'Decred (DCR)';
226 case WalletType.dogecoin:
227 return 'Dogecoin (DOGE)';
228 + case WalletType.base:
229 + return 'Base (BASE)';
230 case WalletType.none:
231 return '';
232 }
233 }
234
224 -CryptoCurrency walletTypeToCryptoCurrency(WalletType type, {bool isTestnet = false}) {
225 - switch (type) {
226 - case WalletType.monero:
227 - return CryptoCurrency.xmr;
228 - case WalletType.bitcoin:
229 - if (isTestnet) {
230 - return CryptoCurrency.tbtc;
231 - }
232 - return CryptoCurrency.btc;
233 - case WalletType.litecoin:
234 - return CryptoCurrency.ltc;
235 - case WalletType.haven:
236 - return CryptoCurrency.xhv;
237 - case WalletType.ethereum:
238 - return CryptoCurrency.eth;
239 - case WalletType.bitcoinCash:
240 - return CryptoCurrency.bch;
241 - case WalletType.nano:
242 - return CryptoCurrency.nano;
243 - case WalletType.banano:
244 - return CryptoCurrency.banano;
245 - case WalletType.polygon:
246 - return CryptoCurrency.maticpoly;
247 - case WalletType.solana:
248 - return CryptoCurrency.sol;
249 - case WalletType.tron:
250 - return CryptoCurrency.trx;
251 - case WalletType.wownero:
252 - return CryptoCurrency.wow;
253 - case WalletType.zano:
254 - return CryptoCurrency.zano;
255 - case WalletType.decred:
256 - return CryptoCurrency.dcr;
257 - case WalletType.dogecoin:
258 - return CryptoCurrency.doge;
259 - case WalletType.none:
260 - throw Exception(
261 - 'Unexpected wallet type: ${type.toString()} for CryptoCurrency walletTypeToCryptoCurrency');
262 - }
263 -}
264 -
235 WalletType? cryptoCurrencyToWalletType(CryptoCurrency type) {
236 switch (type) {
237 case CryptoCurrency.xmr:
@@ -294,6 +264,8 @@ WalletType? cryptoCurrencyToWalletType(CryptoCurrency type) {
264 return WalletType.decred;
265 case CryptoCurrency.doge:
266 return WalletType.dogecoin;
267 + case CryptoCurrency.baseEth:
268 + return WalletType.base;
269 default:
270 return null;
271 }
cw_decred/lib/transaction_info.dart
+1
@@ -1,3 +1,4 @@
1 +import 'package:cw_core/currency_for_wallet_type.dart';
2 import 'package:cw_core/transaction_direction.dart';
3 import 'package:cw_core/transaction_info.dart';
4 import 'package:cw_core/format_amount.dart';
cw_evm/lib/evm_chain_client.dart
+12 -12
@@ -39,15 +39,14 @@ abstract class EVMChainClient {
39 Uri? rpcUri;
40 bool isModifiedNodeUri = false;
41
42 - if (node.uriRaw == 'eth.nownodes.io' || node.uriRaw == 'matic.nownodes.io') {
42 + if (node.uriRaw.contains('nownodes.io')) {
43 isModifiedNodeUri = true;
44 String nowNodeApiKey = secrets.nowNodesApiKey;
45
46 rpcUri = Uri.https(node.uriRaw, '/$nowNodeApiKey');
47 }
48
49 - _client =
50 - Web3Client(isModifiedNodeUri ? rpcUri!.toString() : node.uri.toString(), client);
49 + _client = Web3Client(isModifiedNodeUri ? rpcUri!.toString() : node.uri.toString(), client);
50
51 return true;
52 } catch (e) {
@@ -127,10 +126,11 @@ abstract class EVMChainClient {
126 final gasEstimate = await _client!.estimateGas(
127 sender: senderAddress,
128 to: EthereumAddress.fromHex(contractAddress),
130 - data: data ?? transfer.encodeCall([
131 - toAddress,
132 - value.getInWei,
133 - ]),
129 + data: data ??
130 + transfer.encodeCall([
131 + toAddress,
132 + value.getInWei,
133 + ]),
134 );
135
136 return gasEstimate.toInt();
@@ -171,10 +171,10 @@ abstract class EVMChainClient {
171 int? gasPrice,
172 }) async {
173 assert(currency == CryptoCurrency.eth ||
174 - currency == CryptoCurrency.maticpoly ||
174 + currency == CryptoCurrency.maticpoly || currency == CryptoCurrency.baseEth ||
175 contractAddress != null);
176
177 - bool isNativeToken = currency == CryptoCurrency.eth || currency == CryptoCurrency.maticpoly;
177 + bool isNativeToken = currency == CryptoCurrency.eth || currency == CryptoCurrency.maticpoly || currency == CryptoCurrency.baseEth;
178
179 final Transaction transaction = createTransaction(
180 from: privateKey.address,
@@ -233,7 +233,6 @@ abstract class EVMChainClient {
233 required String contractAddress,
234 int? gasPrice,
235 }) async {
236 -
236 final Transaction transaction = createTransaction(
237 from: privateKey.address,
238 to: EthereumAddress.fromHex(contractAddress),
@@ -264,7 +263,8 @@ abstract class EVMChainClient {
263 feeCurrency: feeCurrency,
264 sendTransaction: () => sendTransaction(signedTransaction),
265 exponent: exponent,
267 - isInfiniteApproval: amount.toRadixString(16) == 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
266 + isInfiniteApproval: amount.toRadixString(16) ==
267 + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
268 );
269 }
270
@@ -342,7 +342,7 @@ abstract class EVMChainClient {
342 final erc20 = ERC20(address: EthereumAddress.fromHex(contractAddress), client: _client!);
343 final balance = await erc20.balanceOf(userAddress);
344
345 - int exponent = (await erc20.decimals()).toInt();
345 + int exponent = (await erc20.decimals()).toInt();
346
347 return EVMChainERC20Balance(balance, exponent: exponent);
348 } on RangeError catch (_) {
cw_evm/lib/evm_chain_wallet.dart
+45 -17
@@ -171,25 +171,29 @@ abstract class EVMChainWalletBase
171 try {
172 // Check native balance
173 await _client.getBalance(_evmChainPrivateKey.address, throwOnError: true);
174 -
174 +
175 // Check USDC token balance
176 String usdcContractAddress;
177 - if (_client.chainId == 1) {
178 - // Ethereum mainnet
179 - usdcContractAddress = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48";
180 - } else if (_client.chainId == 137) {
181 - // Polygon mainnet
182 - usdcContractAddress = "0x2791bca1f2de4661ed88a30c99a7a9449aa84174";
183 - } else {
184 - // If we are not on Ethereum or Polygon, we skip ERC20 token check
185 - return true;
177 +
178 + switch (_client.chainId) {
179 + case 1:
180 + usdcContractAddress = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48";
181 + break;
182 + case 137:
183 + usdcContractAddress = "0x2791bca1f2de4661ed88a30c99a7a9449aa84174";
184 + break;
185 + case 8453:
186 + usdcContractAddress = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
187 + break;
188 + default:
189 + return true;
190 }
187 -
191 +
192 await _client.fetchERC20Balances(
193 _evmChainPrivateKey.address,
194 usdcContractAddress,
195 );
192 -
196 +
197 return true;
198 } catch (e) {
199 return false;
@@ -542,6 +546,32 @@ abstract class EVMChainWalletBase
546 maxFeePerGasForTransaction = gasFeesModel.maxFeePerGas;
547
548 if (output.sendAll && transactionCurrency is! Erc20Token) {
549 + if (_client.chainId == 8453) {
550 + // Add a safety buffer for Base chain to account for gas price fluctuations
551 + // Use 1% buffer or minimum 1000 wei (0.000001 ETH), whichever is higher
552 + final gasBufferPercent =
553 + estimatedFeesForTransaction * BigInt.from(101) ~/ BigInt.from(100);
554 + final gasBufferMin = estimatedFeesForTransaction + BigInt.from(1000);
555 + final gasBuffer = gasBufferPercent > gasBufferMin ? gasBufferPercent : gasBufferMin;
556 +
557 + totalAmount = (currencyBalance.balance - gasBuffer);
558 +
559 + // Re-estimate gas with the correct amount to get more accurate gas estimation
560 + final refinedGasFeesModel = await calculateActualEstimatedFeeForCreateTransaction(
561 + amount: totalAmount,
562 + receivingAddressHex: toAddress,
563 + priority: _credentials.priority!,
564 + contractAddress: contractAddress,
565 + );
566 +
567 + // Use the higher of the two gas estimations to be safe
568 + final refinedGasFee = BigInt.from(refinedGasFeesModel.estimatedGasFee);
569 + estimatedFeesForTransaction = refinedGasFee > gasBuffer ? refinedGasFee : gasBuffer;
570 + estimatedGasUnitsForTransaction = refinedGasFeesModel.estimatedGasUnits;
571 + maxFeePerGasForTransaction = refinedGasFeesModel.maxFeePerGas;
572 + }
573 +
574 + // Final amount calculation with the higher gas estimation
575 totalAmount = (currencyBalance.balance - estimatedFeesForTransaction);
576 }
577
@@ -569,11 +599,7 @@ abstract class EVMChainWalletBase
599 gasFee: estimatedFeesForTransaction,
600 priority: _credentials.priority!,
601 currency: transactionCurrency,
572 - feeCurrency: switch (_client.chainId) {
573 - 1 => "ETH",
574 - 137 => "POL",
575 - _ => ""
576 - },
602 + feeCurrency: switch (_client.chainId) { 1 => "ETH", 137 => "POL", 8453 => "ETH", _ => "ETH" },
603 maxFeePerGas: maxFeePerGasForTransaction,
604 exponent: exponent,
605 contractAddress:
@@ -890,6 +916,8 @@ abstract class EVMChainWalletBase
916 /// EtherScan for Ethereum.
917 ///
918 /// PolygonScan for Polygon.
919 + ///
920 + /// BaseScan for Base.
921 void updateScanProviderUsageState(bool isEnabled) {
922 if (isEnabled) {
923 _updateTransactions();
integration_test/components/common_test_flows.dart
+6 -1
@@ -3,6 +3,7 @@ import 'dart:io';
3 import 'package:cake_wallet/entities/seed_type.dart';
4 import 'package:cake_wallet/reactions/wallet_utils.dart';
5 import 'package:cake_wallet/wallet_types.g.dart';
6 +import 'package:cw_core/currency_for_wallet_type.dart';
7 import 'package:cw_core/wallet_type.dart';
8 import 'package:flutter/material.dart';
9 import 'package:flutter_test/flutter_test.dart';
@@ -109,7 +110,7 @@ class CommonTestFlows {
110
111 void confirmAllAvailableWalletTypeIconsDisplayCorrectly() {
112 for (var walletType in availableWalletTypes) {
112 - final imageUrl = walletTypeToCryptoCurrency(walletType).iconPath;
113 + final imageUrl = walletTypeToCryptoCurrency(walletType).normalizedIconPath;
114
115 final walletIconFinder = find.image(
116 Image.asset(
@@ -353,6 +354,8 @@ class CommonTestFlows {
354 return secrets.polygonTestWalletSeeds;
355 case WalletType.solana:
356 return secrets.solanaTestWalletSeeds;
357 + case WalletType.base:
358 + return secrets.baseTestWalletSeeds;
359 case WalletType.tron:
360 return secrets.tronTestWalletSeeds;
361 case WalletType.nano:
@@ -387,6 +390,8 @@ class CommonTestFlows {
390 return secrets.bitcoinCashTestWalletReceiveAddress;
391 case WalletType.polygon:
392 return secrets.polygonTestWalletReceiveAddress;
393 + case WalletType.base:
394 + return secrets.baseTestWalletReceiveAddress;
395 case WalletType.solana:
396 return secrets.solanaTestWalletReceiveAddress;
397 case WalletType.tron:
ios/Runner/InfoBase.plist
+20
@@ -302,6 +302,26 @@
302 <string>dogecoin-wallet</string>
303 </array>
304 </dict>
305 + <dict>
306 + <key>CFBundleTypeRole</key>
307 + <string>Editor</string>
308 + <key>CFBundleURLName</key>
309 + <string>base</string>
310 + <key>CFBundleURLSchemes</key>
311 + <array>
312 + <string>base</string>
313 + </array>
314 + </dict>
315 + <dict>
316 + <key>CFBundleTypeRole</key>
317 + <string>Viewer</string>
318 + <key>CFBundleURLName</key>
319 + <string>base-wallet</string>
320 + <key>CFBundleURLSchemes</key>
321 + <array>
322 + <string>base-wallet</string>
323 + </array>
324 + </dict>
325 </array>
326 <key>CFBundleVersion</key>
327 <string>$(CURRENT_PROJECT_VERSION)</string>
lib/base/cw_base.dart new
+238
@@ -0,0 +1,238 @@
1 +part of 'base.dart';
2 +
3 +class CWBase extends Base {
4 + @override
5 + List<String> getBaseWordList(String language) => EVMChainMnemonics.englishWordlist;
6 +
7 + WalletService createBaseWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) =>
8 + BaseWalletService(walletInfoSource, isDirect, client: BaseClient());
9 +
10 + @override
11 + WalletCredentials createBaseNewWalletCredentials({
12 + required String name,
13 + String? mnemonic,
14 + WalletInfo? walletInfo,
15 + String? password,
16 + String? passphrase,
17 + }) =>
18 + EVMChainNewWalletCredentials(
19 + name: name,
20 + walletInfo: walletInfo,
21 + password: password,
22 + mnemonic: mnemonic,
23 + passphrase: passphrase,
24 + );
25 +
26 + @override
27 + WalletCredentials createBaseRestoreWalletFromSeedCredentials({
28 + required String name,
29 + required String mnemonic,
30 + required String password,
31 + String? passphrase,
32 + }) =>
33 + EVMChainRestoreWalletFromSeedCredentials(
34 + name: name,
35 + password: password,
36 + mnemonic: mnemonic,
37 + passphrase: passphrase,
38 + );
39 +
40 + @override
41 + WalletCredentials createBaseRestoreWalletFromPrivateKey({
42 + required String name,
43 + required String privateKey,
44 + required String password,
45 + }) =>
46 + EVMChainRestoreWalletFromPrivateKey(name: name, password: password, privateKey: privateKey);
47 +
48 + @override
49 + WalletCredentials createBaseHardwareWalletCredentials({
50 + required String name,
51 + required HardwareAccountData hwAccountData,
52 + WalletInfo? walletInfo,
53 + }) =>
54 + EVMChainRestoreWalletFromHardware(
55 + name: name,
56 + hwAccountData: hwAccountData,
57 + walletInfo: walletInfo,
58 + );
59 +
60 + @override
61 + String getAddress(WalletBase wallet) => (wallet as BaseWallet).walletAddresses.address;
62 +
63 + @override
64 + String getPrivateKey(WalletBase wallet) {
65 + final privateKeyHolder = (wallet as BaseWallet).evmChainPrivateKey;
66 + if (privateKeyHolder is EthPrivateKey) return bytesToHex(privateKeyHolder.privateKey);
67 + return "";
68 + }
69 +
70 + @override
71 + String getPublicKey(WalletBase wallet) {
72 + final privateKeyInUnitInt = (wallet as BaseWallet).evmChainPrivateKey;
73 + return privateKeyInUnitInt.address.hex;
74 + }
75 +
76 + @override
77 + TransactionPriority getDefaultTransactionPriority() => EVMChainTransactionPriority.medium;
78 +
79 + @override
80 + TransactionPriority getBaseTransactionPrioritySlow() => EVMChainTransactionPriority.slow;
81 +
82 + @override
83 + List<TransactionPriority> getTransactionPriorities() => EVMChainTransactionPriority.all;
84 +
85 + @override
86 + TransactionPriority deserializeBaseTransactionPriority(int raw) =>
87 + EVMChainTransactionPriority.deserialize(raw: raw);
88 +
89 + Object createBaseTransactionCredentials(
90 + List<Output> outputs, {
91 + required TransactionPriority priority,
92 + required CryptoCurrency currency,
93 + int? feeRate,
94 + }) =>
95 + EVMChainTransactionCredentials(
96 + outputs
97 + .map(
98 + (out) => OutputInfo(
99 + fiatAmount: out.fiatAmount,
100 + cryptoAmount: out.cryptoAmount,
101 + address: out.address,
102 + note: out.note,
103 + sendAll: out.sendAll,
104 + extractedAddress: out.extractedAddress,
105 + isParsedAddress: out.isParsedAddress,
106 + formattedCryptoAmount: out.formattedCryptoAmount,
107 + ),
108 + )
109 + .toList(),
110 + priority: priority as EVMChainTransactionPriority,
111 + currency: currency,
112 + feeRate: feeRate,
113 + );
114 +
115 + Object createBaseTransactionCredentialsRaw(
116 + List<OutputInfo> outputs, {
117 + TransactionPriority? priority,
118 + required CryptoCurrency currency,
119 + required int feeRate,
120 + }) =>
121 + EVMChainTransactionCredentials(
122 + outputs,
123 + priority: priority as EVMChainTransactionPriority?,
124 + currency: currency,
125 + feeRate: feeRate,
126 + );
127 +
128 + @override
129 + int formatterBaseParseAmount(String amount) => EVMChainFormatter.parseEVMChainAmount(amount);
130 +
131 + @override
132 + double formatterBaseAmountToDouble({
133 + TransactionInfo? transaction,
134 + BigInt? amount,
135 + int exponent = 18,
136 + }) {
137 + assert(transaction != null || amount != null);
138 +
139 + if (transaction != null) {
140 + transaction as EVMChainTransactionInfo;
141 + return transaction.ethAmount / BigInt.from(10).pow(transaction.exponent);
142 + } else {
143 + return (amount!) / BigInt.from(10).pow(exponent);
144 + }
145 + }
146 +
147 + @override
148 + List<Erc20Token> getERC20Currencies(WalletBase wallet) => (wallet as BaseWallet).erc20Currencies;
149 +
150 + @override
151 + Future<void> addErc20Token(WalletBase wallet, CryptoCurrency token) =>
152 + (wallet as BaseWallet).addErc20Token(token as Erc20Token);
153 +
154 + @override
155 + Future<void> deleteErc20Token(WalletBase wallet, CryptoCurrency token) =>
156 + (wallet as BaseWallet).deleteErc20Token(token as Erc20Token);
157 +
158 + @override
159 + Future<void> removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token) =>
160 + (wallet as BaseWallet).removeTokenTransactionsInHistory(token as Erc20Token);
161 +
162 + @override
163 + Future<Erc20Token?> getErc20Token(WalletBase wallet, String contractAddress) =>
164 + (wallet as BaseWallet).getErc20Token(contractAddress, 'base');
165 +
166 + @override
167 + CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction) {
168 + transaction as EVMChainTransactionInfo;
169 + if (transaction.tokenSymbol == CryptoCurrency.baseEth.title ||
170 + transaction.tokenSymbol == "BASE") {
171 + return CryptoCurrency.baseEth;
172 + }
173 +
174 + wallet as BaseWallet;
175 +
176 + return wallet.erc20Currencies.firstWhere(
177 + (element) =>
178 + transaction.contractAddress?.toLowerCase() == element.contractAddress?.toLowerCase(),
179 + );
180 + }
181 +
182 + @override
183 + void updateBaseScanUsageState(WalletBase wallet, bool isEnabled) =>
184 + (wallet as BaseWallet).updateScanProviderUsageState(isEnabled);
185 +
186 + @override
187 + Web3Client? getWeb3Client(WalletBase wallet) => (wallet as BaseWallet).getWeb3Client();
188 +
189 + @override
190 + String getTokenAddress(CryptoCurrency asset) => (asset as Erc20Token).contractAddress;
191 +
192 + @override
193 + Future<PendingTransaction> createTokenApproval(
194 + WalletBase wallet,
195 + BigInt amount,
196 + String spender,
197 + CryptoCurrency token,
198 + TransactionPriority priority,
199 + ) =>
200 + (wallet as EVMChainWallet).createApprovalTransaction(
201 + amount,
202 + spender,
203 + token,
204 + priority as EVMChainTransactionPriority,
205 + "BASE",
206 + );
207 +
208 + @override
209 + void setHardwareWalletService(WalletBase wallet, HardwareWalletService service) {
210 + if (service is EVMChainLedgerService) {
211 + ((wallet as EVMChainWallet).evmChainPrivateKey as EvmLedgerCredentials).setLedgerConnection(
212 + service.ledgerConnection, wallet.walletInfo.derivationInfo?.derivationPath);
213 + } else if (service is EVMChainBitboxService) {
214 + ((wallet as EVMChainWallet).evmChainPrivateKey as EvmBitboxCredentials)
215 + .setBitbox(service.manager, wallet.walletInfo.derivationInfo?.derivationPath);
216 + }
217 + }
218 +
219 + @override
220 + HardwareWalletService getLedgerHardwareWalletService(ledger.LedgerConnection connection) =>
221 + EVMChainLedgerService(connection);
222 +
223 + @override
224 + HardwareWalletService getBitboxHardwareWalletService(bitbox.BitboxManager manager) =>
225 + EVMChainBitboxService(manager, chainId: 8453);
226 +
227 + @override
228 + List<String> getDefaultTokenContractAddresses() =>
229 + DefaultBaseErc20Tokens().initialBaseErc20Tokens.map((e) => e.contractAddress).toList();
230 +
231 + @override
232 + bool isTokenAlreadyAdded(WalletBase wallet, String contractAddress) {
233 + final baseWallet = wallet as BaseWallet;
234 + return baseWallet.erc20Currencies.any(
235 + (element) => element.contractAddress.toLowerCase() == contractAddress.toLowerCase(),
236 + );
237 + }
238 +}
lib/buy/dfx/dfx_buy_provider.dart
+1
@@ -135,6 +135,7 @@ class DFXBuyProvider extends BuyProvider {
135 switch (wallet.type) {
136 case WalletType.ethereum:
137 case WalletType.polygon:
138 + case WalletType.base:
139 case WalletType.solana:
140 case WalletType.tron:
141 return wallet.signMessage(message);
lib/buy/kryptonim/kryptonim.dart
+2
@@ -218,6 +218,7 @@ class KryptonimBuyProvider extends BuyProvider {
218 String _normalizeBlockChain(CryptoCurrency cur) {
219 String? blockchain = switch (cur.tag) {
220 'ETH' => 'Ethereum',
221 + 'BASE' => 'Base',
222 'POL' => 'Polygon',
223 'AVAXC' => 'Avalanche',
224 'SOL' => 'Solana',
@@ -229,6 +230,7 @@ class KryptonimBuyProvider extends BuyProvider {
230 CryptoCurrency.btc => 'Bitcoin',
231 CryptoCurrency.ltc => 'Litecoin',
232 CryptoCurrency.eth => 'Ethereum',
233 + CryptoCurrency.baseEth => 'Base',
234 CryptoCurrency.maticpoly => 'Matic',
235 _ => null,
236 };
lib/buy/moonpay/moonpay_provider.dart
+1
@@ -17,6 +17,7 @@ import 'package:cake_wallet/palette.dart';
17 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
18 import 'package:cake_wallet/store/app_store.dart';
19 import 'package:cake_wallet/themes/core/material_base_theme.dart';
20 +import 'package:cw_core/currency_for_wallet_type.dart';
21 import 'package:cw_core/utils/proxy_wrapper.dart';
22 import 'package:cw_core/crypto_currency.dart';
23 import 'package:cw_core/wallet_base.dart';
lib/buy/robinhood/robinhood_buy_provider.dart
+1
@@ -105,6 +105,7 @@ class RobinhoodBuyProvider extends BuyProvider {
105 switch (wallet.type) {
106 case WalletType.ethereum:
107 case WalletType.polygon:
108 + case WalletType.base:
109 case WalletType.solana:
110 case WalletType.tron:
111 case WalletType.dogecoin:
lib/buy/wyre/wyre_buy_provider.dart
+1
@@ -4,6 +4,7 @@ import 'package:cake_wallet/order/order_source_description.dart';
4 import 'package:cake_wallet/buy/pairs_utils.dart';
5 import 'package:cake_wallet/entities/fiat_currency.dart';
6 import 'package:cw_core/crypto_currency.dart';
7 +import 'package:cw_core/currency_for_wallet_type.dart';
8 import 'package:cw_core/utils/proxy_wrapper.dart';
9 import 'package:cake_wallet/buy/buy_amount.dart';
10 import 'package:cake_wallet/buy/buy_provider.dart';
lib/core/payment_uris.dart
+15
@@ -86,6 +86,21 @@ class EthereumURI extends PaymentURI {
86 }
87 }
88
89 +class BaseURI extends PaymentURI {
90 + BaseURI({required super.amount, required super.address});
91 +
92 + @override
93 + String toString() {
94 + var base = 'base:$address';
95 +
96 + if (amount.isNotEmpty) {
97 + base += '?amount=${amount.replaceAll(',', '.')}';
98 + }
99 +
100 + return base;
101 + }
102 +}
103 +
104 class BitcoinCashURI extends PaymentURI {
105 BitcoinCashURI({required super.amount, required super.address});
106
lib/core/seed_validator.dart
+3
@@ -1,3 +1,4 @@
1 +import 'package:cake_wallet/base/base.dart';
2 import 'package:cake_wallet/bitcoin/bitcoin.dart';
3 import 'package:cake_wallet/core/validator.dart';
4 import 'package:cake_wallet/entities/mnemonic_item.dart';
@@ -52,6 +53,8 @@ class SeedValidator extends Validator<MnemonicItem> {
53 return zano!.getWordList(language);
54 case WalletType.decred:
55 return decred!.getDecredWordList();
56 + case WalletType.base:
57 + return base!.getBaseWordList(language);
58 case WalletType.none:
59 case WalletType.haven:
60 return [];
lib/core/wallet_creation_service.dart
+1
@@ -80,6 +80,7 @@ class WalletCreationService {
80 case WalletType.bitcoinCash:
81 case WalletType.ethereum:
82 case WalletType.polygon:
83 + case WalletType.base:
84 case WalletType.solana:
85 case WalletType.tron:
86 case WalletType.dogecoin:
lib/di.dart
+3
@@ -5,6 +5,7 @@ import 'package:cake_wallet/anonpay/anonpay_api.dart';
5 import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
6 import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
7 import 'package:cake_wallet/anypay/anypay_api.dart';
8 +import 'package:cake_wallet/base/base.dart';
9 import 'package:cake_wallet/bitcoin/bitcoin.dart';
10 import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
11 import 'package:cake_wallet/buy/dfx/dfx_buy_provider.dart';
@@ -1241,6 +1242,8 @@ Future<void> setup({
1242 return zano!.createZanoWalletService(_walletInfoSource);
1243 case WalletType.decred:
1244 return decred!.createDecredWalletService(_walletInfoSource, _unspentCoinsInfoSource);
1245 + case WalletType.base:
1246 + return base!.createBaseWalletService(_walletInfoSource, SettingsStoreBase.walletPasswordDirectInput);
1247 case WalletType.haven:
1248 return HavenWalletService(_walletInfoSource);
1249 case WalletType.none:
lib/entities/default_settings_migration.dart
+21
@@ -47,6 +47,7 @@ const zanoDefaultNodeUri = '37.27.100.59:10500';
47 const moneroWorldNodeUri = '.moneroworld.com';
48 const decredDefaultUri = "default-spv-nodes";
49 const dogecoinDefaultNodeUri = 'dogecoin.stackwallet.com:50022';
50 +const baseDefaultNodeUri = 'base.nownodes.io';
51
52 Future<void> defaultSettingsMigration(
53 {required int version,
@@ -534,6 +535,15 @@ Future<void> defaultSettingsMigration(
535 currentNodePreferenceKey: PreferencesKey.currentDogecoinNodeIdKey,
536 );
537 break;
538 + case 52:
539 + await addWalletNodeList(nodes: nodes, type: WalletType.base);
540 + await _changeDefaultNode(
541 + nodes: nodes,
542 + sharedPreferences: sharedPreferences,
543 + type: WalletType.base,
544 + currentNodePreferenceKey: PreferencesKey.currentBaseNodeIdKey,
545 + );
546 + break;
547 default:
548 break;
549 }
@@ -642,6 +652,8 @@ String _getDefaultNodeUri(WalletType type) {
652 return decredDefaultUri;
653 case WalletType.dogecoin:
654 return dogecoinDefaultNodeUri;
655 + case WalletType.base:
656 + return baseDefaultNodeUri;
657 case WalletType.banano:
658 case WalletType.none:
659 return '';
@@ -1041,6 +1053,7 @@ Future<void> checkCurrentNodes(
1053 final currentHavenNodeId = sharedPreferences.getInt(PreferencesKey.currentHavenNodeIdKey);
1054 final currentEthereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
1055 final currentPolygonNodeId = sharedPreferences.getInt(PreferencesKey.currentPolygonNodeIdKey);
1056 + final currentBaseNodeId = sharedPreferences.getInt(PreferencesKey.currentBaseNodeIdKey);
1057 final currentNanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
1058 final currentNanoPowNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoPowNodeIdKey);
1059 final currentDecredNodeId = sharedPreferences.getInt(PreferencesKey.currentDecredNodeIdKey);
@@ -1064,6 +1077,8 @@ Future<void> checkCurrentNodes(
1077 nodeSource.values.firstWhereOrNull((node) => node.key == currentEthereumNodeId);
1078 final currentPolygonNodeServer =
1079 nodeSource.values.firstWhereOrNull((node) => node.key == currentPolygonNodeId);
1080 + final currentBaseNodeServer =
1081 + nodeSource.values.firstWhereOrNull((node) => node.key == currentBaseNodeId);
1082 final currentNanoNodeServer =
1083 nodeSource.values.firstWhereOrNull((node) => node.key == currentNanoNodeId);
1084 final currentDecredNodeServer =
@@ -1155,6 +1170,12 @@ Future<void> checkCurrentNodes(
1170 await sharedPreferences.setInt(PreferencesKey.currentPolygonNodeIdKey, node.key as int);
1171 }
1172
1173 + if (currentBaseNodeServer == null) {
1174 + final node = Node(uri: baseDefaultNodeUri, type: WalletType.base);
1175 + await nodeSource.add(node);
1176 + await sharedPreferences.setInt(PreferencesKey.currentBaseNodeIdKey, node.key as int);
1177 + }
1178 +
1179 if (currentSolanaNodeServer == null) {
1180 final node = Node(uri: solanaDefaultNodeUri, type: WalletType.solana);
1181 await nodeSource.add(node);
lib/entities/ens_record.dart
+6
@@ -1,3 +1,4 @@
1 +import 'package:cake_wallet/base/base.dart';
2 import 'package:cake_wallet/ethereum/ethereum.dart';
3 import 'package:cake_wallet/polygon/polygon.dart';
4 import 'package:cw_core/utils/proxy_wrapper.dart';
@@ -21,6 +22,10 @@ class EnsRecord {
22 _client = polygon!.getWeb3Client(wallet);
23 }
24
25 + if (wallet != null && wallet.type == WalletType.base) {
26 + _client = base!.getWeb3Client(wallet);
27 + }
28 +
29 if (_client == null) {
30 late final client = ProxyWrapper().getHttpIOClient();
31
@@ -42,6 +47,7 @@ class EnsRecord {
47 return await ens.withName(name).getCoinAddress(CoinType.XHV);
48 case WalletType.ethereum:
49 case WalletType.polygon:
50 + case WalletType.base:
51 default:
52 return (await ens.withName(name).getAddress()).hex;
53 }
lib/entities/evm_transaction_error_fees_handler.dart
+82 -34
@@ -32,6 +32,50 @@ class EVMTransactionErrorFeesHandler {
32 String errorMessage,
33 double assetPriceUsd,
34 ) {
35 + // Pattern: "insufficient funds for gas * price + value: have 4728796358953246 want 4728796842182575"
36 + RegExp insufficientFundsRegExp = RegExp(r'have (\d+) want (\d+)');
37 + Match? insufficientFundsMatch = insufficientFundsRegExp.firstMatch(errorMessage);
38 +
39 + if (insufficientFundsMatch != null) {
40 + try {
41 + // Extract the numerical strings from the new format
42 + String balanceStr = insufficientFundsMatch.group(1)!;
43 + String requiredStr = insufficientFundsMatch.group(2)!;
44 +
45 + // Parse the numerical strings to BigInt
46 + BigInt balanceWei = BigInt.parse(balanceStr);
47 + BigInt requiredWei = BigInt.parse(requiredStr);
48 +
49 + // Calculate overshot (how much more is needed)
50 + BigInt overshotWei = requiredWei - balanceWei;
51 +
52 + // The transaction cost is the required amount
53 + BigInt txCostWei = requiredWei;
54 +
55 + // Convert wei to ETH (1 ETH = 1e18 wei)
56 + double balanceEth = balanceWei.toDouble() / 1e18;
57 + double txCostEth = txCostWei.toDouble() / 1e18;
58 + double overshotEth = overshotWei.toDouble() / 1e18;
59 +
60 + // Calculate the USD values
61 + double balanceUsd = balanceEth * assetPriceUsd;
62 + double txCostUsd = txCostEth * assetPriceUsd;
63 + double overshotUsd = overshotEth * assetPriceUsd;
64 +
65 + return EVMTransactionErrorFeesHandler(
66 + balanceWei: balanceWei.toString(),
67 + balanceEth: balanceEth.toString().safeSubString(0, 12),
68 + balanceUsd: balanceUsd.toString().safeSubString(0, 4),
69 + txCostWei: txCostWei.toString(),
70 + txCostEth: txCostEth.toString().safeSubString(0, 12),
71 + txCostUsd: txCostUsd.toString().safeSubString(0, 4),
72 + overshotWei: overshotWei.toString(),
73 + overshotEth: overshotEth.toString().safeSubString(0, 12),
74 + overshotUsd: overshotUsd.toString().safeSubString(0, 4),
75 + );
76 + } catch (e) {}
77 + }
78 +
79 // Define Regular Expressions to extract the numerical values
80 RegExp balanceRegExp = RegExp(r'balance (\d+)');
81 RegExp txCostRegExp = RegExp(r'tx cost (\d+)');
@@ -44,40 +88,44 @@ class EVMTransactionErrorFeesHandler {
88
89 // Check if all required values are found
90 if (balanceMatch != null && txCostMatch != null && overshotMatch != null) {
47 - // Extract the numerical strings
48 - String balanceStr = balanceMatch.group(1)!;
49 - String txCostStr = txCostMatch.group(1)!;
50 - String overshotStr = overshotMatch.group(1)!;
51 -
52 - // Parse the numerical strings to BigInt
53 - BigInt balanceWei = BigInt.parse(balanceStr);
54 - BigInt txCostWei = BigInt.parse(txCostStr);
55 - BigInt overshotWei = BigInt.parse(overshotStr);
56 -
57 - // Convert wei to ETH (1 ETH = 1e18 wei)
58 - double balanceEth = balanceWei.toDouble() / 1e18;
59 - double txCostEth = txCostWei.toDouble() / 1e18;
60 - double overshotEth = overshotWei.toDouble() / 1e18;
61 -
62 - // Calculate the USD values
63 - double balanceUsd = balanceEth * assetPriceUsd;
64 - double txCostUsd = txCostEth * assetPriceUsd;
65 - double overshotUsd = overshotEth * assetPriceUsd;
66 -
67 - return EVMTransactionErrorFeesHandler(
68 - balanceWei: balanceWei.toString(),
69 - balanceEth: balanceEth.toString().safeSubString(0, 12),
70 - balanceUsd: balanceUsd.toString().safeSubString(0, 4),
71 - txCostWei: txCostWei.toString(),
72 - txCostEth: txCostEth.toString().safeSubString(0, 12),
73 - txCostUsd: txCostUsd.toString().safeSubString(0, 4),
74 - overshotWei: overshotWei.toString(),
75 - overshotEth: overshotEth.toString().safeSubString(0, 12),
76 - overshotUsd: overshotUsd.toString().safeSubString(0, 4),
77 - );
78 - } else {
79 - // If any value is missing, return an error message
80 - return EVMTransactionErrorFeesHandler(error: 'Could not parse the error message.');
91 + try {
92 + // Extract the numerical strings
93 + String balanceStr = balanceMatch.group(1)!;
94 + String txCostStr = txCostMatch.group(1)!;
95 + String overshotStr = overshotMatch.group(1)!;
96 +
97 + // Parse the numerical strings to BigInt
98 + BigInt balanceWei = BigInt.parse(balanceStr);
99 + BigInt txCostWei = BigInt.parse(txCostStr);
100 + BigInt overshotWei = BigInt.parse(overshotStr);
101 +
102 + // Convert wei to ETH (1 ETH = 1e18 wei)
103 + double balanceEth = balanceWei.toDouble() / 1e18;
104 + double txCostEth = txCostWei.toDouble() / 1e18;
105 + double overshotEth = overshotWei.toDouble() / 1e18;
106 +
107 + // Calculate the USD values
108 + double balanceUsd = balanceEth * assetPriceUsd;
109 + double txCostUsd = txCostEth * assetPriceUsd;
110 + double overshotUsd = overshotEth * assetPriceUsd;
111 +
112 + return EVMTransactionErrorFeesHandler(
113 + balanceWei: balanceWei.toString(),
114 + balanceEth: balanceEth.toString().safeSubString(0, 12),
115 + balanceUsd: balanceUsd.toString().safeSubString(0, 4),
116 + txCostWei: txCostWei.toString(),
117 + txCostEth: txCostEth.toString().safeSubString(0, 12),
118 + txCostUsd: txCostUsd.toString().safeSubString(0, 4),
119 + overshotWei: overshotWei.toString(),
120 + overshotEth: overshotEth.toString().safeSubString(0, 12),
121 + overshotUsd: overshotUsd.toString().safeSubString(0, 4),
122 + );
123 + } catch (e) {
124 + // If parsing fails, continue to error case
125 + }
126 }
127 +
128 + // If both parsing attempts fail, return an error message
129 + return EVMTransactionErrorFeesHandler(error: 'Could not parse the error message.');
130 }
131 }
lib/entities/node_list.dart
+6 -1
@@ -49,6 +49,9 @@ Future<List<Node>> loadDefaultNodes(WalletType type) async {
49 case WalletType.dogecoin:
50 path = 'assets/dogecoin_electrum_server_list.yml';
51 break;
52 + case WalletType.base:
53 + path = 'assets/base_node_list.yml';
54 + break;
55 case WalletType.banano:
56 case WalletType.none:
57 path = '';
@@ -100,6 +103,7 @@ Future<void> resetToDefault(Box<Node> nodeSource) async {
103 final decredNodes = await loadDefaultNodes(WalletType.decred);
104 final zanoNodes = await loadDefaultNodes(WalletType.zano);
105 final dogecoinElectrumServerList = await loadDefaultNodes(WalletType.dogecoin);
106 + final baseNodes = await loadDefaultNodes(WalletType.base);
107
108 final nodes = moneroNodes +
109 bitcoinElectrumServerList +
@@ -113,7 +117,8 @@ Future<void> resetToDefault(Box<Node> nodeSource) async {
117 tronNodes +
118 zanoNodes +
119 decredNodes +
116 - dogecoinElectrumServerList;
120 + dogecoinElectrumServerList +
121 + baseNodes;
122
123 await nodeSource.clear();
124 await nodeSource.addAll(nodes);
lib/entities/parse_address_from_domain.dart
+1
@@ -114,6 +114,7 @@ class AddressResolver {
114 "podcast",
115 "pog",
116 "polygon",
117 + "base",
118 "press",
119 "privacy",
120 "pro",
lib/entities/preferences_key.dart
+3
@@ -8,6 +8,7 @@ class PreferencesKey {
8 static const currentZanoNodeIdKey = 'current_node_id_zano';
9 static const currentEthereumNodeIdKey = 'current_node_id_eth';
10 static const currentPolygonNodeIdKey = 'current_node_id_matic';
11 + static const currentBaseNodeIdKey = 'current_node_id_base';
12 static const currentNanoNodeIdKey = 'current_node_id_nano';
13 static const currentNanoPowNodeIdKey = 'current_node_id_nano_pow';
14 static const currentDecredNodeIdKey = 'current_node_id_decred';
@@ -52,6 +53,7 @@ class PreferencesKey {
53 static const litecoinTransactionPriority = 'current_fee_priority_litecoin';
54 static const ethereumTransactionPriority = 'current_fee_priority_ethereum';
55 static const polygonTransactionPriority = 'current_fee_priority_polygon';
56 + static const baseTransactionPriority = 'current_fee_priority_base';
57 static const bitcoinCashTransactionPriority = 'current_fee_priority_bitcoin_cash';
58 static const zanoTransactionPriority = 'current_fee_priority_zano';
59 static const wowneroTransactionPriority = 'current_fee_priority_wownero';
@@ -77,6 +79,7 @@ class PreferencesKey {
79 static const pinNativeTokenAtTop = 'pin_native_token_at_top';
80 static const useEtherscan = 'use_etherscan';
81 static const usePolygonScan = 'use_polygonscan';
82 + static const useBaseScan = 'use_base_scan';
83 static const useTronGrid = 'use_trongrid';
84 static const useMempoolFeeAPI = 'use_mempool_fee_api';
85 static const defaultNanoRep = 'default_nano_representative';
lib/entities/priority_for_wallet_type.dart
+3
@@ -1,3 +1,4 @@
1 +import 'package:cake_wallet/base/base.dart';
2 import 'package:cake_wallet/bitcoin/bitcoin.dart';
3 import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
4 import 'package:cake_wallet/dogecoin/dogecoin.dart';
@@ -28,6 +29,8 @@ List<TransactionPriority> priorityForWalletType(WalletType type) {
29 return dogecoin!.getTransactionPriorities();
30 case WalletType.polygon:
31 return polygon!.getTransactionPriorities();
32 + case WalletType.base:
33 + return base!.getTransactionPriorities();
34 // no such thing for nano/banano/solana/tron:
35 case WalletType.nano:
36 case WalletType.banano:
lib/reactions/fiat_rate_update.dart
+6
@@ -1,4 +1,5 @@
1 import 'dart:async';
2 +import 'package:cake_wallet/base/base.dart';
3 import 'package:cake_wallet/core/fiat_conversion_service.dart';
4 import 'package:cake_wallet/entities/fiat_api_mode.dart';
5 import 'package:cake_wallet/ethereum/ethereum.dart';
@@ -44,6 +45,11 @@ Future<void> startFiatRateUpdate(
45 polygon!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
46 }
47
48 + if (appStore.wallet!.type == WalletType.base) {
49 + currencies =
50 + base!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
51 + }
52 +
53 if (appStore.wallet!.type == WalletType.solana) {
54 currencies =
55 solana!.getSPLTokenCurrencies(appStore.wallet!).where((element) => element.enabled);
lib/reactions/on_current_wallet_change.dart
+5
@@ -1,3 +1,4 @@
1 +import 'package:cake_wallet/base/base.dart';
2 import 'package:cake_wallet/bitcoin/bitcoin.dart';
3 import 'package:cake_wallet/di.dart';
4 import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
@@ -130,6 +131,10 @@ void startCurrentWalletChangeReaction(
131 currencies =
132 polygon!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
133 }
134 + if (wallet.type == WalletType.base) {
135 + currencies =
136 + base!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
137 + }
138 if (wallet.type == WalletType.solana) {
139 currencies =
140 solana!.getSPLTokenCurrencies(appStore.wallet!).where((element) => element.enabled);
lib/reactions/wallet_connect.dart
+12 -2
@@ -8,6 +8,7 @@ bool isEVMCompatibleChain(WalletType walletType) {
8 switch (walletType) {
9 case WalletType.polygon:
10 case WalletType.ethereum:
11 + case WalletType.base:
12 return true;
13 default:
14 return false;
@@ -18,6 +19,7 @@ bool isNFTACtivatedChain(WalletType walletType) {
19 switch (walletType) {
20 case WalletType.polygon:
21 case WalletType.ethereum:
22 + case WalletType.base:
23 case WalletType.solana:
24 return true;
25 default:
@@ -29,6 +31,7 @@ bool isWalletConnectCompatibleChain(WalletType walletType) {
31 switch (walletType) {
32 case WalletType.polygon:
33 case WalletType.ethereum:
34 + case WalletType.base:
35 case WalletType.solana:
36 return true;
37 default:
@@ -42,6 +45,8 @@ String getChainNameSpaceAndIdBasedOnWalletType(WalletType walletType) {
45 return EVMChainId.ethereum.chain();
46 case WalletType.polygon:
47 return EVMChainId.polygon.chain();
48 + case WalletType.base:
49 + return EVMChainId.base.chain();
50 case WalletType.solana:
51 return SolanaChainId.mainnet.chain();
52 default:
@@ -52,8 +57,8 @@ String getChainNameSpaceAndIdBasedOnWalletType(WalletType walletType) {
57 List<String> getChainSupportedMethodsOnWalletType(WalletType walletType) {
58 switch (walletType) {
59 case WalletType.ethereum:
55 - return EVMSupportedMethods.values.map((e) => e.name).toList();
60 case WalletType.polygon:
61 + case WalletType.base:
62 return EVMSupportedMethods.values.map((e) => e.name).toList();
63 case WalletType.solana:
64 return SolanaSupportedMethods.values.map((e) => e.name).toList();
@@ -66,7 +71,8 @@ int getChainIdBasedOnWalletType(WalletType walletType) {
71 switch (walletType) {
72 case WalletType.polygon:
73 return 137;
69 -
74 + case WalletType.base:
75 + return 8453;
76 // For now, we return eth chain Id as the default, we'll modify as we add more wallets
77 case WalletType.ethereum:
78 default:
@@ -80,6 +86,8 @@ String getChainNameBasedOnWalletType(WalletType walletType) {
86 return 'eth';
87 case WalletType.polygon:
88 return 'polygon';
89 + case WalletType.base:
90 + return 'base';
91 case WalletType.solana:
92 return 'mainnet';
93 default:
@@ -93,6 +101,8 @@ String getTokenNameBasedOnWalletType(WalletType walletType) {
101 return 'ETH';
102 case WalletType.polygon:
103 return 'MATIC';
104 + case WalletType.base:
105 + return 'BASE';
106 case WalletType.solana:
107 return 'SOL';
108 default:
lib/reactions/wallet_utils.dart
+2
@@ -4,6 +4,7 @@ bool isBIP39Wallet(WalletType walletType) {
4 switch (walletType) {
5 case WalletType.ethereum:
6 case WalletType.polygon:
7 + case WalletType.base:
8 case WalletType.solana:
9 case WalletType.tron:
10 case WalletType.bitcoin:
@@ -41,6 +42,7 @@ bool hasTokens(WalletType walletType) {
42 case WalletType.solana:
43 case WalletType.tron:
44 case WalletType.zano:
45 + case WalletType.base:
46 return true;
47 default:
48 return false;
lib/src/screens/connect_device/select_hardware_wallet_account_page.dart
+2 -1
@@ -10,6 +10,7 @@ import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
10 import 'package:cake_wallet/utils/responsive_layout_util.dart';
11 import 'package:cake_wallet/utils/show_pop_up.dart';
12 import 'package:cake_wallet/view_model/wallet_hardware_restore_view_model.dart';
13 +import 'package:cw_core/currency_for_wallet_type.dart';
14 import 'package:cw_core/wallet_type.dart';
15 import 'package:flutter/material.dart';
16 import 'package:flutter_mobx/flutter_mobx.dart';
@@ -148,7 +149,7 @@ class _SelectHardwareWalletAccountFormState extends State<SelectHardwareWalletAc
149 padding: EdgeInsets.only(top: 10),
150 child: SelectButton(
151 image: Image.asset(
151 - walletTypeToCryptoCurrency(_walletHardwareRestoreVM.type).iconPath ??
152 + walletTypeToCryptoCurrency(_walletHardwareRestoreVM.type).normalizedIconPath ??
153 '',
154 height: 24,
155 width: 24,
lib/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart
+3
@@ -38,6 +38,7 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
38 final ethereumIcon = Image.asset('assets/images/crypto/ethereum.png', height: 24, width: 24);
39 final polygonIcon = Image.asset('assets/images/crypto/polygon.png', height: 24, width: 24);
40 final bitcoinCashIcon = Image.asset('assets/images/crypto/bitcoin-cash.png', height: 24, width: 24);
41 + final baseIcon = Image.asset('assets/images/base_icon.png', height: 24, width: 24);
42 final nanoIcon = Image.asset('assets/images/crypto/nano.png', height: 24, width: 24);
43 final bananoIcon = Image.asset('assets/images/crypto/nano.png', height: 24, width: 24);
44 final solanaIcon = Image.asset('assets/images/crypto/solana.png', height: 24, width: 24);
@@ -184,6 +185,8 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
185 return decredIcon;
186 case WalletType.dogecoin:
187 return dogeIcon;
188 + case WalletType.base:
189 + return baseIcon;
190 case WalletType.none:
191 return nonWalletTypeIcon;
192 }
lib/src/screens/dashboard/widgets/menu_widget.dart
+4
@@ -33,6 +33,7 @@ class MenuWidgetState extends State<MenuWidget> {
33 this.bananoIcon = Image.asset('assets/images/crypto/nano.png'),
34 this.bitcoinCashIcon = Image.asset('assets/images/crypto/bitcoin-cash.png'),
35 this.polygonIcon = Image.asset('assets/images/crypto/polygon.png'),
36 + this.baseIcon = Image.asset('assets/images/base_icon.png'),
37 this.solanaIcon = Image.asset('assets/images/crypto/solana.png'),
38 this.tronIcon = Image.asset('assets/images/crypto/tron.png'),
39 this.wowneroIcon = Image.asset('assets/images/crypto/wownero.png'),
@@ -60,6 +61,7 @@ class MenuWidgetState extends State<MenuWidget> {
61 Image nanoIcon;
62 Image bananoIcon;
63 Image polygonIcon;
64 + Image baseIcon;
65 Image solanaIcon;
66 Image tronIcon;
67 Image wowneroIcon;
@@ -247,6 +249,8 @@ class MenuWidgetState extends State<MenuWidget> {
249 return polygonIcon;
250 case WalletType.solana:
251 return solanaIcon;
252 + case WalletType.base:
253 + return baseIcon;
254 case WalletType.tron:
255 return tronIcon;
256 case WalletType.wownero:
lib/src/screens/dashboard/widgets/sign_form.dart
+1
@@ -2,6 +2,7 @@ import 'package:cake_wallet/generated/i18n.dart';
2 import 'package:cake_wallet/src/widgets/address_text_field.dart';
3 import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
4 import 'package:cake_wallet/utils/show_bar.dart';
5 +import 'package:cw_core/currency_for_wallet_type.dart';
6 import 'package:cw_core/wallet_type.dart';
7 import 'package:flutter/material.dart';
8 import 'package:flutter/services.dart';
lib/src/screens/dashboard/widgets/verify_form.dart
+1
@@ -1,5 +1,6 @@
1 import 'package:cake_wallet/generated/i18n.dart';
2 import 'package:cake_wallet/src/widgets/address_text_field.dart';
3 +import 'package:cw_core/currency_for_wallet_type.dart';
4 import 'package:cw_core/wallet_type.dart';
5 import 'package:flutter/material.dart';
6
lib/src/screens/new_wallet/new_wallet_type_page.dart
+4 -3
@@ -15,6 +15,7 @@ import 'package:cake_wallet/utils/responsive_layout_util.dart';
15 import 'package:cake_wallet/utils/show_pop_up.dart';
16 import 'package:cake_wallet/view_model/new_wallet_type_view_model.dart';
17 import 'package:cake_wallet/wallet_types.g.dart';
18 +import 'package:cw_core/currency_for_wallet_type.dart';
19 import 'package:cw_core/hardware/device_connection_type.dart';
20 import 'package:cw_core/wallet_info.dart';
21 import 'package:cw_core/wallet_type.dart';
@@ -142,9 +143,9 @@ class WalletTypeFormState extends State<WalletTypeForm> {
143 child: SelectButton(
144 key: ValueKey('new_wallet_type_${type.name}_button_key'),
145 image: Image.asset(
145 - walletTypeToCryptoCurrency(type).iconPath ?? '',
146 - height: 28,
147 - width: 28,
146 + walletTypeToCryptoCurrency(type).normalizedIconPath ?? '',
147 + height: 24,
148 + width: 24,
149 ),
150 text: walletTypeToDisplayName(type),
151 showTrailingIcon: false,
lib/src/screens/new_wallet/wallet_group_display_page.dart
+2 -2
@@ -7,7 +7,7 @@ import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
7 import 'package:cake_wallet/src/widgets/primary_button.dart';
8 import 'package:cake_wallet/themes/core/material_base_theme.dart';
9 import 'package:cake_wallet/view_model/wallet_groups_display_view_model.dart';
10 -import 'package:cw_core/wallet_type.dart';
10 +import 'package:cw_core/currency_for_wallet_type.dart';
11 import 'package:flutter/material.dart';
12 import 'package:flutter_mobx/flutter_mobx.dart';
13
@@ -95,7 +95,7 @@ class WalletGroupsDisplayBody extends StatelessWidget {
95 isSelected:
96 walletGroupsDisplayViewModel.selectedSingleWallet == wallet,
97 leadingWidget: Image.asset(
98 - walletTypeToCryptoCurrency(wallet.type).iconPath!,
98 + walletTypeToCryptoCurrency(wallet.type).normalizedIconPath!,
99 width: 32,
100 height: 32,
101 ),
lib/src/screens/new_wallet/widgets/grouped_wallet_expansion_tile.dart
+2 -3
@@ -1,4 +1,4 @@
1 -import 'package:cw_core/wallet_type.dart';
1 +import 'package:cw_core/currency_for_wallet_type.dart';
2 import 'package:flutter/material.dart';
3 import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
4
@@ -99,7 +99,6 @@ class GroupedWalletExpansionTile extends StatelessWidget {
99 final currentColor = item.isCurrent
100 ? Theme.of(context).colorScheme.primary
101 : Theme.of(context).colorScheme.surface;
102 - final walletTypeToCrypto = walletTypeToCryptoCurrency(item.type);
102 return ListTile(
103 contentPadding: EdgeInsets.zero,
104 key: ValueKey(item.name),
@@ -124,7 +123,7 @@ class GroupedWalletExpansionTile extends StatelessWidget {
123 : SizedBox(width: 7),
124 SizedBox(width: 24),
125 Image.asset(
127 - walletTypeToCrypto.iconPath!,
126 + walletTypeToCryptoCurrency(item.type).normalizedIconPath!,
127 width: 32,
128 height: 32,
129 ),
lib/src/screens/settings/privacy_page.dart
+8
@@ -118,6 +118,14 @@ class PrivacyPage extends BasePage {
118 _privacySettingsViewModel.setUsePolygonScan(value);
119 },
120 ),
121 + if (_privacySettingsViewModel.canUseBaseScan)
122 + SettingsSwitcherCell(
123 + title: S.current.basescan_history,
124 + value: _privacySettingsViewModel.useBaseScan,
125 + onValueChange: (BuildContext _, bool value) {
126 + _privacySettingsViewModel.setUseBaseScan(value);
127 + },
128 + ),
129 if (_privacySettingsViewModel.canUseTronGrid)
130 SettingsSwitcherCell(
131 title: S.current.trongrid_history,
lib/src/screens/wallet_connect/services/chain_service/eth/evm_chain_id.dart
+4
@@ -1,6 +1,7 @@
1 enum EVMChainId {
2 ethereum,
3 polygon,
4 + base,
5 goerli,
6 mumbai,
7 arbitrum,
@@ -17,6 +18,9 @@ extension EVMChainIdX on EVMChainId {
18 case EVMChainId.polygon:
19 name = '137';
20 break;
21 + case EVMChainId.base:
22 + name = '8453';
23 + break;
24 case EVMChainId.goerli:
25 name = '5';
26 break;
lib/src/screens/wallet_connect/services/key_service/wallet_connect_key_service.dart
+6
@@ -1,3 +1,4 @@
1 +import 'package:cake_wallet/base/base.dart';
2 import 'package:cake_wallet/ethereum/ethereum.dart';
3 import 'package:cake_wallet/polygon/polygon.dart';
4 import 'package:cake_wallet/reactions/wallet_connect.dart';
@@ -23,6 +24,8 @@ class KeyServiceImpl implements WalletConnectKeyService {
24 return ethereum!.getPrivateKey(wallet);
25 case WalletType.polygon:
26 return polygon!.getPrivateKey(wallet);
27 + case WalletType.base:
28 + return base!.getPrivateKey(wallet);
29 case WalletType.solana:
30 return solana!.getPrivateKey(wallet);
31 default:
@@ -36,6 +39,8 @@ class KeyServiceImpl implements WalletConnectKeyService {
39 return ethereum!.getPublicKey(wallet);
40 case WalletType.polygon:
41 return polygon!.getPublicKey(wallet);
42 + case WalletType.base:
43 + return base!.getPublicKey(wallet);
44 case WalletType.solana:
45 return solana!.getPublicKey(wallet);
46 default:
@@ -51,6 +56,7 @@ class KeyServiceImpl implements WalletConnectKeyService {
56 'eip155:1',
57 'eip155:5',
58 'eip155:137',
59 + 'eip155:8453',
60 'eip155:42161',
61 'eip155:80001',
62 ],
lib/src/screens/wallet_list/wallet_list_page.dart
+1
@@ -25,6 +25,7 @@ import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
25 import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
26 import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
27 import 'package:cake_wallet/wallet_type_utils.dart';
28 +import 'package:cw_core/currency_for_wallet_type.dart';
29 import 'package:cw_core/wallet_info.dart';
30 import 'package:cw_core/wallet_type.dart';
31 import 'package:flutter/material.dart';
lib/src/widgets/bottom_sheet/payment_confirmation_bottom_sheet.dart
+1
@@ -1,5 +1,6 @@
1 import 'package:cake_wallet/generated/i18n.dart';
2 import 'package:cake_wallet/utils/payment_request.dart';
3 +import 'package:cw_core/currency_for_wallet_type.dart';
4 import 'package:flutter/material.dart';
5 import 'package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart';
6 import 'package:cake_wallet/src/widgets/primary_button.dart';
lib/src/widgets/bottom_sheet/swap_confirmation_bottom_sheet.dart
+6 -5
@@ -9,6 +9,7 @@ import 'package:cake_wallet/src/widgets/bottom_sheet/swap_details_bottom_sheet.d
9 import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
10 import 'package:cake_wallet/utils/address_formatter.dart';
11 import 'package:cake_wallet/utils/debounce.dart';
12 +import 'package:cw_core/currency_for_wallet_type.dart';
13 import 'package:cw_core/crypto_amount_format.dart';
14
15 import 'package:flutter/material.dart';
@@ -136,7 +137,7 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
137
138 @override
139 Widget build(BuildContext context) {
139 - final detectedCurrencyName = walletTypeToCryptoCurrency(widget.paymentFlowResult.walletType!);
140 + final detectedCurrency = walletTypeToCryptoCurrency(widget.paymentFlowResult.walletType!);
141
142 return Form(
143 key: _formKey,
@@ -159,7 +160,7 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
160 const SizedBox(width: 12),
161 CakeImageWidget(
162 imageUrl:
162 - walletTypeToCryptoCurrency(widget.paymentFlowResult.walletType!).iconPath!,
163 + detectedCurrency.iconPath!,
164 width: 32,
165 height: 32,
166 ),
@@ -168,7 +169,7 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
169 const SizedBox(height: 16),
170 SwapConfirmationTextfield(
171 key: ValueKey('swap_confirmation_bottomsheet_amount_textfield_key'),
171 - hintText: 'Amount (${detectedCurrencyName})',
172 + hintText: 'Amount (${detectedCurrency})',
173 focusNode: _amountFocus,
174 controller: _amountController,
175 keyboardType: TextInputType.numberWithOptions(decimal: true, signed: false),
@@ -226,7 +227,7 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
227 min != null
228 ? Text(
229 key: ValueKey('min_limit_text_key'),
229 - S.of(context).min_value(min, detectedCurrencyName.toString()),
230 + S.of(context).min_value(min, detectedCurrency.toString()),
231 style: Theme.of(context).textTheme.bodySmall!.copyWith(
232 fontSize: 10,
233 height: 1.2,
@@ -238,7 +239,7 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
239 max != null
240 ? Text(
241 key: ValueKey('max_limit_text_key'),
241 - S.of(context).max_value(max, detectedCurrencyName.toString()),
242 + S.of(context).max_value(max, detectedCurrency.toString()),
243 style: Theme.of(context).textTheme.bodySmall!.copyWith(
244 fontSize: 10,
245 height: 1.2,
lib/src/widgets/bottom_sheet/wallet_switcher_bottom_sheet.dart
+2 -1
@@ -1,4 +1,5 @@
1 import 'package:cake_wallet/generated/i18n.dart';
2 +import 'package:cw_core/currency_for_wallet_type.dart';
3 import 'package:flutter/material.dart';
4 import 'package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart';
5 import 'package:cake_wallet/src/widgets/standard_list.dart';
@@ -94,7 +95,7 @@ class _WalletSwitcherContent extends StatelessWidget {
95 child: Row(
96 children: [
97 Image.asset(
97 - walletTypeToCryptoCurrency(wallet.type).iconPath!,
98 + walletTypeToCryptoCurrency(wallet.type).normalizedIconPath!,
99 width: 32,
100 height: 32,
101 ),
lib/store/settings_store.dart
+49
@@ -2,6 +2,7 @@ import 'dart:async';
2 import 'dart:convert';
3 import 'dart:io';
4
5 +import 'package:cake_wallet/base/base.dart';
6 import 'package:cake_wallet/bitcoin/bitcoin.dart';
7 import 'package:cake_wallet/core/utilities.dart';
8 import 'package:cake_wallet/decred/decred.dart';
@@ -108,6 +109,7 @@ abstract class SettingsStoreBase with Store {
109 required this.sortBalanceBy,
110 required this.pinNativeTokenAtTop,
111 required this.useEtherscan,
112 + required this.useBaseScan,
113 required this.usePolygonScan,
114 required this.useTronGrid,
115 required this.useMempoolFeeAPI,
@@ -139,6 +141,7 @@ abstract class SettingsStoreBase with Store {
141 TransactionPriority? initialLitecoinTransactionPriority,
142 TransactionPriority? initialEthereumTransactionPriority,
143 TransactionPriority? initialPolygonTransactionPriority,
144 + TransactionPriority? initialBaseTransactionPriority,
145 TransactionPriority? initialBitcoinCashTransactionPriority,
146 TransactionPriority? initialZanoTransactionPriority,
147 TransactionPriority? initialDecredTransactionPriority,
@@ -224,6 +227,10 @@ abstract class SettingsStoreBase with Store {
227 priority[WalletType.polygon] = initialPolygonTransactionPriority;
228 }
229
230 + if (initialBaseTransactionPriority != null) {
231 + priority[WalletType.base] = initialBaseTransactionPriority;
232 + }
233 +
234 if (initialBitcoinCashTransactionPriority != null) {
235 priority[WalletType.bitcoinCash] = initialBitcoinCashTransactionPriority;
236 }
@@ -291,6 +298,9 @@ abstract class SettingsStoreBase with Store {
298 case WalletType.polygon:
299 key = PreferencesKey.polygonTransactionPriority;
300 break;
301 + case WalletType.base:
302 + key = PreferencesKey.baseTransactionPriority;
303 + break;
304 case WalletType.zano:
305 key = PreferencesKey.zanoTransactionPriority;
306 break;
@@ -453,6 +463,11 @@ abstract class SettingsStoreBase with Store {
463 (bool usePolygonScan) =>
464 _sharedPreferences.setBool(PreferencesKey.usePolygonScan, usePolygonScan));
465
466 + reaction(
467 + (_) => useBaseScan,
468 + (bool useBaseScan) =>
469 + _sharedPreferences.setBool(PreferencesKey.useBaseScan, useBaseScan));
470 +
471 reaction((_) => useTronGrid,
472 (bool useTronGrid) => _sharedPreferences.setBool(PreferencesKey.useTronGrid, useTronGrid));
473
@@ -808,6 +823,9 @@ abstract class SettingsStoreBase with Store {
823 @observable
824 bool usePolygonScan;
825
826 + @observable
827 + bool useBaseScan;
828 +
829 @observable
830 bool useTronGrid;
831
@@ -949,6 +967,7 @@ abstract class SettingsStoreBase with Store {
967 TransactionPriority? litecoinTransactionPriority;
968 TransactionPriority? ethereumTransactionPriority;
969 TransactionPriority? polygonTransactionPriority;
970 + TransactionPriority? baseTransactionPriority;
971 TransactionPriority? bitcoinCashTransactionPriority;
972 TransactionPriority? wowneroTransactionPriority;
973 TransactionPriority? zanoTransactionPriority;
@@ -970,6 +989,10 @@ abstract class SettingsStoreBase with Store {
989 polygonTransactionPriority = polygon?.deserializePolygonTransactionPriority(
990 sharedPreferences.getInt(PreferencesKey.polygonTransactionPriority)!);
991 }
992 + if (sharedPreferences.getInt(PreferencesKey.baseTransactionPriority) != null) {
993 + baseTransactionPriority = base?.deserializeBaseTransactionPriority(
994 + sharedPreferences.getInt(PreferencesKey.baseTransactionPriority)!);
995 + }
996 if (sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority) != null) {
997 bitcoinCashTransactionPriority = bitcoinCash?.deserializeBitcoinCashTransactionPriority(
998 sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority)!);
@@ -996,6 +1019,7 @@ abstract class SettingsStoreBase with Store {
1019 wowneroTransactionPriority ??= wownero?.getDefaultTransactionPriority();
1020 decredTransactionPriority ??= decred?.getDecredTransactionPriorityMedium();
1021 polygonTransactionPriority ??= polygon?.getDefaultTransactionPriority();
1022 + baseTransactionPriority ??= base?.getDefaultTransactionPriority();
1023 zanoTransactionPriority ??= zano?.getDefaultTransactionPriority();
1024
1025 final currentBalanceDisplayMode = BalanceDisplayMode.deserialize(
@@ -1042,6 +1066,7 @@ abstract class SettingsStoreBase with Store {
1066 : defaultSeedPhraseLength;
1067 final useEtherscan = sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true;
1068 final usePolygonScan = sharedPreferences.getBool(PreferencesKey.usePolygonScan) ?? true;
1069 + final useBaseScan = sharedPreferences.getBool(PreferencesKey.useBaseScan) ?? true;
1070 final useTronGrid = sharedPreferences.getBool(PreferencesKey.useTronGrid) ?? true;
1071 final useMempoolFeeAPI = sharedPreferences.getBool(PreferencesKey.useMempoolFeeAPI) ?? true;
1072 final defaultNanoRep = sharedPreferences.getString(PreferencesKey.defaultNanoRep) ?? "";
@@ -1087,6 +1112,7 @@ abstract class SettingsStoreBase with Store {
1112 sharedPreferences.getInt(PreferencesKey.currentBitcoinCashNodeIdKey);
1113 final ethereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
1114 final polygonNodeId = sharedPreferences.getInt(PreferencesKey.currentPolygonNodeIdKey);
1115 + final baseNodeId = sharedPreferences.getInt(PreferencesKey.currentBaseNodeIdKey);
1116 final nanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
1117 final nanoPowNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoPowNodeIdKey);
1118 final solanaNodeId = sharedPreferences.getInt(PreferencesKey.currentSolanaNodeIdKey);
@@ -1107,6 +1133,8 @@ abstract class SettingsStoreBase with Store {
1133 nodeSource.values.firstWhereOrNull((e) => e.uriRaw == ethereumDefaultNodeUri);
1134 final polygonNode = nodeSource.get(polygonNodeId) ??
1135 nodeSource.values.firstWhereOrNull((e) => e.uriRaw == polygonDefaultNodeUri);
1136 + final baseNode = nodeSource.get(baseNodeId) ??
1137 + nodeSource.values.firstWhereOrNull((e) => e.uriRaw == baseDefaultNodeUri);
1138 final bitcoinCashElectrumServer = nodeSource.get(bitcoinCashElectrumServerId) ??
1139 nodeSource.values.firstWhereOrNull((e) => e.uriRaw == cakeWalletBitcoinCashDefaultNodeUri);
1140 final nanoNode = nodeSource.get(nanoNodeId) ??
@@ -1180,6 +1208,10 @@ abstract class SettingsStoreBase with Store {
1208 nodes[WalletType.polygon] = polygonNode;
1209 }
1210
1211 + if (baseNode != null) {
1212 + nodes[WalletType.base] = baseNode;
1213 + }
1214 +
1215 if (bitcoinCashElectrumServer != null) {
1216 nodes[WalletType.bitcoinCash] = bitcoinCashElectrumServer;
1217 }
@@ -1358,6 +1390,7 @@ abstract class SettingsStoreBase with Store {
1390 pinNativeTokenAtTop: pinNativeTokenAtTop,
1391 useEtherscan: useEtherscan,
1392 usePolygonScan: usePolygonScan,
1393 + useBaseScan: useBaseScan,
1394 useTronGrid: useTronGrid,
1395 useMempoolFeeAPI: useMempoolFeeAPI,
1396 defaultNanoRep: defaultNanoRep,
@@ -1404,6 +1437,7 @@ abstract class SettingsStoreBase with Store {
1437 shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
1438 initialEthereumTransactionPriority: ethereumTransactionPriority,
1439 initialPolygonTransactionPriority: polygonTransactionPriority,
1440 + initialBaseTransactionPriority: baseTransactionPriority,
1441 initialSyncMode: savedSyncMode,
1442 initialSyncAll: savedSyncAll,
1443 shouldShowYatPopup: shouldShowYatPopup,
@@ -1455,6 +1489,11 @@ abstract class SettingsStoreBase with Store {
1489 priority[WalletType.polygon] = polygon!.deserializePolygonTransactionPriority(
1490 sharedPreferences.getInt(PreferencesKey.polygonTransactionPriority)!);
1491 }
1492 + if (base != null &&
1493 + sharedPreferences.getInt(PreferencesKey.baseTransactionPriority) != null) {
1494 + priority[WalletType.base] = base!.deserializeBaseTransactionPriority(
1495 + sharedPreferences.getInt(PreferencesKey.baseTransactionPriority)!);
1496 + }
1497 if (bitcoinCash != null &&
1498 sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority) != null) {
1499 priority[WalletType.bitcoinCash] = bitcoinCash!.deserializeBitcoinCashTransactionPriority(
@@ -1548,6 +1587,7 @@ abstract class SettingsStoreBase with Store {
1587 pinNativeTokenAtTop = sharedPreferences.getBool(PreferencesKey.pinNativeTokenAtTop) ?? true;
1588 useEtherscan = sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true;
1589 usePolygonScan = sharedPreferences.getBool(PreferencesKey.usePolygonScan) ?? true;
1590 + useBaseScan = sharedPreferences.getBool(PreferencesKey.useBaseScan) ?? true;
1591 useTronGrid = sharedPreferences.getBool(PreferencesKey.useTronGrid) ?? true;
1592 useMempoolFeeAPI = sharedPreferences.getBool(PreferencesKey.useMempoolFeeAPI) ?? true;
1593 defaultNanoRep = sharedPreferences.getString(PreferencesKey.defaultNanoRep) ?? "";
@@ -1578,6 +1618,7 @@ abstract class SettingsStoreBase with Store {
1618 final havenNodeId = sharedPreferences.getInt(PreferencesKey.currentHavenNodeIdKey);
1619 final ethereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
1620 final polygonNodeId = sharedPreferences.getInt(PreferencesKey.currentPolygonNodeIdKey);
1621 + final baseNodeId = sharedPreferences.getInt(PreferencesKey.currentBaseNodeIdKey);
1622 final nanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
1623 final solanaNodeId = sharedPreferences.getInt(PreferencesKey.currentSolanaNodeIdKey);
1624 final tronNodeId = sharedPreferences.getInt(PreferencesKey.currentTronNodeIdKey);
@@ -1591,6 +1632,7 @@ abstract class SettingsStoreBase with Store {
1632 final havenNode = nodeSource.get(havenNodeId);
1633 final ethereumNode = nodeSource.get(ethereumNodeId);
1634 final polygonNode = nodeSource.get(polygonNodeId);
1635 + final baseNode = nodeSource.get(baseNodeId);
1636 final bitcoinCashNode = nodeSource.get(bitcoinCashElectrumServerId);
1637 final nanoNode = nodeSource.get(nanoNodeId);
1638 final solanaNode = nodeSource.get(solanaNodeId);
@@ -1624,6 +1666,10 @@ abstract class SettingsStoreBase with Store {
1666 nodes[WalletType.polygon] = polygonNode;
1667 }
1668
1669 + if (baseNode != null) {
1670 + nodes[WalletType.base] = baseNode;
1671 + }
1672 +
1673 if (bitcoinCashNode != null) {
1674 nodes[WalletType.bitcoinCash] = bitcoinCashNode;
1675 }
@@ -1784,6 +1830,9 @@ abstract class SettingsStoreBase with Store {
1830 case WalletType.polygon:
1831 await _sharedPreferences.setInt(PreferencesKey.currentPolygonNodeIdKey, node.key as int);
1832 break;
1833 + case WalletType.base:
1834 + await _sharedPreferences.setInt(PreferencesKey.currentBaseNodeIdKey, node.key as int);
1835 + break;
1836 case WalletType.solana:
1837 await _sharedPreferences.setInt(PreferencesKey.currentSolanaNodeIdKey, node.key as int);
1838 break;
lib/utils/qr_util.dart
+3 -1
@@ -28,9 +28,11 @@ String getQrImage(WalletType type) {
28 return 'assets/images/dcr_chain_qr.svg';
29 case WalletType.dogecoin:
30 return 'assets/images/doge_chain_qr.svg';
31 + case WalletType.base:
32 + return 'assets/images/base_chain_QR.svg';
33 case WalletType.banano:
34 case WalletType.haven:
35 case WalletType.none:
34 - return 'assets/images/qr-cake.svg';
36 + return 'assets/images/qr-cake.png';
37 }
38 }
lib/utils/token_utilities.dart
+5 -2
@@ -1,3 +1,4 @@
1 +import 'package:cake_wallet/reactions/wallet_connect.dart';
2 import 'package:cw_core/cake_hive.dart';
3 import 'package:cw_core/crypto_currency.dart';
4 import 'package:cw_core/erc20_token.dart';
@@ -13,14 +14,14 @@ class TokenUtilities {
14 Box<WalletInfo> walletInfoSource,
15 ) async {
16 final evmWallets = walletInfoSource.values.where(
16 - (w) => w.type == WalletType.ethereum || w.type == WalletType.polygon,
17 + (w) => isEVMCompatibleChain(w.type),
18 );
19
20 final seen = <String>{};
21 final unique = <Erc20Token>[];
22
23 for (final wallet in evmWallets) {
23 - final chain = wallet.type == WalletType.ethereum ? 'ETH' : 'POL';
24 + final chain = getTokenNameBasedOnWalletType(wallet.type);
25 final box = await _openEvmTokensBoxFor(wallet);
26
27 for (final t in box.values.where((t) => t.enabled)) {
@@ -88,6 +89,7 @@ class TokenUtilities {
89 switch (walletType) {
90 case WalletType.ethereum:
91 case WalletType.polygon:
92 + case WalletType.base:
93 final tokens = await loadAllUniqueEvmTokens(walletInfoSource);
94 for (final t in tokens) {
95 if (t.contractAddress.toLowerCase() == lower) return t;
@@ -117,6 +119,7 @@ class TokenUtilities {
119 final boxName = switch (walletInfo.type) {
120 WalletType.ethereum => '${walletKey}_${Erc20Token.ethereumBoxName}',
121 WalletType.polygon => '${walletKey}_${Erc20Token.polygonBoxName}',
122 + WalletType.base => '${walletKey}_${Erc20Token.baseBoxName}',
123 _ => '${walletKey}_${Erc20Token.ethereumBoxName}',
124 };
125
lib/view_model/advanced_privacy_settings_view_model.dart
+2
@@ -39,6 +39,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
39 case WalletType.bitcoinCash:
40 case WalletType.dogecoin:
41 case WalletType.polygon:
42 + case WalletType.base:
43 case WalletType.solana:
44 case WalletType.tron:
45 return true;
@@ -79,6 +80,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
80 WalletType.bitcoinCash,
81 WalletType.ethereum,
82 WalletType.polygon,
83 + WalletType.base,
84 WalletType.tron,
85 WalletType.solana,
86 WalletType.monero,
lib/view_model/anon_invoice_page_view_model.dart
+1
@@ -5,6 +5,7 @@ import 'package:cake_wallet/core/execution_state.dart';
5 import 'package:cake_wallet/entities/fiat_currency.dart';
6 import 'package:cake_wallet/entities/preferences_key.dart';
7 import 'package:cake_wallet/utils/qr_util.dart';
8 +import 'package:cw_core/currency_for_wallet_type.dart';
9 import 'package:cw_core/receive_page_option.dart';
10 import 'package:cake_wallet/store/settings_store.dart';
11 import 'package:cw_core/crypto_currency.dart';
lib/view_model/buy/buy_view_model.dart
+1
@@ -3,6 +3,7 @@ import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
3 import 'package:cake_wallet/buy/wyre/wyre_buy_provider.dart';
4 import 'package:cw_core/crypto_currency.dart';
5 import 'package:cake_wallet/entities/fiat_currency.dart';
6 +import 'package:cw_core/currency_for_wallet_type.dart';
7 import 'package:cw_core/utils/print_verbose.dart';
8 import 'package:cw_core/wallet_type.dart';
9 import 'package:cake_wallet/store/settings_store.dart';
lib/view_model/contact_list/contact_list_view_model.dart
+1
@@ -10,6 +10,7 @@ import 'package:cake_wallet/generated/i18n.dart';
10 import 'package:cake_wallet/store/settings_store.dart';
11 import 'package:cake_wallet/utils/mobx.dart';
12 import 'package:cw_core/crypto_currency.dart';
13 +import 'package:cw_core/currency_for_wallet_type.dart';
14 import 'package:cw_core/wallet_info.dart';
15 import 'package:cw_core/wallet_type.dart';
16 import 'package:hive/hive.dart';
lib/view_model/dashboard/balance_view_model.dart
+1
@@ -166,6 +166,7 @@ abstract class BalanceViewModelBase with Store {
166 case WalletType.haven:
167 case WalletType.ethereum:
168 case WalletType.polygon:
169 + case WalletType.base:
170 case WalletType.solana:
171 case WalletType.tron:
172 return S.current.xmr_full_balance;
lib/view_model/dashboard/dashboard_view_model.dart
+1
@@ -873,6 +873,7 @@ abstract class DashboardViewModelBase with Store {
873 case WalletType.bitcoinCash:
874 case WalletType.ethereum:
875 case WalletType.polygon:
876 + case WalletType.base:
877 case WalletType.solana:
878 case WalletType.nano:
879 case WalletType.banano:
lib/view_model/dashboard/home_settings_view_model.dart
+51 -8
@@ -1,5 +1,6 @@
1 import 'dart:convert';
2
3 +import 'package:cake_wallet/base/base.dart';
4 import 'package:cake_wallet/core/fiat_conversion_service.dart';
5 import 'package:cake_wallet/entities/fiat_api_mode.dart';
6 import 'package:cake_wallet/entities/erc20_token_info_moralis.dart';
@@ -99,6 +100,18 @@ abstract class HomeSettingsViewModelBase with Store {
100 await polygon!.addErc20Token(_balanceViewModel.wallet, polygonToken);
101 }
102
103 + if (_balanceViewModel.wallet.type == WalletType.base) {
104 + final baseToken = Erc20Token(
105 + name: token.name,
106 + symbol: token.title,
107 + decimal: token.decimals,
108 + contractAddress: contractAddress.toLowerCase(),
109 + iconPath: token.iconPath,
110 + isPotentialScam: token.isPotentialScam,
111 + );
112 + await base!.addErc20Token(_balanceViewModel.wallet, baseToken);
113 + }
114 +
115 if (_balanceViewModel.wallet.type == WalletType.solana) {
116 final splToken = token.copyWith(enabled: true);
117 await solana!.addSPLToken(
@@ -136,6 +149,10 @@ abstract class HomeSettingsViewModelBase with Store {
149 return polygon!.isTokenAlreadyAdded(_balanceViewModel.wallet, contractAddress);
150 }
151
152 + if (_balanceViewModel.wallet.type == WalletType.base) {
153 + return base!.isTokenAlreadyAdded(_balanceViewModel.wallet, contractAddress);
154 + }
155 +
156 if (_balanceViewModel.wallet.type == WalletType.solana) {
157 return solana!.isTokenAlreadyAdded(_balanceViewModel.wallet, contractAddress);
158 }
@@ -163,6 +180,10 @@ abstract class HomeSettingsViewModelBase with Store {
180 await polygon!.deleteErc20Token(_balanceViewModel.wallet, token as Erc20Token);
181 }
182
183 + if (_balanceViewModel.wallet.type == WalletType.base) {
184 + await base!.deleteErc20Token(_balanceViewModel.wallet, token as Erc20Token);
185 + }
186 +
187 if (_balanceViewModel.wallet.type == WalletType.solana) {
188 await solana!.deleteSPLToken(_balanceViewModel.wallet, token);
189 }
@@ -189,16 +210,14 @@ abstract class HomeSettingsViewModelBase with Store {
210 return false;
211 }
212
192 - bool isEthereum = _balanceViewModel.wallet.type == WalletType.ethereum;
193 -
213 bool isPotentialScamViaMoralis = await _isPotentialScamTokenViaMoralis(
214 contractAddress,
196 - isEthereum ? 'eth' : 'polygon',
215 + getChainNameBasedOnWalletType(_balanceViewModel.wallet.type),
216 );
217
218 bool isUnverifiedContract = await _isContractUnverified(
219 contractAddress,
201 - isEthereum: isEthereum,
220 + chainId: getChainIdBasedOnWalletType(_balanceViewModel.wallet.type).toString(),
221 );
222
223 final showWarningForContractAddress = isPotentialScamViaMoralis || isUnverifiedContract;
@@ -219,6 +238,9 @@ abstract class HomeSettingsViewModelBase with Store {
238 case WalletType.polygon:
239 defaultTokenAddresses = polygon!.getDefaultTokenContractAddresses();
240 break;
241 + case WalletType.base:
242 + defaultTokenAddresses = base!.getDefaultTokenContractAddresses();
243 + break;
244 case WalletType.solana:
245 defaultTokenAddresses = solana!.getDefaultTokenContractAddresses();
246 break;
@@ -306,13 +328,13 @@ abstract class HomeSettingsViewModelBase with Store {
328
329 Future<bool> _isContractUnverified(
330 String contractAddress, {
309 - required bool isEthereum,
331 + required String chainId,
332 }) async {
333 final uri = Uri.https(
334 "api.etherscan.io",
335 "/v2/api",
336 {
315 - "chainid": isEthereum ? "1" : "137",
337 + "chainid": chainId,
338 "module": "contract",
339 "action": "getsourcecode",
340 "address": contractAddress,
@@ -354,6 +376,10 @@ abstract class HomeSettingsViewModelBase with Store {
376 return await polygon!.getErc20Token(_balanceViewModel.wallet, contractAddress);
377 }
378
379 + if (_balanceViewModel.wallet.type == WalletType.base) {
380 + return await base!.getErc20Token(_balanceViewModel.wallet, contractAddress);
381 + }
382 +
383 if (_balanceViewModel.wallet.type == WalletType.solana) {
384 return await solana!.getSPLToken(_balanceViewModel.wallet, contractAddress);
385 }
@@ -395,6 +421,11 @@ abstract class HomeSettingsViewModelBase with Store {
421 if (!value) polygon!.removeTokenTransactionsInHistory(_balanceViewModel.wallet, token);
422 }
423
424 + if (_balanceViewModel.wallet.type == WalletType.base) {
425 + base!.addErc20Token(_balanceViewModel.wallet, token as Erc20Token);
426 + if (!value) base!.removeTokenTransactionsInHistory(_balanceViewModel.wallet, token);
427 + }
428 +
429 if (_balanceViewModel.wallet.type == WalletType.solana) {
430 final address = solana!.getTokenAddress(token);
431 solana!.addSPLToken(_balanceViewModel.wallet, token, address);
@@ -448,6 +479,14 @@ abstract class HomeSettingsViewModelBase with Store {
479 ..sort(_sortFunc));
480 }
481
482 + if (_balanceViewModel.wallet.type == WalletType.base) {
483 + tokens.addAll(base!
484 + .getERC20Currencies(_balanceViewModel.wallet)
485 + .where((element) => _matchesSearchText(element))
486 + .toList()
487 + ..sort(_sortFunc));
488 + }
489 +
490 if (_balanceViewModel.wallet.type == WalletType.solana) {
491 tokens.addAll(solana!
492 .getSPLTokenCurrencies(_balanceViewModel.wallet)
@@ -489,7 +528,7 @@ abstract class HomeSettingsViewModelBase with Store {
528 bool _matchesSearchText(CryptoCurrency asset) {
529 final address = getTokenAddressBasedOnWallet(asset);
530
492 - // The homes settings would only be displayed for either of Tron, Ethereum, Polygon or Solana Wallets.
531 + // The homes settings would only be displayed for either of Tron, Ethereum, Polygon, Base or Solana Wallets.
532 if (address == null) return false;
533
534 return searchText.isEmpty ||
@@ -515,11 +554,15 @@ abstract class HomeSettingsViewModelBase with Store {
554 return polygon!.getTokenAddress(asset);
555 }
556
557 + if (_balanceViewModel.wallet.type == WalletType.base) {
558 + return base!.getTokenAddress(asset);
559 + }
560 +
561 if (_balanceViewModel.wallet.type == WalletType.zano) {
562 return zano!.getZanoAssetAddress(asset);
563 }
564
522 - // 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).
565 + // 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).
566 return null;
567 }
568 }
lib/view_model/dashboard/transaction_list_item.dart
+13
@@ -1,3 +1,4 @@
1 +import 'package:cake_wallet/base/base.dart';
2 import 'package:cake_wallet/decred/decred.dart';
3 import 'package:cake_wallet/entities/balance_display_mode.dart';
4 import 'package:cake_wallet/entities/fiat_currency.dart';
@@ -134,6 +135,11 @@ class TransactionListItem extends ActionListItem with Keyable {
135 return asset;
136 }
137
138 + if (balanceViewModel.wallet.type == WalletType.base) {
139 + final asset = base!.assetOfTransaction(balanceViewModel.wallet, transaction);
140 + return asset;
141 + }
142 +
143 if (balanceViewModel.wallet.type == WalletType.solana) {
144 final asset = solana!.assetOfTransaction(balanceViewModel.wallet, transaction);
145 return asset;
@@ -186,6 +192,13 @@ class TransactionListItem extends ActionListItem with Keyable {
192 cryptoAmount: polygon!.formatterPolygonAmountToDouble(transaction: transaction),
193 price: price);
194 break;
195 + case WalletType.base:
196 + final asset = base!.assetOfTransaction(balanceViewModel.wallet, transaction);
197 + final price = balanceViewModel.fiatConvertationStore.prices[asset];
198 + amount = calculateFiatAmountRaw(
199 + cryptoAmount: base!.formatterBaseAmountToDouble(transaction: transaction),
200 + price: price);
201 + break;
202 case WalletType.nano:
203 amount = calculateFiatAmountRaw(
204 cryptoAmount: double.parse(nanoUtil!.getRawAsUsableString(
lib/view_model/exchange/exchange_trade_view_model.dart
+9 -2
@@ -17,6 +17,7 @@ import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
17 import 'package:cake_wallet/exchange/provider/xoswap_exchange_provider.dart';
18 import 'package:cake_wallet/exchange/trade.dart';
19 import 'package:cake_wallet/generated/i18n.dart';
20 +import 'package:cake_wallet/reactions/wallet_connect.dart';
21 import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_item.dart';
22 import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
23 import 'package:cake_wallet/store/dashboard/trades_store.dart';
@@ -306,6 +307,9 @@ abstract class ExchangeTradeViewModelBase with Store {
307 wallet.currency == CryptoCurrency.maticpoly &&
308 tradeFrom?.tag == CryptoCurrency.maticpoly.tag;
309
310 + bool _isBaseToken() =>
311 + wallet.currency == CryptoCurrency.baseEth && tradeFrom?.tag == CryptoCurrency.baseEth.title;
312 +
313 bool _isTronToken() =>
314 wallet.currency == CryptoCurrency.trx && tradeFrom?.tag == CryptoCurrency.trx.title;
315
@@ -317,7 +321,8 @@ abstract class ExchangeTradeViewModelBase with Store {
321 _isEthToken() ||
322 _isPolygonToken() ||
323 _isSplToken() ||
320 - _isTronToken();
324 + _isTronToken() ||
325 + _isBaseToken();
326 }
327
328 PaymentURI? get paymentUri {
@@ -343,6 +348,8 @@ abstract class ExchangeTradeViewModelBase with Store {
348 // TODO: Expand ERC681URI support to Polygon(modify decoding flow for QRs, pay anything, and deep link handling)
349 case WalletType.polygon:
350 return PolygonURI(amount: amount, address: inputAddress);
351 + case WalletType.base:
352 + return BaseURI(amount: amount, address: inputAddress);
353 case WalletType.solana:
354 return SolanaURI(amount: amount, address: inputAddress);
355 case WalletType.tron:
@@ -377,7 +384,7 @@ abstract class ExchangeTradeViewModelBase with Store {
384 contractAddress: null,
385 );
386 } else {
380 - if (wallet.type == WalletType.polygon || wallet.type == WalletType.ethereum) {
387 + if (isEVMCompatibleChain(wallet.type)) {
388 final erc20Token = TokenUtilities.findErc20Token(currency, wallet);
389
390 if (erc20Token != null) {
lib/view_model/exchange/exchange_view_model.dart
+4
@@ -878,6 +878,10 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
878 depositCurrency = CryptoCurrency.maticpoly;
879 receiveCurrency = CryptoCurrency.xmr;
880 break;
881 + case WalletType.base:
882 + depositCurrency = CryptoCurrency.baseEth;
883 + receiveCurrency = CryptoCurrency.xmr;
884 + break;
885 case WalletType.solana:
886 depositCurrency = CryptoCurrency.sol;
887 receiveCurrency = CryptoCurrency.xmr;
lib/view_model/node_list/node_create_or_edit_view_model.dart
+1
@@ -80,6 +80,7 @@ abstract class NodeCreateOrEditViewModelBase with Store {
80 switch (_walletType) {
81 case WalletType.ethereum:
82 case WalletType.polygon:
83 + case WalletType.base:
84 case WalletType.solana:
85 case WalletType.banano:
86 case WalletType.nano:
lib/view_model/restore/wallet_restore_from_qr_code.dart
+4 -9
@@ -3,11 +3,13 @@ import 'dart:convert';
3 import 'package:cake_wallet/core/seed_validator.dart';
4 import 'package:cake_wallet/entities/parse_address_from_domain.dart';
5 import 'package:cake_wallet/entities/qr_scanner.dart';
6 +import 'package:cake_wallet/reactions/wallet_connect.dart';
7 import 'package:cake_wallet/routes.dart';
8 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
9 import 'package:cake_wallet/utils/show_pop_up.dart';
10 import 'package:cake_wallet/view_model/restore/restore_mode.dart';
11 import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
12 +import 'package:cw_core/currency_for_wallet_type.dart';
13 import 'package:cw_core/wallet_type.dart';
14 import 'package:flutter/cupertino.dart';
15 import 'package:cake_wallet/generated/i18n.dart';
@@ -29,6 +31,7 @@ class WalletRestoreFromQRCode {
31 'litecoin_wallet': WalletType.litecoin,
32 'ethereum-wallet': WalletType.ethereum,
33 'polygon-wallet': WalletType.polygon,
34 + 'base-wallet': WalletType.base,
35 'nano-wallet': WalletType.nano,
36 'nano_wallet': WalletType.nano,
37 'bitcoincash': WalletType.bitcoinCash,
@@ -206,15 +209,7 @@ class WalletRestoreFromQRCode {
209 : throw Exception('Unexpected restore mode: spend_key or view_key is invalid');
210 }
211
209 - if (type == WalletType.ethereum && credentials.containsKey('private_key')) {
210 - final privateKey = credentials['private_key'] as String;
211 - if (privateKey.isEmpty) {
212 - throw Exception('Unexpected restore mode: private_key');
213 - }
214 - return WalletRestoreMode.keys;
215 - }
216 -
217 - if (type == WalletType.polygon && credentials.containsKey('private_key')) {
212 + if (isEVMCompatibleChain(type) && credentials.containsKey('private_key')) {
213 final privateKey = credentials['private_key'] as String;
214 if (privateKey.isEmpty) {
215 throw Exception('Unexpected restore mode: private_key');
lib/view_model/send/fees_view_model.dart
+6
@@ -1,3 +1,4 @@
1 +import 'package:cake_wallet/base/base.dart';
2 import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
3 import 'package:cake_wallet/decred/decred.dart';
4 import 'package:cake_wallet/dogecoin/dogecoin.dart';
@@ -90,6 +91,8 @@ abstract class FeesViewModelBase extends WalletChangeListenerViewModel with Stor
91 return transactionPriority == bitcoinCash!.getBitcoinCashTransactionPrioritySlow();
92 case WalletType.polygon:
93 return transactionPriority == polygon!.getPolygonTransactionPrioritySlow();
94 + case WalletType.base:
95 + return transactionPriority == base!.getBaseTransactionPrioritySlow();
96 case WalletType.decred:
97 return transactionPriority == decred!.getDecredTransactionPrioritySlow();
98 case WalletType.dogecoin:
@@ -198,6 +201,9 @@ abstract class FeesViewModelBase extends WalletChangeListenerViewModel with Stor
201 case WalletType.polygon:
202 _settingsStore.priority[wallet.type] = polygon!.getDefaultTransactionPriority();
203 break;
204 + case WalletType.base:
205 + _settingsStore.priority[wallet.type] = base!.getDefaultTransactionPriority();
206 + break;
207 default:
208 break;
209 }
lib/view_model/send/output.dart
+9
@@ -1,3 +1,4 @@
1 +import 'package:cake_wallet/base/base.dart';
2 import 'package:cake_wallet/decred/decred.dart';
3 import 'package:cake_wallet/di.dart';
4 import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart';
@@ -114,6 +115,9 @@ abstract class OutputBase with Store {
115 case WalletType.polygon:
116 _amount = polygon!.formatterPolygonParseAmount(_cryptoAmount);
117 break;
118 + case WalletType.base:
119 + _amount = base!.formatterBaseParseAmount(_cryptoAmount);
120 + break;
121 case WalletType.wownero:
122 _amount = wownero!.formatterWowneroParseAmount(amount: _cryptoAmount);
123 break;
@@ -196,6 +200,10 @@ abstract class OutputBase with Store {
200 return polygon!.formatterPolygonAmountToDouble(amount: BigInt.from(fee));
201 }
202
203 + if (_wallet.type == WalletType.base) {
204 + return base!.formatterBaseAmountToDouble(amount: BigInt.from(fee));
205 + }
206 +
207 if (_wallet.type == WalletType.zano) {
208 return zano!.formatterIntAmountToDouble(
209 amount: fee, currency: cryptoCurrencyHandler(), forFee: true);
@@ -328,6 +336,7 @@ abstract class OutputBase with Store {
336 case WalletType.monero:
337 case WalletType.ethereum:
338 case WalletType.polygon:
339 + case WalletType.base:
340 case WalletType.solana:
341 case WalletType.tron:
342 case WalletType.haven:
lib/view_model/send/send_template_view_model.dart
+2
@@ -1,6 +1,7 @@
1 import 'package:cake_wallet/reactions/wallet_connect.dart';
2 import 'package:cake_wallet/view_model/send/template_view_model.dart';
3 import 'package:cw_core/crypto_currency.dart';
4 +import 'package:cw_core/currency_for_wallet_type.dart';
5 import 'package:cw_core/wallet_type.dart';
6 import 'package:mobx/mobx.dart';
7 import 'package:cake_wallet/entities/template.dart';
@@ -55,6 +56,7 @@ abstract class SendTemplateViewModelBase with Store {
56 _wallet.type != WalletType.haven &&
57 _wallet.type != WalletType.ethereum &&
58 _wallet.type != WalletType.polygon &&
59 + _wallet.type != WalletType.base &&
60 _wallet.type != WalletType.solana &&
61 _wallet.type != WalletType.tron;
62
lib/view_model/send/send_view_model.dart
+7
@@ -1,5 +1,6 @@
1 import 'dart:async';
2
3 +import 'package:cake_wallet/base/base.dart';
4 import 'package:cake_wallet/bitcoin/bitcoin.dart';
5 import 'package:cake_wallet/core/address_validator.dart';
6 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
46 import 'package:cake_wallet/wownero/wownero.dart';
47 import 'package:cake_wallet/zano/zano.dart';
48 import 'package:cw_core/crypto_currency.dart';
49 +import 'package:cw_core/currency_for_wallet_type.dart';
50 import 'package:cw_core/exceptions.dart';
51 import 'package:cw_core/pending_transaction.dart';
52 import 'package:cw_core/sync_status.dart';
@@ -215,6 +217,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
217 switch (type) {
218 case WalletType.ethereum:
219 case WalletType.polygon:
220 + case WalletType.base:
221 case WalletType.tron:
222 case WalletType.solana:
223 return wallet.currency;
@@ -708,6 +711,9 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
711 case WalletType.polygon:
712 return polygon!.createPolygonTransactionCredentials(outputs,
713 priority: priority!, currency: selectedCryptoCurrency);
714 + case WalletType.base:
715 + return base!.createBaseTransactionCredentials(outputs,
716 + priority: priority!, currency: selectedCryptoCurrency);
717 case WalletType.solana:
718 return solana!
719 .createSolanaTransactionCredentials(outputs, currency: selectedCryptoCurrency);
@@ -833,6 +839,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
839 }
840 if (walletType == WalletType.ethereum ||
841 walletType == WalletType.polygon ||
842 + walletType == WalletType.base ||
843 walletType == WalletType.haven) {
844 if (errorMessage.contains('gas required exceeds allowance')) {
845 return S.current.gas_exceeds_allowance;
lib/view_model/settings/privacy_settings_view_model.dart
+12
@@ -1,3 +1,4 @@
1 +import 'package:cake_wallet/base/base.dart';
2 import 'package:cake_wallet/bitcoin/bitcoin.dart';
3 import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
4 import 'package:cake_wallet/entities/exchange_api_mode.dart';
@@ -75,6 +76,9 @@ abstract class PrivacySettingsViewModelBase with Store {
76 @computed
77 bool get usePolygonScan => _settingsStore.usePolygonScan;
78
79 + @computed
80 + bool get useBaseScan => _settingsStore.useBaseScan;
81 +
82 @computed
83 bool get useTronGrid => _settingsStore.useTronGrid;
84
@@ -112,6 +116,8 @@ abstract class PrivacySettingsViewModelBase with Store {
116
117 bool get canUsePolygonScan => _wallet.type == WalletType.polygon;
118
119 + bool get canUseBaseScan => _wallet.type == WalletType.base;
120 +
121 bool get canUseTronGrid => _wallet.type == WalletType.tron;
122
123 bool get canUseMempoolFeeAPI => _wallet.type == WalletType.bitcoin;
@@ -177,6 +183,12 @@ abstract class PrivacySettingsViewModelBase with Store {
183 polygon!.updatePolygonScanUsageState(_wallet, value);
184 }
185
186 + @action
187 + void setUseBaseScan(bool value) {
188 + _settingsStore.useBaseScan = value;
189 + base!.updateBaseScanUsageState(_wallet, value);
190 + }
191 +
192 @action
193 void setUseTronGrid(bool value) {
194 _settingsStore.useTronGrid = value;
lib/view_model/transaction_details_view_model.dart
+58
@@ -1,5 +1,6 @@
1 import 'package:cake_wallet/tron/tron.dart';
2 import 'package:cake_wallet/wownero/wownero.dart';
3 +import 'package:cw_core/currency_for_wallet_type.dart';
4 import 'package:cw_core/utils/print_verbose.dart';
5 import 'package:cw_core/wallet_base.dart';
6 import 'package:cw_core/transaction_info.dart';
@@ -73,6 +74,9 @@ abstract class TransactionDetailsViewModelBase with Store {
74 case WalletType.polygon:
75 _addPolygonListItems(tx, dateFormat);
76 break;
77 + case WalletType.base:
78 + _addBaseListItems(tx, dateFormat);
79 + break;
80 case WalletType.solana:
81 _addSolanaListItems(tx, dateFormat);
82 break;
@@ -198,6 +202,8 @@ abstract class TransactionDetailsViewModelBase with Store {
202 return 'https://${wallet.isTestnet ? "testnet" : "dcrdata"}.decred.org/tx/${txId.split(':')[0]}';
203 case WalletType.dogecoin:
204 return 'https://blockchair.com/dogecoin/transaction/${txId}';
205 + case WalletType.base:
206 + return 'https://basescan.org/tx/${txId}';
207 case WalletType.none:
208 return '';
209 }
@@ -233,6 +239,8 @@ abstract class TransactionDetailsViewModelBase with Store {
239 return S.current.view_transaction_on + 'explorer.zano.org';
240 case WalletType.decred:
241 return S.current.view_transaction_on + 'dcrdata.decred.org';
242 + case WalletType.base:
243 + return S.current.view_transaction_on + 'basescan.org';
244 case WalletType.none:
245 return '';
246 }
@@ -548,6 +556,56 @@ abstract class TransactionDetailsViewModelBase with Store {
556 items.addAll(_items);
557 }
558
559 + void _addBaseListItems(TransactionInfo tx, DateFormat dateFormat) {
560 + final _items = [
561 + StandartListItem(
562 + title: S.current.transaction_details_transaction_id,
563 + value: tx.txHash,
564 + key: ValueKey('standard_list_item_transaction_details_id_key'),
565 + ),
566 + StandartListItem(
567 + title: S.current.transaction_details_date,
568 + value: dateFormat.format(tx.date),
569 + key: ValueKey('standard_list_item_transaction_details_date_key'),
570 + ),
571 + StandartListItem(
572 + title: S.current.confirmations,
573 + value: tx.confirmations.toString(),
574 + key: ValueKey('standard_list_item_transaction_confirmations_key'),
575 + ),
576 + StandartListItem(
577 + title: S.current.transaction_details_height,
578 + value: '${tx.height}',
579 + key: ValueKey('standard_list_item_transaction_details_height_key'),
580 + ),
581 + StandartListItem(
582 + title: S.current.transaction_details_amount,
583 + value: tx.amountFormatted(),
584 + key: ValueKey('standard_list_item_transaction_details_amount_key'),
585 + ),
586 + if (tx.feeFormatted()?.isNotEmpty ?? false)
587 + StandartListItem(
588 + title: S.current.transaction_details_fee,
589 + value: tx.feeFormatted()!,
590 + key: ValueKey('standard_list_item_transaction_details_fee_key'),
591 + ),
592 + if (showRecipientAddress && tx.to != null && tx.direction == TransactionDirection.outgoing)
593 + StandartListItem(
594 + title: S.current.transaction_details_recipient_address,
595 + value: tx.to!,
596 + key: ValueKey('standard_list_item_transaction_details_recipient_address_key'),
597 + ),
598 + if (tx.direction == TransactionDirection.incoming && tx.from != null)
599 + StandartListItem(
600 + title: S.current.transaction_details_source_address,
601 + value: tx.from!,
602 + key: ValueKey('standard_list_item_transaction_details_source_address_key'),
603 + ),
604 + ];
605 +
606 + items.addAll(_items);
607 + }
608 +
609 void _addSolanaListItems(TransactionInfo tx, DateFormat dateFormat) {
610 final _items = [
611 StandartListItem(
lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
+18 -2
@@ -1,6 +1,7 @@
1 import 'dart:developer' as dev;
2 import 'dart:core';
3
4 +import 'package:cake_wallet/base/base.dart';
5 import 'package:cake_wallet/bitcoin/bitcoin.dart';
6 import 'package:cake_wallet/core/fiat_conversion_service.dart';
7 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
31 import 'package:cake_wallet/wownero/wownero.dart';
32 import 'package:cw_core/amount_converter.dart';
33 import 'package:cw_core/currency.dart';
34 +import 'package:cw_core/currency_for_wallet_type.dart';
35 import 'package:cw_core/wallet_type.dart';
36 import 'package:intl/intl.dart';
37 import 'package:mobx/mobx.dart';
@@ -38,7 +40,6 @@ part 'wallet_address_list_view_model.g.dart';
40
41 class WalletAddressListViewModel = WalletAddressListViewModelBase with _$WalletAddressListViewModel;
42
41 -
43 abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewModel with Store {
44 WalletAddressListViewModelBase({
45 required AppStore appStore,
@@ -143,6 +144,8 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
144 return DecredURI(amount: amount, address: address.address);
145 case WalletType.dogecoin:
146 return DogeURI(amount: amount, address: address.address);
147 + case WalletType.base:
148 + return BaseURI(amount: amount, address: address.address);
149 case WalletType.none:
150 throw Exception('Unexpected type: ${type.toString()}');
151 }
@@ -263,6 +266,12 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
266 addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
267 }
268
269 + if (wallet.type == WalletType.base) {
270 + final primaryAddress = base!.getAddress(wallet);
271 +
272 + addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
273 + }
274 +
275 if (wallet.type == WalletType.solana) {
276 final primaryAddress = solana!.getAddress(wallet);
277
@@ -435,7 +444,12 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
444 'assets/images/zano_icon.svg',
445 'assets/images/more_tokens.svg',
446 ];
438 -
447 + case WalletType.base:
448 + return [
449 + 'assets/images/eth_icon.svg',
450 + 'assets/images/usdc_icon.svg',
451 + 'assets/images/more_tokens.svg',
452 + ];
453 default:
454 return [];
455 }
@@ -457,6 +471,8 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
471 return 'assets/images/trx_chain_mono.svg';
472 case WalletType.zano:
473 return 'assets/images/zano_chain_mono.svg';
474 + case WalletType.base:
475 + return 'assets/images/base_chain_mono.svg';
476 default:
477 return 'assets/images/eth_chain_mono.svg';
478 }
lib/view_model/wallet_keys_view_model.dart
+3
@@ -126,6 +126,7 @@ abstract class WalletKeysViewModelBase with Store {
126 break;
127 case WalletType.ethereum:
128 case WalletType.polygon:
129 + case WalletType.base:
130 case WalletType.solana:
131 case WalletType.tron:
132 items.addAll([
@@ -252,6 +253,8 @@ abstract class WalletKeysViewModelBase with Store {
253 return 'banano-wallet';
254 case WalletType.polygon:
255 return 'polygon-wallet';
256 + case WalletType.base:
257 + return 'base-wallet';
258 case WalletType.solana:
259 return 'solana-wallet';
260 case WalletType.tron:
lib/view_model/wallet_new_vm.dart
+8
@@ -1,3 +1,4 @@
1 +import 'package:cake_wallet/base/base.dart';
2 import 'package:cake_wallet/core/new_wallet_arguments.dart';
3 import 'package:cake_wallet/dogecoin/dogecoin.dart';
4 import 'package:cake_wallet/ethereum/ethereum.dart';
@@ -90,6 +91,13 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
91 mnemonic: newWalletArguments!.mnemonic,
92 passphrase: passphrase,
93 );
94 + case WalletType.base:
95 + return base!.createBaseNewWalletCredentials(
96 + name: name,
97 + password: walletPassword,
98 + mnemonic: newWalletArguments!.mnemonic,
99 + passphrase: passphrase,
100 + );
101 case WalletType.bitcoinCash:
102 return bitcoinCash!.createBitcoinCashNewWalletCredentials(
103 name: name,
lib/view_model/wallet_restore_view_model.dart
+16
@@ -1,3 +1,4 @@
1 +import 'package:cake_wallet/base/base.dart';
2 import 'package:cake_wallet/bitcoin/bitcoin.dart';
3 import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
4 import 'package:cake_wallet/core/generate_wallet_password.dart';
@@ -51,6 +52,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
52 case WalletType.haven:
53 case WalletType.ethereum:
54 case WalletType.polygon:
55 + case WalletType.base:
56 case WalletType.decred:
57 case WalletType.bitcoin:
58 availableModes = [WalletRestoreMode.seed, WalletRestoreMode.keys];
@@ -90,6 +92,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
92 late final bool hasRestoreFromPrivateKey = [
93 WalletType.ethereum,
94 WalletType.polygon,
95 + WalletType.base,
96 WalletType.nano,
97 WalletType.banano,
98 WalletType.solana,
@@ -174,6 +177,13 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
177 password: password,
178 passphrase: passphrase,
179 );
180 + case WalletType.base:
181 + return base!.createBaseRestoreWalletFromSeedCredentials(
182 + name: name,
183 + mnemonic: seed,
184 + password: password,
185 + passphrase: passphrase,
186 + );
187 case WalletType.solana:
188 return solana!.createSolanaRestoreWalletFromSeedCredentials(
189 name: name,
@@ -261,6 +271,12 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
271 password: password,
272 privateKey: options['private_key'] as String,
273 );
274 + case WalletType.base:
275 + return base!.createBaseRestoreWalletFromPrivateKey(
276 + name: name,
277 + password: password,
278 + privateKey: options['private_key'] as String,
279 + );
280 case WalletType.solana:
281 return solana!.createSolanaRestoreWalletFromPrivateKey(
282 name: name,
model_generator.sh
+1 -1
@@ -1,7 +1,7 @@
1 #!/bin/bash
2 set -x -e
3
4 -for cwcoin in cw_{core,evm,monero,bitcoin,nano,bitcoin_cash,solana,tron,wownero,zano,decred,dogecoin}
4 +for cwcoin in cw_{core,evm,monero,bitcoin,nano,bitcoin_cash,solana,tron,wownero,zano,decred,dogecoin,base}
5 do
6 if [[ "x$1" == "xasync" ]];
7 then
pubspec_base.yaml
+1
@@ -235,6 +235,7 @@ flutter:
235 - assets/zano_node_list.yml
236 - assets/decred_node_list.yml
237 - assets/dogecoin_electrum_server_list.yml
238 + - assets/base_node_list.yml
239 - assets/text/
240 - assets/faq/
241 - assets/animation/
res/values/strings_ar.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "كلمة مرور النسخ الاحتياطي",
89 "balance": "توازن",
90 "balance_page": "صفحة التوازن",
91 + "basescan_history": "تاريخ باسيسكان",
92 "bill_amount": "مبلغ الفاتورة",
93 "billing_address_info": "إذا طُلب منك عنوان إرسال فواتير ، فأدخل عنوان الشحن الخاص بك",
94 "biometric_auth_reason": "امسح بصمة إصبعك للمصادقة",
res/values/strings_bg.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "Парола за възстановяване",
89 "balance": "Баланс",
90 "balance_page": "Страница за баланс",
91 + "basescan_history": "История на басескан",
92 "bill_amount": "Искана сума",
93 "billing_address_info": "Ако Ви попитат за билинг адрес, въведето своя адрес за доставка",
94 "biometric_auth_reason": "Сканирайте своя пръстов отпечатък",
res/values/strings_cs.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "Heslo pro zálohy",
89 "balance": "Zůstatek",
90 "balance_page": "Stránka zůstatku",
91 + "basescan_history": "Historie basescan",
92 "bill_amount": "Účtovaná částka",
93 "billing_address_info": "Při dotazu na fakturační adresu, zadejte svou doručovací adresu",
94 "biometric_auth_reason": "Naskenujte otisk prstu pro ověření",
res/values/strings_de.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "Passwort sichern",
89 "balance": "Saldo",
90 "balance_page": "Guthaben-Seite",
91 + "basescan_history": "Baseskangeschichte",
92 "bill_amount": "Rechnungsbetrag",
93 "billing_address_info": "Wenn Sie nach einer Rechnungsadresse gefragt werden, geben Sie bitte Ihre Lieferadresse an",
94 "biometric_auth_reason": "Scannen Sie Ihren Fingerabdruck zur Authentifizierung",
res/values/strings_en.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "Backup password",
89 "balance": "Balance",
90 "balance_page": "Balance Page",
91 + "basescan_history": "BaseScan History",
92 "bill_amount": "Bill Amount",
93 "billing_address_info": "If asked for a billing address, provide your shipping address",
94 "biometric_auth_reason": "Scan your fingerprint to authenticate",
res/values/strings_es.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "Contraseña de respaldo",
89 "balance": "Balance",
90 "balance_page": "Página de saldo",
91 + "basescan_history": "Historia de Basescan",
92 "bill_amount": "Importe de la factura",
93 "billing_address_info": "Si se le solicita una dirección de facturación, proporcione su dirección de envío",
94 "biometric_auth_reason": "Escanee su huella digital para autenticar",
res/values/strings_fr.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "Mot de passe de sauvegarde",
89 "balance": "Solde",
90 "balance_page": "Page Solde",
91 + "basescan_history": "HISTOIRE DE BASESCAN",
92 "bill_amount": "Montant de la facture",
93 "billing_address_info": "Si une adresse de facturation vous est demandée, indiquez votre adresse de livraison",
94 "biometric_auth_reason": "Scannez votre empreinte digitale pour vous authentifier",
res/values/strings_ha.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "Ajiyayyen kalmar sirri",
89 "balance": "Ma'auni",
90 "balance_page": "Ma'auni Page",
91 + "basescan_history": "Tarihin Basescan",
92 "bill_amount": "Adadin Bill",
93 "billing_address_info": "Idan an nemi adireshin biyan kuɗi, samar da adireshin jigilar kaya",
94 "biometric_auth_reason": "Duba hoton yatsa don tantancewa",
res/values/strings_hi.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "बैकअप पासवर्ड",
89 "balance": "संतुलन",
90 "balance_page": "बैलेंस पेज",
91 + "basescan_history": "बेसस्कैन इतिहास",
92 "bill_amount": "बिल राशि",
93 "billing_address_info": "यदि बिलिंग पता मांगा जाए, तो अपना शिपिंग पता प्रदान करें",
94 "biometric_auth_reason": "प्रमाणित करने के लिए अपने फ़िंगरप्रिंट को स्कैन करें",
res/values/strings_hr.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "Lozinka za sigurnosnu kopiju",
89 "balance": "Uravnotežiti",
90 "balance_page": "Stranica sa stanjem",
91 + "basescan_history": "Basescan povijest",
92 "bill_amount": "Iznos računa",
93 "billing_address_info": "Ako se od vas zatraži adresa za naplatu, navedite svoju adresu za dostavu",
94 "biometric_auth_reason": "Skenirajte svoj otisak prsta za autentifikaciju",
res/values/strings_hy.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "Կրկնօրինակի գաղտնաբառ",
89 "balance": "Հաշվեկշիռ",
90 "balance_page": "Հաշվեկշռի էջ",
91 + "basescan_history": "Բասեսկանի պատմություն",
92 "bill_amount": "Հաշիվը",
93 "billing_address_info": "Եթե խնդրեն հաշվեվճարի հասցե, ապա տրամադրեք ձեր առաքման հասցեն",
94 "biometric_auth_reason": "Սկանավեք ձեր մատնահետքը նույնականացման համար",
res/values/strings_id.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "Kata sandi cadangan",
89 "balance": "Keseimbangan",
90 "balance_page": "Halaman Saldo",
91 + "basescan_history": "Sejarah Basescan",
92 "bill_amount": "Jumlah Tagihan",
93 "billing_address_info": "Jika diminta alamat billing, berikan alamat pengiriman Anda",
94 "biometric_auth_reason": "Pindai sidik jari Anda untuk mengautentikasi",
res/values/strings_it.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "Backup password",
89 "balance": "Saldo",
90 "balance_page": "Pagina di saldo",
91 + "basescan_history": "Storia di Basescan",
92 "bill_amount": "Importo della ricevuta",
93 "billing_address_info": "Se ti viene richiesto un indirizzo di fatturazione, fornisci il tuo indirizzo di spedizione",
94 "biometric_auth_reason": "Scansiona la tua impronta per autenticarti",
res/values/strings_ja.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "バックアップパスワード",
89 "balance": "バランス",
90 "balance_page": "残高ページ",
91 + "basescan_history": "Basescanの歴史",
92 "bill_amount": "請求額",
93 "billing_address_info": "請求先住所を尋ねられた場合は、配送先住所を入力してください",
94 "biometric_auth_reason": "प指紋をスキャンして認証する",
res/values/strings_ko.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "백업 비밀번호",
89 "balance": "잔액",
90 "balance_page": "잔액 페이지",
91 + "basescan_history": "바스 스칸 역사",
92 "bill_amount": "청구 금액",
93 "billing_address_info": "청구지 주소를 묻는 경우 배송 주소를 제공하세요",
94 "biometric_auth_reason": "인증하려면 지문을 스캔하세요",
res/values/strings_my.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "စကားဝှက်ကို အရန်သိမ်းဆည်းပါ။",
89 "balance": "လက်ကျန်ငေှ",
90 "balance_page": "လက်ကျန်စာမျက်နှာ",
91 + "basescan_history": "Bascan သမိုင်း",
92 "bill_amount": "ဘီလ်ပမာဏ",
93 "billing_address_info": "ငွေပေးချေရမည့်လိပ်စာကို တောင်းဆိုပါက သင့်ပို့ဆောင်ရေးလိပ်စာကို ပေးပါ။",
94 "biometric_auth_reason": "စစ်မှန်ကြောင်းအထောက်အထားပြရန် သင့်လက်ဗွေကို စကန်ဖတ်ပါ။",
res/values/strings_nl.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "Reservewachtwoord",
89 "balance": "Evenwicht",
90 "balance_page": "Saldo pagina",
91 + "basescan_history": "BASESCAN GESCHIEDENIS",
92 "bill_amount": "Bill bedrag",
93 "billing_address_info": "Als u om een ​​factuuradres wordt gevraagd, geef dan uw verzendadres op",
94 "biometric_auth_reason": "Scan uw vingerafdruk om te verifiëren",
res/values/strings_pl.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "Hasło kpoii zapasowej",
89 "balance": "Saldo",
90 "balance_page": "Strona salda",
91 + "basescan_history": "Historia Basescan",
92 "bill_amount": "Kwota rachunku",
93 "billing_address_info": "Jeśli zostaniesz poproszony o podanie adresu rozliczeniowego, podaj swój adres wysyłki",
94 "biometric_auth_reason": "Zeskanuj swój odcisk palca, aby uwierzytelnić",
res/values/strings_pt.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "Senha de backup",
89 "balance": "Equilíbrio",
90 "balance_page": "Página de saldo",
91 + "basescan_history": "História de Basescan",
92 "bill_amount": "Valor da conta",
93 "billing_address_info": "Se for solicitado um endereço de cobrança, forneça seu endereço de entrega",
94 "biometric_auth_reason": "Digitalize sua impressão digital para autenticar",
res/values/strings_ru.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "Пароль резервной копии",
89 "balance": "Баланс",
90 "balance_page": "Страница баланса",
91 + "basescan_history": "Басесканская история",
92 "bill_amount": "Сумма счета",
93 "billing_address_info": "Если вас попросят указать платежный адрес, укажите адрес доставки",
94 "biometric_auth_reason": "Отсканируйте свой отпечаток пальца для аутентификации",
res/values/strings_th.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "รหัสผ่านสำรองข้อมูล",
89 "balance": "สมดุล",
90 "balance_page": "หน้ายอดคงเหลือ",
91 + "basescan_history": "ประวัติศาสตร์ Basescan",
92 "bill_amount": "จำนวนบิล",
93 "billing_address_info": "ถ้าถูกร้องขอที่อยู่สำหรับการวางบิล ให้ใช้ที่อยู่จัดส่งของคุณ",
94 "biometric_auth_reason": "สแกนลายนิ้วมือของคุณเพื่อยืนยันตัวตน",
res/values/strings_tl.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "Backup na password",
89 "balance": "Balanse",
90 "balance_page": "Pahina ng Balanse",
91 + "basescan_history": "Kasaysayan ng Basescan",
92 "bill_amount": "Halaga ng Bill",
93 "billing_address_info": "Kung humihingi ng billing address, ibigay ang iyong shipping address",
94 "biometric_auth_reason": "I-scan ang iyong fingerprint para ma-authenticate",
res/values/strings_tr.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "Yedek parolası",
89 "balance": "Denge",
90 "balance_page": "Bakiye Sayfası",
91 + "basescan_history": "Basescan tarihi",
92 "bill_amount": "Fatura Tutarı",
93 "billing_address_info": "Eğer fatura adresi istenirse, kargo adresinizi girin",
94 "biometric_auth_reason": "Kimlik doğrulaması için parmak izini okutun",
res/values/strings_uk.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "Пароль резервної копії",
89 "balance": "Балансувати",
90 "balance_page": "Сторінка балансу",
91 + "basescan_history": "Базесканська історія",
92 "bill_amount": "Сума рахунку",
93 "billing_address_info": "Якщо буде запропоновано платіжну адресу, вкажіть свою адресу доставки",
94 "biometric_auth_reason": "Відскануйте свій відбиток пальця для аутентифікації",
res/values/strings_ur.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "بیک اپ پاس ورڈ",
89 "balance": "بقیہ",
90 "balance_page": "بیلنس صفحہ",
91 + "basescan_history": "باسکی تاریخ",
92 "bill_amount": "بل رقم",
93 "billing_address_info": "اگر آپ سے بلنگ کا پتہ پوچھا جائے تو اپنا شپنگ ایڈریس فراہم کریں۔",
94 "biometric_auth_reason": "تصدیق کرنے کے لیے اپنے فنگر پرنٹ کو اسکین کریں۔",
res/values/strings_vi.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "Mật khẩu sao lưu",
89 "balance": "Số dư",
90 "balance_page": "Trang số dư",
91 + "basescan_history": "Lịch sử Basescan",
92 "bill_amount": "Số tiền hóa đơn",
93 "billing_address_info": "Nếu được yêu cầu địa chỉ thanh toán, hãy cung cấp địa chỉ giao hàng của bạn",
94 "biometric_auth_reason": "Quét vân tay để xác thực",
res/values/strings_yo.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "Ṣẹ̀dà ọ̀rọ̀ aṣínà",
89 "balance": "Iwọntunwọnsi",
90 "balance_page": "Oju-iwe iwọntunwọnsi",
91 + "basescan_history": "Itan Basiki",
92 "bill_amount": "Iye ìwé owó",
93 "billing_address_info": "Tí ọlọ́jà bá bèèrè àdírẹ́sì sísan yín, fún òun ni àdírẹ́sì t'á ránṣẹ́ káàdì yìí sí",
94 "biometric_auth_reason": "Ya ìka ọwọ́ yín láti ṣe ìfẹ̀rílàdí",
res/values/strings_zh.arb
+1
@@ -88,6 +88,7 @@
88 "backup_password": "备份密码",
89 "balance": "平衡",
90 "balance_page": "余额页",
91 + "basescan_history": "Basescan历史",
92 "bill_amount": "账单金额",
93 "billing_address_info": "如果要求提供帐单地址,请提供您的送货地址",
94 "biometric_auth_reason": "扫描指纹进行身份认证",
scripts/android/pubspec_gen.sh
+1 -1
@@ -10,7 +10,7 @@ case $APP_ANDROID_TYPE in
10 CONFIG_ARGS="--monero"
11 ;;
12 $CAKEWALLET)
13 - CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --zano --decred --dogecoin"
13 + CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --zano --decred --dogecoin --base"
14 ;;
15 esac
16
scripts/build_bitbox_flutter.sh
+1 -1
@@ -2,7 +2,7 @@
2
3 git clone https://github.com/konstantinullrich/bitbox_flutter
4 cd bitbox_flutter
5 -bash ./build_bindings.sh --dont-install
5 +bash ./build_bindings.sh
6
7 FILE=api.aar
8 if [ -f "$FILE" ]; then
scripts/ios/app_config.sh
+1 -1
@@ -31,7 +31,7 @@ case $APP_IOS_TYPE in
31 ;;
32
33 $CAKEWALLET)
34 - CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --zano --decred --dogecoin"
34 + CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --zano --decred --dogecoin --base"
35 ;;
36 esac
37
scripts/linux/app_config.sh
+1 -1
@@ -13,7 +13,7 @@ CONFIG_ARGS=""
13
14 case $APP_LINUX_TYPE in
15 $CAKEWALLET)
16 - CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --dogecoin";;
16 + CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --dogecoin --base";;
17 esac
18
19 cp -rf pubspec_description.yaml pubspec.yaml
scripts/macos/app_config.sh
+1 -1
@@ -36,7 +36,7 @@ case $APP_MACOS_TYPE in
36 $MONERO_COM)
37 CONFIG_ARGS="--monero";;
38 $CAKEWALLET)
39 - CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --dogecoin";;
39 + CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --dogecoin --base";;
40 esac
41
42 cp -rf pubspec_description.yaml pubspec.yaml
tool/configure.dart
+146
@@ -12,6 +12,7 @@ const wowneroOutputPath = 'lib/wownero/wownero.dart';
12 const zanoOutputPath = 'lib/zano/zano.dart';
13 const decredOutputPath = 'lib/decred/decred.dart';
14 const dogecoinOutputPath = 'lib/dogecoin/dogecoin.dart';
15 +const baseOutputPath = 'lib/base/base.dart';
16 const walletTypesPath = 'lib/wallet_types.g.dart';
17 const secureStoragePath = 'lib/core/secure_storage.dart';
18 const pubspecDefaultPath = 'pubspec_default.yaml';
@@ -32,6 +33,7 @@ Future<void> main(List<String> args) async {
33 final hasZano = args.contains('${prefix}zano');
34 final hasDecred = args.contains('${prefix}decred');
35 final hasDogecoin = args.contains('${prefix}dogecoin');
36 + final hasBase = args.contains('${prefix}base');
37 final excludeFlutterSecureStorage = args.contains('${prefix}excludeFlutterSecureStorage');
38
39 await generateBitcoin(hasBitcoin);
@@ -47,6 +49,7 @@ Future<void> main(List<String> args) async {
49 // await generateBanano(hasEthereum);
50 await generateDecred(hasDecred);
51 await generateDogecoin(hasDogecoin);
52 + await generateBase(hasBase);
53
54 await generatePubspec(
55 hasMonero: hasMonero,
@@ -63,6 +66,7 @@ Future<void> main(List<String> args) async {
66 hasZano: hasZano,
67 hasDecred: hasDecred,
68 hasDogecoin: hasDogecoin,
69 + hasBase: hasBase,
70 );
71 await generateWalletTypes(
72 hasMonero: hasMonero,
@@ -78,6 +82,7 @@ Future<void> main(List<String> args) async {
82 hasZano: hasZano,
83 hasDecred: hasDecred,
84 hasDogecoin: hasDogecoin,
85 + hasBase: hasBase,
86 );
87 await injectSecureStorage(!excludeFlutterSecureStorage);
88 }
@@ -1497,6 +1502,132 @@ abstract class DogeCoin {
1502 await outputFile.writeAsString(output);
1503 }
1504
1505 +Future<void> generateBase(bool hasImplementation) async {
1506 + final outputFile = File(baseOutputPath);
1507 + const baseCommonHeaders = """
1508 +import 'package:cake_wallet/view_model/send/output.dart';
1509 +import 'package:cw_core/crypto_currency.dart';
1510 +import 'package:cw_core/erc20_token.dart';
1511 +import 'package:cw_core/hardware/hardware_account_data.dart';
1512 +import 'package:cw_core/hardware/hardware_wallet_service.dart';
1513 +import 'package:cw_core/output_info.dart';
1514 +import 'package:cw_core/pending_transaction.dart';
1515 +import 'package:cw_core/transaction_info.dart';
1516 +import 'package:cw_core/transaction_priority.dart';
1517 +import 'package:cw_core/wallet_base.dart';
1518 +import 'package:cw_core/wallet_credentials.dart';
1519 +import 'package:cw_core/wallet_info.dart';
1520 +import 'package:cw_core/wallet_service.dart';
1521 +import 'package:hive/hive.dart';
1522 +import 'package:ledger_flutter_plus/ledger_flutter_plus.dart' as ledger;
1523 +import 'package:bitbox_flutter/bitbox_flutter.dart' as bitbox;
1524 +import 'package:web3dart/web3dart.dart';
1525 +
1526 +""";
1527 + const baseCWHeaders = """
1528 +import 'package:cw_evm/evm_chain_formatter.dart';
1529 +import 'package:cw_evm/evm_chain_mnemonics.dart';
1530 +import 'package:cw_evm/evm_chain_transaction_credentials.dart';
1531 +import 'package:cw_evm/evm_chain_transaction_info.dart';
1532 +import 'package:cw_evm/evm_chain_transaction_priority.dart';
1533 +import 'package:cw_evm/evm_chain_wallet_creation_credentials.dart';
1534 +import 'package:cw_evm/hardware/evm_chain_ledger_credentials.dart';
1535 +import 'package:cw_evm/hardware/evm_chain_bitbox_credentials.dart';
1536 +import 'package:cw_evm/evm_chain_wallet.dart';
1537 +import 'package:cw_evm/hardware/evm_chain_bitbox_service.dart';
1538 +import 'package:cw_evm/hardware/evm_chain_ledger_service.dart';
1539 +
1540 +import 'package:cw_base/base_client.dart';
1541 +import 'package:cw_base/base_wallet.dart';
1542 +import 'package:cw_base/base_wallet_service.dart';
1543 +import 'package:cw_base/default_base_erc20_tokens.dart';
1544 +import 'package:eth_sig_util/util/utils.dart';
1545 +
1546 +""";
1547 + const baseCwPart = "part 'cw_base.dart';";
1548 + const baseContent = """
1549 +abstract class Base {
1550 + List<String> getBaseWordList(String language);
1551 + WalletService createBaseWalletService(Box<WalletInfo> walletInfoSource, bool isDirect);
1552 + WalletCredentials createBaseNewWalletCredentials(
1553 + {required String name,
1554 + WalletInfo? walletInfo,
1555 + String? password,
1556 + String? mnemonic,
1557 + String? passphrase});
1558 + WalletCredentials createBaseRestoreWalletFromSeedCredentials(
1559 + {required String name,
1560 + required String mnemonic,
1561 + required String password,
1562 + String? passphrase});
1563 + WalletCredentials createBaseRestoreWalletFromPrivateKey(
1564 + {required String name, required String privateKey, required String password});
1565 + WalletCredentials createBaseHardwareWalletCredentials(
1566 + {required String name, required HardwareAccountData hwAccountData, WalletInfo? walletInfo});
1567 + String getAddress(WalletBase wallet);
1568 + String getPrivateKey(WalletBase wallet);
1569 + String getPublicKey(WalletBase wallet);
1570 + TransactionPriority getDefaultTransactionPriority();
1571 + TransactionPriority getBaseTransactionPrioritySlow();
1572 + List<TransactionPriority> getTransactionPriorities();
1573 + TransactionPriority deserializeBaseTransactionPriority(int raw);
1574 +
1575 + Object createBaseTransactionCredentials(
1576 + List<Output> outputs, {
1577 + required TransactionPriority priority,
1578 + required CryptoCurrency currency,
1579 + int? feeRate,
1580 + });
1581 +
1582 + Object createBaseTransactionCredentialsRaw(
1583 + List<OutputInfo> outputs, {
1584 + TransactionPriority? priority,
1585 + required CryptoCurrency currency,
1586 + required int feeRate,
1587 + });
1588 +
1589 + int formatterBaseParseAmount(String amount);
1590 + double formatterBaseAmountToDouble(
1591 + {TransactionInfo? transaction, BigInt? amount, int exponent = 18});
1592 + List<Erc20Token> getERC20Currencies(WalletBase wallet);
1593 + Future<void> addErc20Token(WalletBase wallet, CryptoCurrency token);
1594 + Future<void> deleteErc20Token(WalletBase wallet, CryptoCurrency token);
1595 + Future<void> removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token);
1596 + Future<Erc20Token?> getErc20Token(WalletBase wallet, String contractAddress);
1597 +
1598 + Future<PendingTransaction> createTokenApproval(WalletBase wallet, BigInt amount, String spender,
1599 + CryptoCurrency token, TransactionPriority priority);
1600 +
1601 + CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction);
1602 + void updateBaseScanUsageState(WalletBase wallet, bool isEnabled);
1603 + Web3Client? getWeb3Client(WalletBase wallet);
1604 + String getTokenAddress(CryptoCurrency asset);
1605 +
1606 + void setHardwareWalletService(WalletBase wallet, HardwareWalletService service);
1607 + HardwareWalletService getLedgerHardwareWalletService(ledger.LedgerConnection connection);
1608 + HardwareWalletService getBitboxHardwareWalletService(bitbox.BitboxManager manager);
1609 + List<String> getDefaultTokenContractAddresses();
1610 + bool isTokenAlreadyAdded(WalletBase wallet, String contractAddress);
1611 +}
1612 + """;
1613 +
1614 + const baseEmptyDefinition = 'Base? base;\n';
1615 + const baseCWDefinition = 'Base? base = CWBase();\n';
1616 +
1617 + final output = '$baseCommonHeaders\n' +
1618 + (hasImplementation ? '$baseCWHeaders\n' : '\n') +
1619 + (hasImplementation ? '$baseCwPart\n\n' : '\n') +
1620 + (hasImplementation ? baseCWDefinition : baseEmptyDefinition) +
1621 + '\n' +
1622 + baseContent;
1623 +
1624 + if (outputFile.existsSync()) {
1625 + await outputFile.delete();
1626 + }
1627 +
1628 + await outputFile.writeAsString(output);
1629 +}
1630 +
1631 Future<void> generatePubspec({
1632 required bool hasMonero,
1633 required bool hasBitcoin,
@@ -1512,6 +1643,7 @@ Future<void> generatePubspec({
1643 required bool hasZano,
1644 required bool hasDecred,
1645 required bool hasDogecoin,
1646 + required bool hasBase,
1647 }) async {
1648 const cwCore = """
1649 cw_core:
@@ -1580,6 +1712,11 @@ Future<void> generatePubspec({
1712 cw_dogecoin:
1713 path: ./cw_dogecoin
1714 """;
1715 + const cwBase = """
1716 + cw_base:
1717 + path: ./cw_base
1718 + """;
1719 +
1720 final inputFile = File(pubspecOutputPath);
1721 final inputText = await inputFile.readAsString();
1722 final inputLines = inputText.split('\n');
@@ -1649,6 +1786,10 @@ Future<void> generatePubspec({
1786 output += '\n$cwDogecoin';
1787 }
1788
1789 + if (hasBase) {
1790 + output += '\n$cwBase';
1791 + }
1792 +
1793 final outputLines = output.split('\n');
1794 inputLines.insertAll(dependenciesIndex + 1, outputLines);
1795 final outputContent = inputLines.join('\n');
@@ -1675,6 +1816,7 @@ Future<void> generateWalletTypes({
1816 required bool hasZano,
1817 required bool hasDecred,
1818 required bool hasDogecoin,
1819 + required bool hasBase,
1820 }) async {
1821 final walletTypesFile = File(walletTypesPath);
1822
@@ -1706,6 +1848,10 @@ Future<void> generateWalletTypes({
1848 outputContent += '\tWalletType.dogecoin,\n';
1849 }
1850
1851 + if (hasBase) {
1852 + outputContent += '\tWalletType.base,\n';
1853 + }
1854 +
1855 if (hasBitcoinCash) {
1856 outputContent += '\tWalletType.bitcoinCash,\n';
1857 }
tool/utils/secret_key.dart
+2 -1
@@ -55,8 +55,8 @@ class SecretKey {
55 SecretKey('litecoinTestWalletSeeds', () => ''),
56 SecretKey('bitcoinCashTestWalletSeeds', () => ''),
57 SecretKey('polygonTestWalletSeeds', () => ''),
58 + SecretKey('baseTestWalletSeeds', () => ''),
59 SecretKey('solanaTestWalletSeeds', () => ''),
59 - SecretKey('polygonTestWalletSeeds', () => ''),
60 SecretKey('tronTestWalletSeeds', () => ''),
61 SecretKey('nanoTestWalletSeeds', () => ''),
62 SecretKey('wowneroTestWalletSeeds', () => ''),
@@ -66,6 +66,7 @@ class SecretKey {
66 SecretKey('litecoinTestWalletReceiveAddress', () => ''),
67 SecretKey('bitcoinCashTestWalletReceiveAddress', () => ''),
68 SecretKey('polygonTestWalletReceiveAddress', () => ''),
69 + SecretKey('baseTestWalletReceiveAddress', () => ''),
70 SecretKey('solanaTestWalletReceiveAddress', () => ''),
71 SecretKey('tronTestWalletReceiveAddress', () => ''),
72 SecretKey('nanoTestWalletReceiveAddress', () => ''),