diff --git a/.github/workflows/automated_integration_test.yml b/.github/workflows/automated_integration_test.yml
index 34a4c6b6d..b95b71031 100644
--- a/.github/workflows/automated_integration_test.yml
+++ b/.github/workflows/automated_integration_test.yml
@@ -163,6 +163,7 @@ jobs:
echo "const litecoinTestWalletSeeds = '${{ secrets.LITECOIN_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const bitcoinCashTestWalletSeeds = '${{ secrets.BITCOIN_CASH_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const polygonTestWalletSeeds = '${{ secrets.POLYGON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
+ echo "const baseTestWalletSeeds = '${{ secrets.BASE_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const solanaTestWalletSeeds = '${{ secrets.SOLANA_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const solanaTestWalletSeeds2 = '${{ secrets.SOLANA_TEST_WALLET_SEEDS2 }}';" >> lib/.secrets.g.dart
echo "const tronTestWalletSeeds = '${{ secrets.TRON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
@@ -176,6 +177,7 @@ jobs:
echo "const litecoinTestWalletReceiveAddress = '${{ secrets.LITECOIN_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const bitcoinCashTestWalletReceiveAddress = '${{ secrets.BITCOIN_CASH_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const polygonTestWalletReceiveAddress = '${{ secrets.POLYGON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
+ echo "const baseTestWalletReceiveAddress = '${{ secrets.BASE_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const solanaTestWalletReceiveAddress = '${{ secrets.SOLANA_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const tronTestWalletReceiveAddress = '${{ secrets.TRON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const nanoTestWalletReceiveAddress = '${{ secrets.NANO_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
diff --git a/.github/workflows/no_restricted_imports.yaml b/.github/workflows/no_restricted_imports.yaml
index 03c3de018..2b07c27cd 100644
--- a/.github/workflows/no_restricted_imports.yaml
+++ b/.github/workflows/no_restricted_imports.yaml
@@ -20,6 +20,7 @@ jobs:
"cw_mweb"
"cw_nano"
"cw_polygon"
+ "cw_base"
"cw_solana"
"cw_tron"
"cw_wownero"
diff --git a/.github/workflows/pr_test_build_android.yml b/.github/workflows/pr_test_build_android.yml
index 5d5b52782..05fb8b993 100644
--- a/.github/workflows/pr_test_build_android.yml
+++ b/.github/workflows/pr_test_build_android.yml
@@ -157,6 +157,7 @@ jobs:
echo "const litecoinTestWalletSeeds = '${{ secrets.LITECOIN_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const bitcoinCashTestWalletSeeds = '${{ secrets.BITCOIN_CASH_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const polygonTestWalletSeeds = '${{ secrets.POLYGON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
+ echo "const baseTestWalletSeeds = '${{ secrets.BASE_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const solanaTestWalletSeeds = '${{ secrets.SOLANA_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const tronTestWalletSeeds = '${{ secrets.TRON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const nanoTestWalletSeeds = '${{ secrets.NANO_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
@@ -168,6 +169,7 @@ jobs:
echo "const litecoinTestWalletReceiveAddress = '${{ secrets.LITECOIN_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const bitcoinCashTestWalletReceiveAddress = '${{ secrets.BITCOIN_CASH_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const polygonTestWalletReceiveAddress = '${{ secrets.POLYGON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
+ echo "const baseTestWalletReceiveAddress = '${{ secrets.BASE_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const solanaTestWalletReceiveAddress = '${{ secrets.SOLANA_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const tronTestWalletReceiveAddress = '${{ secrets.TRON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const nanoTestWalletReceiveAddress = '${{ secrets.NANO_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
diff --git a/.github/workflows/pr_test_build_linux.yml b/.github/workflows/pr_test_build_linux.yml
index 40318d36c..e9f380926 100644
--- a/.github/workflows/pr_test_build_linux.yml
+++ b/.github/workflows/pr_test_build_linux.yml
@@ -150,6 +150,7 @@ jobs:
echo "const litecoinTestWalletSeeds = '${{ secrets.LITECOIN_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const bitcoinCashTestWalletSeeds = '${{ secrets.BITCOIN_CASH_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const polygonTestWalletSeeds = '${{ secrets.POLYGON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
+ echo "const baseTestWalletSeeds = '${{ secrets.BASE_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const solanaTestWalletSeeds = '${{ secrets.SOLANA_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const tronTestWalletSeeds = '${{ secrets.TRON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const nanoTestWalletSeeds = '${{ secrets.NANO_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
@@ -162,6 +163,7 @@ jobs:
echo "const litecoinTestWalletReceiveAddress = '${{ secrets.LITECOIN_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const bitcoinCashTestWalletReceiveAddress = '${{ secrets.BITCOIN_CASH_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const polygonTestWalletReceiveAddress = '${{ secrets.POLYGON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
+ echo "const baseTestWalletReceiveAddress = '${{ secrets.BASE_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const solanaTestWalletReceiveAddress = '${{ secrets.SOLANA_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const tronTestWalletReceiveAddress = '${{ secrets.TRON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const nanoTestWalletReceiveAddress = '${{ secrets.NANO_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
diff --git a/.gitignore b/.gitignore
index 3fa31bd7a..be83f1a62 100644
--- a/.gitignore
+++ b/.gitignore
@@ -141,6 +141,7 @@ lib/wownero/wownero.dart
lib/zano/zano.dart
lib/decred/decred.dart
lib/dogecoin/dogecoin.dart
+lib/base/base.dart
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x.png
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x~ipad.png
diff --git a/README.md b/README.md
index 87654b55f..1266d08a5 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,7 @@ Cake Wallet includes support for several cryptocurrencies, including:
* Litecoin (LTC)
* Bitcoin Cash (BCH)
* Polygon (POL)
+* Base (BASE)
* Solana (SOL)
* Tron (TRX)
* Nano (XNO)
diff --git a/analysis_options.yaml b/analysis_options.yaml
index bd35233ba..bc119a66c 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -23,6 +23,7 @@ analyzer:
lib/tron/cw_tron.dart,
lib/wownero/cw_wownero.dart,
lib/zano/cw_zano.dart,
+ lib/base/cw_base.dart,
]
language:
strict-casts: true
diff --git a/android/app/src/main/AndroidManifestBase.xml b/android/app/src/main/AndroidManifestBase.xml
index 1bc56c1d5..287554cd3 100644
--- a/android/app/src/main/AndroidManifestBase.xml
+++ b/android/app/src/main/AndroidManifestBase.xml
@@ -106,6 +106,9 @@
+
+
+
diff --git a/assets/base_node_list.yml b/assets/base_node_list.yml
new file mode 100644
index 000000000..d135f7aea
--- /dev/null
+++ b/assets/base_node_list.yml
@@ -0,0 +1,15 @@
+-
+ uri: base.nownodes.io
+ is_default: true
+ useSSL: true
+ isEnabledForAutoSwitching: true
+-
+ uri: base.llamarpc.com
+ useSSL: true
+-
+ uri: base-rpc.publicnode.com
+ useSSL: true
+ isEnabledForAutoSwitching: true
+-
+ uri: 1rpc.io/base
+ useSSL: true
\ No newline at end of file
diff --git a/assets/images/base_chain_QR.svg b/assets/images/base_chain_QR.svg
new file mode 100644
index 000000000..7f801d529
--- /dev/null
+++ b/assets/images/base_chain_QR.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/base_chain_mono.svg b/assets/images/base_chain_mono.svg
new file mode 100644
index 000000000..48ebe83a3
--- /dev/null
+++ b/assets/images/base_chain_mono.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/base_icon.png b/assets/images/base_icon.png
new file mode 100644
index 000000000..d7cb8edad
Binary files /dev/null and b/assets/images/base_icon.png differ
diff --git a/cakewallet.bat b/cakewallet.bat
index b9da8762f..8baa910e8 100644
--- a/cakewallet.bat
+++ b/cakewallet.bat
@@ -1,5 +1,5 @@
@echo off
-set cw_win_app_config=--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --dogecoin
+set cw_win_app_config=--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --dogecoin --base
set cw_root=%cd%
set cw_archive_name=Cake Wallet.zip
set cw_archive_path=%cw_root%\%cw_archive_name%
@@ -24,7 +24,7 @@ IF NOT EXIST "%secrets_file_path%" (
) ELSE (echo === Using previously/already generated secrets file: %secrets_file_path% ===)
echo === Generating mobx models ===
-for /d %%i in (cw_core cw_monero cw_bitcoin cw_ethereum cw_evm cw_polygon cw_nano cw_bitcoin_cash cw_solana cw_tron .) do (
+for /d %%i in (cw_core cw_monero cw_bitcoin cw_ethereum cw_evm cw_polygon cw_nano cw_bitcoin_cash cw_solana cw_tron cw_base.) do (
cd %%i
call flutter pub get > nul
call dart run build_runner build --delete-conflicting-outputs > nul
diff --git a/cw_base/.gitignore b/cw_base/.gitignore
new file mode 100644
index 000000000..eb6c05cd3
--- /dev/null
+++ b/cw_base/.gitignore
@@ -0,0 +1,31 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
+/pubspec.lock
+**/doc/api/
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+build/
diff --git a/cw_base/.metadata b/cw_base/.metadata
new file mode 100644
index 000000000..6f8d1aa9c
--- /dev/null
+++ b/cw_base/.metadata
@@ -0,0 +1,10 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+ revision: "fcf2c11572af6f390246c056bc905eca609533a0"
+ channel: "[user-branch]"
+
+project_type: package
diff --git a/cw_base/CHANGELOG.md b/cw_base/CHANGELOG.md
new file mode 100644
index 000000000..41cc7d819
--- /dev/null
+++ b/cw_base/CHANGELOG.md
@@ -0,0 +1,3 @@
+## 0.0.1
+
+* TODO: Describe initial release.
diff --git a/cw_base/LICENSE b/cw_base/LICENSE
new file mode 100644
index 000000000..ba75c69f7
--- /dev/null
+++ b/cw_base/LICENSE
@@ -0,0 +1 @@
+TODO: Add your license here.
diff --git a/cw_base/README.md b/cw_base/README.md
new file mode 100644
index 000000000..4a260d8d2
--- /dev/null
+++ b/cw_base/README.md
@@ -0,0 +1,39 @@
+
+
+TODO: Put a short description of the package here that helps potential users
+know whether this package might be useful for them.
+
+## Features
+
+TODO: List what your package can do. Maybe include images, gifs, or videos.
+
+## Getting started
+
+TODO: List prerequisites and provide or point to information on how to
+start using the package.
+
+## Usage
+
+TODO: Include short and useful examples for package users. Add longer examples
+to `/example` folder.
+
+```dart
+const like = 'sample';
+```
+
+## Additional information
+
+TODO: Tell users more about the package: where to find more information, how to
+contribute to the package, how to file issues, what response they can expect
+from the package authors, and more.
diff --git a/cw_base/analysis_options.yaml b/cw_base/analysis_options.yaml
new file mode 100644
index 000000000..a5744c1cf
--- /dev/null
+++ b/cw_base/analysis_options.yaml
@@ -0,0 +1,4 @@
+include: package:flutter_lints/flutter.yaml
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
diff --git a/cw_base/lib/base_client.dart b/cw_base/lib/base_client.dart
new file mode 100644
index 000000000..4cf7bc910
--- /dev/null
+++ b/cw_base/lib/base_client.dart
@@ -0,0 +1,103 @@
+import 'dart:convert';
+
+import 'package:cw_evm/evm_chain_client.dart';
+import 'package:cw_evm/.secrets.g.dart' as secrets;
+import 'package:cw_evm/evm_chain_transaction_model.dart';
+import 'package:flutter/foundation.dart';
+import 'package:web3dart/web3dart.dart';
+
+class BaseClient extends EVMChainClient {
+ @override
+ Transaction createTransaction({
+ required EthereumAddress from,
+ required EthereumAddress to,
+ required EtherAmount amount,
+ EtherAmount? maxPriorityFeePerGas,
+ Uint8List? data,
+ int? maxGas,
+ EtherAmount? gasPrice,
+ EtherAmount? maxFeePerGas,
+ }) {
+ EtherAmount? finalGasPrice = gasPrice;
+
+ if (gasPrice == null && maxFeePerGas != null) {
+ // If we have EIP-1559 parameters but no legacy gasPrice, then use maxFeePerGas as gasPrice
+ finalGasPrice = maxFeePerGas;
+ }
+
+ return Transaction(
+ from: from,
+ to: to,
+ value: amount,
+ data: data,
+ maxGas: maxGas,
+ gasPrice: finalGasPrice,
+ // maxFeePerGas: maxFeePerGas,
+ // maxPriorityFeePerGas: maxPriorityFeePerGas,
+ );
+ }
+
+ @override
+ Uint8List prepareSignedTransactionForSending(Uint8List signedTransaction) => signedTransaction;
+
+ @override
+ int get chainId => 8453;
+
+ @override
+ Future> fetchTransactions(String address,
+ {String? contractAddress}) async {
+ try {
+ final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
+ "chainid": "$chainId",
+ "module": "account",
+ "action": contractAddress != null ? "tokentx" : "txlist",
+ if (contractAddress != null) "contractaddress": contractAddress,
+ "address": address,
+ "apikey": secrets.etherScanApiKey,
+ }));
+
+ final jsonResponse = json.decode(response.body) as Map;
+
+ if (response.statusCode >= 200 && response.statusCode < 300 && jsonResponse['status'] != 0) {
+ final res = (jsonResponse['result'] as List);
+
+ res.removeWhere((e) => e['value'] == '0');
+
+ return res
+ .map(
+ (e) => EVMChainTransactionModel.fromJson(e as Map, 'ETH'),
+ )
+ .toList();
+ }
+
+ return [];
+ } catch (e) {
+ return [];
+ }
+ }
+
+ @override
+ Future> fetchInternalTransactions(String address) async {
+ try {
+ final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
+ "chainid": "$chainId",
+ "module": "account",
+ "action": "txlistinternal",
+ "address": address,
+ "apikey": secrets.etherScanApiKey,
+ }));
+
+ final jsonResponse = json.decode(response.body) as Map;
+
+ if (response.statusCode >= 200 && response.statusCode < 300 && jsonResponse['status'] != 0) {
+ return (jsonResponse['result'] as List)
+ .map((e) => EVMChainTransactionModel.fromJson(e as Map, 'ETH'))
+ .toList();
+ }
+
+ return [];
+ } catch (_) {
+ return [];
+ }
+ }
+}
diff --git a/cw_base/lib/base_mnemonics_exception.dart b/cw_base/lib/base_mnemonics_exception.dart
new file mode 100644
index 000000000..83f8403d2
--- /dev/null
+++ b/cw_base/lib/base_mnemonics_exception.dart
@@ -0,0 +1,5 @@
+class BaseMnemonicIsIncorrectException implements Exception {
+ @override
+ String toString() =>
+ 'Base mnemonic has incorrect format. Mnemonic should contain 12 or 24 words separated by space.';
+}
diff --git a/cw_base/lib/base_transaction_history.dart b/cw_base/lib/base_transaction_history.dart
new file mode 100644
index 000000000..42d93a1e0
--- /dev/null
+++ b/cw_base/lib/base_transaction_history.dart
@@ -0,0 +1,20 @@
+import 'dart:core';
+
+import 'package:cw_evm/evm_chain_transaction_history.dart';
+import 'package:cw_evm/evm_chain_transaction_info.dart';
+import 'package:cw_base/base_transaction_info.dart';
+
+class BaseTransactionHistory extends EVMChainTransactionHistory {
+ BaseTransactionHistory({
+ required super.walletInfo,
+ required super.password,
+ required super.encryptionFileUtils,
+ });
+
+ @override
+ String getTransactionHistoryFileName() => 'base_transactions.json';
+
+ @override
+ EVMChainTransactionInfo getTransactionInfo(Map val) =>
+ BaseTransactionInfo.fromJson(val);
+}
diff --git a/cw_base/lib/base_transaction_info.dart b/cw_base/lib/base_transaction_info.dart
new file mode 100644
index 000000000..858c4fce7
--- /dev/null
+++ b/cw_base/lib/base_transaction_info.dart
@@ -0,0 +1,41 @@
+import 'package:cw_core/transaction_direction.dart';
+import 'package:cw_evm/evm_chain_transaction_info.dart';
+
+class BaseTransactionInfo extends EVMChainTransactionInfo {
+ BaseTransactionInfo({
+ required super.id,
+ required super.height,
+ required super.ethAmount,
+ required super.ethFee,
+ required super.tokenSymbol,
+ required super.direction,
+ required super.isPending,
+ required super.date,
+ required super.confirmations,
+ required super.to,
+ required super.from,
+ super.contractAddress,
+ super.exponent,
+ });
+
+ factory BaseTransactionInfo.fromJson(Map data) {
+ return BaseTransactionInfo(
+ id: data['id'] as String,
+ height: data['height'] as int,
+ ethAmount: BigInt.parse(data['amount']),
+ exponent: data['exponent'] as int,
+ ethFee: BigInt.parse(data['fee']),
+ direction: parseTransactionDirectionFromInt(data['direction'] as int),
+ date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
+ isPending: data['isPending'] as bool,
+ confirmations: data['confirmations'] as int,
+ tokenSymbol: data['tokenSymbol'] as String,
+ to: data['to'],
+ from: data['from'],
+ contractAddress: data['contractAddress'],
+ );
+ }
+
+ @override
+ String get feeCurrency => 'ETH';
+}
diff --git a/cw_base/lib/base_wallet.dart b/cw_base/lib/base_wallet.dart
new file mode 100644
index 000000000..6b26fda25
--- /dev/null
+++ b/cw_base/lib/base_wallet.dart
@@ -0,0 +1,165 @@
+import 'dart:convert';
+
+import 'package:cw_base/base_client.dart';
+import 'package:cw_base/base_transaction_history.dart';
+import 'package:cw_base/base_transaction_info.dart';
+import 'package:cw_base/default_base_erc20_tokens.dart';
+import 'package:cw_core/cake_hive.dart';
+import 'package:cw_core/crypto_currency.dart';
+import 'package:cw_core/encryption_file_utils.dart';
+import 'package:cw_core/erc20_token.dart';
+import 'package:cw_core/pathForWallet.dart';
+import 'package:cw_core/transaction_direction.dart';
+import 'package:cw_core/wallet_info.dart';
+import 'package:cw_core/wallet_keys_file.dart';
+import 'package:cw_evm/evm_chain_transaction_history.dart';
+import 'package:cw_evm/evm_chain_transaction_info.dart';
+import 'package:cw_evm/evm_chain_transaction_model.dart';
+import 'package:cw_evm/evm_chain_wallet.dart';
+import 'package:cw_evm/evm_erc20_balance.dart';
+
+class BaseWallet extends EVMChainWallet {
+ BaseWallet({
+ required super.walletInfo,
+ required super.password,
+ super.mnemonic,
+ super.initialBalance,
+ super.privateKey,
+ required super.client,
+ required super.encryptionFileUtils,
+ super.passphrase,
+ }) : super(nativeCurrency: CryptoCurrency.baseEth);
+
+ @override
+ Future initErc20TokensBox() async {
+ final boxName = "${walletInfo.name.replaceAll(" ", "_")}_ ${Erc20Token.baseBoxName}";
+
+ evmChainErc20TokensBox = await CakeHive.openBox(boxName);
+ }
+
+ @override
+ void addInitialTokens([bool isMigration = false]) {
+ final initialErc20Tokens = DefaultBaseErc20Tokens().initialBaseErc20Tokens;
+
+ for (final token in initialErc20Tokens) {
+ if (evmChainErc20TokensBox.containsKey(token.contractAddress)) {
+ final existingToken = evmChainErc20TokensBox.get(token.contractAddress);
+ if (existingToken?.tag != token.tag) {
+ evmChainErc20TokensBox.put(token.contractAddress, token);
+ }
+ } else {
+ if (isMigration) token.enabled = false;
+ evmChainErc20TokensBox.put(token.contractAddress, token);
+ }
+ }
+ }
+
+ @override
+ List get getDefaultTokenContractAddresses =>
+ DefaultBaseErc20Tokens().initialBaseErc20Tokens.map((e) => e.contractAddress).toList();
+
+ @override
+ Future checkIfScanProviderIsEnabled() async {
+ return (await sharedPrefs.future).getBool("use_basescan") ?? true;
+ }
+
+ @override
+ String getTransactionHistoryFileName() => 'base_transactions.json';
+
+ @override
+ Erc20Token createNewErc20TokenObject(Erc20Token token, String? iconPath) {
+ return Erc20Token(
+ name: token.name,
+ symbol: token.symbol,
+ contractAddress: token.contractAddress,
+ decimal: token.decimal,
+ enabled: token.enabled,
+ tag: token.tag ?? 'ETH',
+ iconPath: iconPath,
+ isPotentialScam: token.isPotentialScam,
+ );
+ }
+
+ @override
+ EVMChainTransactionInfo getTransactionInfo(
+ EVMChainTransactionModel transactionModel, String address) {
+ final model = BaseTransactionInfo(
+ id: transactionModel.hash,
+ height: transactionModel.blockNumber,
+ ethAmount: transactionModel.amount,
+ direction: transactionModel.from == address
+ ? TransactionDirection.outgoing
+ : TransactionDirection.incoming,
+ isPending: false,
+ date: transactionModel.date,
+ confirmations: transactionModel.confirmations,
+ ethFee: BigInt.from(transactionModel.gasUsed) * transactionModel.gasPrice,
+ exponent: transactionModel.tokenDecimal ?? 18,
+ tokenSymbol: transactionModel.tokenSymbol ?? "ETH",
+ to: transactionModel.to,
+ from: transactionModel.from,
+ contractAddress: transactionModel.contractAddress,
+ );
+ return model;
+ }
+
+ @override
+ EVMChainTransactionHistory setUpTransactionHistory(
+ WalletInfo walletInfo, String password, EncryptionFileUtils encryptionFileUtils) {
+ return BaseTransactionHistory(
+ walletInfo: walletInfo,
+ password: password,
+ encryptionFileUtils: encryptionFileUtils,
+ );
+ }
+
+ static Future open({
+ required String name,
+ required String password,
+ required WalletInfo walletInfo,
+ required EncryptionFileUtils encryptionFileUtils,
+ }) async {
+ final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type);
+ final path = await pathForWallet(name: name, type: walletInfo.type);
+
+ Map? data;
+ try {
+ final jsonSource = await encryptionFileUtils.read(path: path, password: password);
+
+ data = json.decode(jsonSource) as Map;
+ } catch (e) {
+ if (!hasKeysFile) rethrow;
+ }
+
+ final balance = EVMChainERC20Balance.fromJSON(data?['balance'] as String?) ??
+ EVMChainERC20Balance(BigInt.zero);
+
+ final WalletKeysData keysData;
+ // Migrate wallet from the old scheme to then new .keys file scheme
+ if (!hasKeysFile) {
+ final mnemonic = data!['mnemonic'] as String?;
+ final privateKey = data['private_key'] as String?;
+ final passphrase = data['passphrase'] as String?;
+
+ keysData = WalletKeysData(mnemonic: mnemonic, privateKey: privateKey, passphrase: passphrase);
+ } else {
+ keysData = await WalletKeysFile.readKeysFile(
+ name,
+ walletInfo.type,
+ password,
+ encryptionFileUtils,
+ );
+ }
+
+ return BaseWallet(
+ walletInfo: walletInfo,
+ password: password,
+ mnemonic: keysData.mnemonic,
+ privateKey: keysData.privateKey,
+ passphrase: keysData.passphrase,
+ initialBalance: balance,
+ client: BaseClient(),
+ encryptionFileUtils: encryptionFileUtils,
+ );
+ }
+}
diff --git a/cw_base/lib/base_wallet_service.dart b/cw_base/lib/base_wallet_service.dart
new file mode 100644
index 000000000..b6ad9e4e0
--- /dev/null
+++ b/cw_base/lib/base_wallet_service.dart
@@ -0,0 +1,163 @@
+import 'package:bip39/bip39.dart' as bip39;
+import 'package:cw_core/encryption_file_utils.dart';
+import 'package:cw_core/wallet_base.dart';
+import 'package:cw_core/wallet_info.dart';
+import 'package:cw_core/wallet_type.dart';
+import 'package:cw_evm/evm_chain_wallet_creation_credentials.dart';
+import 'package:cw_evm/evm_chain_wallet_service.dart';
+import 'package:cw_base/base_wallet.dart';
+import 'package:cw_base/base_client.dart';
+import 'package:cw_base/base_mnemonics_exception.dart';
+
+class BaseWalletService extends EVMChainWalletService {
+ BaseWalletService(
+ super.walletInfoSource,
+ super.isDirect, {
+ required this.client,
+ });
+
+ late BaseClient client;
+
+ @override
+ WalletType getType() => WalletType.base;
+
+ @override
+ Future create(EVMChainNewWalletCredentials credentials, {bool? isTestnet}) async {
+ final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
+
+ final mnemonic = credentials.mnemonic ?? bip39.generateMnemonic(strength: strength);
+
+ final wallet = BaseWallet(
+ walletInfo: credentials.walletInfo!,
+ mnemonic: mnemonic,
+ password: credentials.password!,
+ passphrase: credentials.passphrase,
+ client: client,
+ encryptionFileUtils: encryptionFileUtilsFor(isDirect),
+ );
+
+ await wallet.init();
+ wallet.addInitialTokens();
+ await wallet.save();
+ return wallet;
+ }
+
+ @override
+ Future openWallet(String name, String password) async {
+ final walletInfo =
+ walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
+
+ try {
+ final wallet = await BaseWallet.open(
+ name: name,
+ password: password,
+ walletInfo: walletInfo,
+ encryptionFileUtils: encryptionFileUtilsFor(isDirect),
+ );
+
+ await wallet.init();
+ wallet.addInitialTokens(true);
+ await wallet.save();
+ saveBackup(name);
+ return wallet;
+ } catch (_) {
+ await restoreWalletFilesFromBackup(name);
+
+ final wallet = await BaseWallet.open(
+ name: name,
+ password: password,
+ walletInfo: walletInfo,
+ encryptionFileUtils: encryptionFileUtilsFor(isDirect),
+ );
+
+ await wallet.init();
+ wallet.addInitialTokens(true);
+ await wallet.save();
+ return wallet;
+ }
+ }
+
+ @override
+ Future restoreFromKeys(EVMChainRestoreWalletFromPrivateKey credentials,
+ {bool? isTestnet}) async {
+ final wallet = BaseWallet(
+ password: credentials.password!,
+ privateKey: credentials.privateKey,
+ walletInfo: credentials.walletInfo!,
+ client: client,
+ encryptionFileUtils: encryptionFileUtilsFor(isDirect),
+ );
+
+ await wallet.init();
+ wallet.addInitialTokens();
+ await wallet.save();
+ return wallet;
+ }
+
+ @override
+ Future restoreFromHardwareWallet(
+ EVMChainRestoreWalletFromHardware credentials) async {
+ credentials.walletInfo!.derivationInfo = DerivationInfo(
+ derivationType: DerivationType.bip39,
+ derivationPath: "m/44'/60'/${credentials.hwAccountData.accountIndex}'/0/0");
+ credentials.walletInfo!.hardwareWalletType = credentials.hardwareWalletType;
+ credentials.walletInfo!.address = credentials.hwAccountData.address;
+
+ final wallet = BaseWallet(
+ walletInfo: credentials.walletInfo!,
+ password: credentials.password!,
+ client: client,
+ encryptionFileUtils: encryptionFileUtilsFor(isDirect),
+ );
+
+ await wallet.init();
+ wallet.addInitialTokens();
+ await wallet.save();
+
+ return wallet;
+ }
+
+ @override
+ Future restoreFromSeed(EVMChainRestoreWalletFromSeedCredentials credentials,
+ {bool? isTestnet}) async {
+ if (!bip39.validateMnemonic(credentials.mnemonic)) {
+ throw BaseMnemonicIsIncorrectException();
+ }
+
+ final wallet = BaseWallet(
+ password: credentials.password!,
+ mnemonic: credentials.mnemonic,
+ walletInfo: credentials.walletInfo!,
+ passphrase: credentials.passphrase,
+ client: client,
+ encryptionFileUtils: encryptionFileUtilsFor(isDirect),
+ );
+
+ await wallet.init();
+ wallet.addInitialTokens();
+ await wallet.save();
+
+ return wallet;
+ }
+
+ @override
+ Future rename(String currentName, String password, String newName) async {
+ final currentWalletInfo = walletInfoSource.values
+ .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
+ final currentWallet = await BaseWallet.open(
+ password: password,
+ name: currentName,
+ walletInfo: currentWalletInfo,
+ encryptionFileUtils: encryptionFileUtilsFor(isDirect),
+ );
+
+ await currentWallet.renameWalletFiles(newName);
+ await saveBackup(newName);
+
+ final newWalletInfo = currentWalletInfo;
+ newWalletInfo.id = WalletBase.idFor(newName, getType());
+ newWalletInfo.name = newName;
+
+ await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
+ }
+}
diff --git a/cw_base/lib/cw_base.dart b/cw_base/lib/cw_base.dart
new file mode 100644
index 000000000..298576d81
--- /dev/null
+++ b/cw_base/lib/cw_base.dart
@@ -0,0 +1,5 @@
+/// A Calculator.
+class Calculator {
+ /// Returns [value] plus 1.
+ int addOne(int value) => value + 1;
+}
diff --git a/cw_base/lib/default_base_erc20_tokens.dart b/cw_base/lib/default_base_erc20_tokens.dart
new file mode 100644
index 000000000..3f899927c
--- /dev/null
+++ b/cw_base/lib/default_base_erc20_tokens.dart
@@ -0,0 +1,67 @@
+import 'package:cw_core/crypto_currency.dart';
+import 'package:cw_core/erc20_token.dart';
+
+class DefaultBaseErc20Tokens {
+ final List _defaultTokens = [
+ Erc20Token(
+ name: "USD Coin",
+ symbol: "USDC",
+ contractAddress: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
+ decimal: 6,
+ enabled: true,
+ ),
+ Erc20Token(
+ name: "USDe",
+ symbol: "USDe",
+ contractAddress: "0x5d3a1Ff2b6BAb83b63cd9AD0787074081a52ef34",
+ decimal: 18,
+ enabled: true,
+ ),
+ Erc20Token(
+ name: "Dai",
+ symbol: "DAI",
+ contractAddress: "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
+ decimal: 18,
+ enabled: true,
+ ),
+ Erc20Token(
+ name: "Bridged Tether USD",
+ symbol: "USDT",
+ contractAddress: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2",
+ decimal: 6,
+ enabled: true,
+ ),
+ Erc20Token(
+ name: "Wrapped Ether",
+ symbol: "WETH",
+ contractAddress: "0x4200000000000000000000000000000000000006",
+ decimal: 18,
+ enabled: false,
+ ),
+ Erc20Token(
+ name: "Wrapped BTC",
+ symbol: "WBTC",
+ contractAddress: "0x0555E30da8f98308EdB960aa94C0Db47230d2B9c",
+ decimal: 8,
+ enabled: false,
+ ),
+ Erc20Token(
+ name: "SPX6900",
+ symbol: "SPX",
+ contractAddress: "0x50dA645f148798F68EF2d7dB7C1CB22A6819bb2C",
+ decimal: 8,
+ enabled: false,
+ ),
+ ];
+
+ List get initialBaseErc20Tokens => _defaultTokens.map((token) {
+ String? iconPath;
+ try {
+ iconPath = CryptoCurrency.all
+ .firstWhere((element) => element.title.toUpperCase() == token.symbol.toUpperCase())
+ .iconPath;
+ } catch (_) {}
+
+ return Erc20Token.copyWith(token, iconPath, 'BASE');
+ }).toList();
+}
diff --git a/cw_base/pubspec.yaml b/cw_base/pubspec.yaml
new file mode 100644
index 000000000..f94468bce
--- /dev/null
+++ b/cw_base/pubspec.yaml
@@ -0,0 +1,73 @@
+name: cw_base
+description: "Base package for Cake Wallet"
+version: 0.0.1
+publish_to: none
+homepage: https://cakewallet.com
+
+environment:
+ sdk: '>=3.0.6 <4.0.0'
+ flutter: ">=1.17.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+ cw_core:
+ path: ../cw_core
+ cw_ethereum:
+ path: ../cw_ethereum
+ cw_evm:
+ path: ../cw_evm
+ web3dart: ^2.7.1
+ hive: ^2.2.3
+ bip39: ^1.0.6
+ collection: ^1.17.1
+
+dependency_overrides:
+ web3dart:
+ git:
+ url: https://github.com/cake-tech/web3dart.git
+ ref: cake
+ watcher: ^1.1.0
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+ flutter_lints: ^2.0.0
+ build_runner: ^2.4.15
+
+# For information on the generic Dart part of this file, see the
+# following page: https://dart.dev/tools/pub/pubspec
+
+# The following section is specific to Flutter packages.
+flutter:
+
+ # To add assets to your package, add an assets section, like this:
+ # assets:
+ # - images/a_dot_burr.jpeg
+ # - images/a_dot_ham.jpeg
+ #
+ # For details regarding assets in packages, see
+ # https://flutter.dev/to/asset-from-package
+ #
+ # An image asset can refer to one or more resolution-specific "variants", see
+ # https://flutter.dev/to/resolution-aware-images
+
+ # To add custom fonts to your package, add a fonts section here,
+ # in this "flutter" section. Each entry in this list should have a
+ # "family" key with the font family name, and a "fonts" key with a
+ # list giving the asset and other descriptors for the font. For
+ # example:
+ # fonts:
+ # - family: Schyler
+ # fonts:
+ # - asset: fonts/Schyler-Regular.ttf
+ # - asset: fonts/Schyler-Italic.ttf
+ # style: italic
+ # - family: Trajan Pro
+ # fonts:
+ # - asset: fonts/TrajanPro.ttf
+ # - asset: fonts/TrajanPro_Bold.ttf
+ # weight: 700
+ #
+ # For details regarding fonts in packages, see
+ # https://flutter.dev/to/font-from-package
diff --git a/cw_base/test/cw_base_test.dart b/cw_base/test/cw_base_test.dart
new file mode 100644
index 000000000..31f598069
--- /dev/null
+++ b/cw_base/test/cw_base_test.dart
@@ -0,0 +1,12 @@
+import 'package:flutter_test/flutter_test.dart';
+
+import 'package:cw_base/cw_base.dart';
+
+void main() {
+ test('adds one to input values', () {
+ final calculator = Calculator();
+ expect(calculator.addOne(2), 3);
+ expect(calculator.addOne(-7), -6);
+ expect(calculator.addOne(0), 1);
+ });
+}
diff --git a/cw_bitcoin/lib/electrum_transaction_info.dart b/cw_bitcoin/lib/electrum_transaction_info.dart
index 7a8b3b951..407e405e1 100644
--- a/cw_bitcoin/lib/electrum_transaction_info.dart
+++ b/cw_bitcoin/lib/electrum_transaction_info.dart
@@ -5,6 +5,7 @@ import 'package:cw_bitcoin/address_from_output.dart';
import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
import 'package:cw_bitcoin/bitcoin_unspent.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/format_amount.dart';
diff --git a/cw_core/lib/crypto_currency.dart b/cw_core/lib/crypto_currency.dart
index a5c2a8179..eb8e5ac15 100644
--- a/cw_core/lib/crypto_currency.dart
+++ b/cw_core/lib/crypto_currency.dart
@@ -115,6 +115,7 @@ class CryptoCurrency extends EnumerableItem with Serializable implemen
CryptoCurrency.flip,
CryptoCurrency.ndeps,
CryptoCurrency.deps,
+ CryptoCurrency.baseEth,
];
static const havenCurrencies = [
@@ -241,6 +242,7 @@ class CryptoCurrency extends EnumerableItem with Serializable implemen
static const deps = CryptoCurrency(title: 'DEPS', tag: 'ETH', fullName: 'Decentralized Euro Protocol Share', raw: 101, name: 'deps', iconPath: 'assets/images/deps_icon.png', decimals: 18);
static const kbtc = CryptoCurrency(title: 'KBTC', tag: 'ETH', fullName: 'Kraken Wrapped Bitcoin', raw: 102, name: 'kbtc', iconPath: 'assets/images/kbtc_icon.png', decimals: 8);
static const cbbtc = CryptoCurrency(title: 'CBBTC', tag: 'ETH', fullName: 'Coinbase Wrapped BTC', raw: 103, name: 'cbbtc', iconPath: 'assets/images/cbbtc_icon.png', decimals: 8);
+ static const baseEth = CryptoCurrency(title: 'ETH', tag: 'BASE', fullName: 'Ethereum', raw: 104, name: 'baseth', iconPath: 'assets/images/eth_icon.png', decimals: 18);
static final Map _rawCurrencyMap =
[...all, ...havenCurrencies].fold