What changed, and why it matters
This commit adds support for the Base blockchain (an Ethereum Layer 2) to the Cake Wallet app. It introduces a new wallet type, transaction handling, default token list, node list, icons, and settings. The changes are mostly feature additions that mirror existing Ethereum/Polygon support. There are a few code-quality items worth noting, such as a hardcoded API endpoint and a gas-price buffer, but nothing in the diff clearly indicates an exploitable security vulnerability.
Treat this as a routine feature commit rather than a security patch. Reviewers should still audit the Base integration for EVM-specific risks: verify the Etherscan API key handling and TLS pinning, confirm the NowNodes substring match does not accidentally route other RPCs through NowNodes, ensure the gas buffer cannot be abused to overcharge users, and validate that default Base ERC-20 token contract addresses and decimals are correct. No immediate security response is indicated by the diff alone.
Security signals we found
New EVM chain integration reuses existing EVM wallet infrastructure
Hardcoded third-party transaction indexer (api.etherscan.io) for Base transaction history
Gas price safety buffer added for Base chain 'send all' calculations
NowNodes endpoint detection broadened from exact host match to substring match ('.contains(ownednodes.io)')
New insufficient-funds error parsing in EVMTransactionErrorFeesHandler
No explicit security disclosure, CVE, or vulnerability fix language in commit message or diff
Evidence from the diff
The commit is a large feature patch (+1828/-197 across 139 files) implementing Base (chainId 8453) wallet support in Cake Wallet. Key components: new cw_base package with BaseClient, BaseWallet, BaseWalletService, BaseTransactionHistory/Info, default ERC-20 token list; integration into wallet type enums, currency mapping, node management, settings store, exchange/buy providers, WalletConnect, privacy toggles, and UI assets. Notable implementation details: BaseClient fetches transactions from api.etherscan.io/v2/api with chainid=8453; BaseWallet native currency is CryptoCurrency.baseEth (tag BASE); EVMChainClient now treats any URI containing ‘nownodes.io’ as a NowNodes endpoint; a 1% or 1000 wei gas buffer is added for Base ‘send all’ transactions; EVMTransactionErrorFeesHandler parses a new ‘insufficient funds: have X want Y’ error format. No explicit security fixes or CVEs are mentioned.
Changed components
cw_base package (BaseClient, BaseWallet, BaseWalletService, BaseTransactionHistory, BaseTransactionInfo, DefaultBaseErc20Tokens)cw_evm (EVMChainClient, EVMChainWallet, gas estimation, fee currency handling)cw_core (WalletType, CryptoCurrency, Node, currency mapping, Erc20Token box name)lib/entities (settings migration, node list, preferences keys, ENS, error fee handler)lib/store/settings_store.dart (Base scan preference, transaction priority)lib/reactions (fiat rate, wallet change, WalletConnect chain support)lib/view_model and UI screens (wallet creation, send, exchange, dashboard, privacy settings)CI workflow secrets for Base test walletsInspect captured patch +1828 / −197
diff --git a/.github/workflows/automated_integration_test.yml b/.github/workflows/automated_integration_test.yml
index 34a4c6b..b95b710 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 03c3de0..2b07c27 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 5d5b527..05fb8b9 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 40318d3..e9f3809 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 3fa31bd..be83f1a 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 87654b5..1266d08 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 bd35233..bc119a6 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 1bc56c1..287554c 100644
--- a/android/app/src/main/AndroidManifestBase.xml
+++ b/android/app/src/main/AndroidManifestBase.xml
@@ -106,6 +106,9 @@
<data android:scheme="dogecoin" />
<data android:scheme="dogecoin-wallet" />
<data android:scheme="dogecoin_wallet" />
+ <data android:scheme="base" />
+ <data android:scheme="base-wallet" />
+ <data android:scheme="base_wallet" />
</intent-filter>
<!-- nano-gpt link scheme -->
<intent-filter android:autoVerify="true">
diff --git a/assets/base_node_list.yml b/assets/base_node_list.yml
new file mode 100644
index 0000000..d135f7a
--- /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 0000000..7f801d5
--- /dev/null
+++ b/assets/images/base_chain_QR.svg
@@ -0,0 +1,5 @@
+<svg width="45" height="45" viewBox="0 0 45 45" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect x="1" y="1" width="43" height="43" rx="6.03125" fill="white"/>
+<rect x="1" y="1" width="43" height="43" rx="6.03125" stroke="black" stroke-width="2"/>
+<path d="M22.4774 35.4375C29.635 35.4375 35.4375 29.6451 35.4375 22.5C35.4375 15.3548 29.635 9.5625 22.4774 9.5625C15.6866 9.5625 10.1158 14.7762 9.5625 21.4125H26.6927V23.5874H9.5625C10.1158 30.2238 15.6866 35.4375 22.4774 35.4375Z" fill="black"/>
+</svg>
diff --git a/assets/images/base_chain_mono.svg b/assets/images/base_chain_mono.svg
new file mode 100644
index 0000000..48ebe83
--- /dev/null
+++ b/assets/images/base_chain_mono.svg
@@ -0,0 +1,3 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M15.9748 30.4C23.9416 30.4 30.4 23.9528 30.4 16C30.4 8.04709 23.9416 1.60001 15.9748 1.60001C8.41642 1.60001 2.2158 7.40309 1.59998 14.7895H20.6666V17.2104H1.59998C2.2158 24.5969 8.41642 30.4 15.9748 30.4Z" fill="white"/>
+</svg>
diff --git a/assets/images/base_icon.png b/assets/images/base_icon.png
new file mode 100644
index 0000000..d7cb8ed
Binary files /dev/null and b/assets/images/base_icon.png differ
diff --git a/cakewallet.bat b/cakewallet.bat
index b9da876..8baa910 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 0000000..eb6c05c
--- /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 0000000..6f8d1aa
--- /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 0000000..41cc7d8
--- /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 0000000..ba75c69
--- /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 0000000..4a260d8
--- /dev/null
+++ b/cw_base/README.md
@@ -0,0 +1,39 @@
+<!--
+This README describes the package. If you publish this package to pub.dev,
+this README's contents appear on the landing page for your package.
+
+For information about how to write a good package README, see the guide for
+[writing package pages](https://dart.dev/tools/pub/writing-package-pages).
+
+For general information about developing packages, see the Dart guide for
+[creating packages](https://dart.dev/guides/libraries/create-packages)
+and the Flutter guide for
+[developing packages and plugins](https://flutter.dev/to/develop-packages).
+-->
+
+TODO: Put a short description of the package here that helps potential users
+know whether this package might be useful for them.
+
+## Features
+
+TODO: List what your package can do. Maybe include images, gifs, or videos.
+
+## Getting started
+
+TODO: List prerequisites and provide or point to information on how to
+start using the package.
+
+## Usage
+
+TODO: Include short and useful examples for package users. Add longer examples
+to `/example` folder.
+
+```dart
+const like = 'sample';
+```
+
+## Additional information
+
+TODO: Tell users more about the package: where to find more information, how to
+contribute to the package, how to file issues, what response they can expect
+from the package authors, and more.
diff --git a/cw_base/analysis_options.yaml b/cw_base/analysis_options.yaml
new file mode 100644
index 0000000..a5744c1
--- /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 0000000..4cf7bc9
--- /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<List<EVMChainTransactionModel>> fetchTransactions(String address,
+ {String? contractAddress}) async {
+ try {
+ final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
+ "chainid": "$chainId",
+ "module": "account",
+ "action": contractAddress != null ? "tokentx" : "txlist",
+ if (contractAddress != null) "contractaddress": contractAddress,
+ "address": address,
+ "apikey": secrets.etherScanApiKey,
+ }));
+
+ final jsonResponse = json.decode(response.body) as Map<String, dynamic>;
+
+ if (response.statusCode >= 200 && response.statusCode < 300 && jsonResponse['status'] != 0) {
+ final res = (jsonResponse['result'] as List);
+
+ res.removeWhere((e) => e['value'] == '0');
+
+ return res
+ .map(
+ (e) => EVMChainTransactionModel.fromJson(e as Map<String, dynamic>, 'ETH'),
+ )
+ .toList();
+ }
+
+ return [];
+ } catch (e) {
+ return [];
+ }
+ }
+
+ @override
+ Future<List<EVMChainTransactionModel>> fetchInternalTransactions(String address) async {
+ try {
+ final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
+ "chainid": "$chainId",
+ "module": "account",
+ "action": "txlistinternal",
+ "address": address,
+ "apikey": secrets.etherScanApiKey,
+ }));
+
+ final jsonResponse = json.decode(response.body) as Map<String, dynamic>;
+
+ if (response.statusCode >= 200 && response.statusCode < 300 && jsonResponse['status'] != 0) {
+ return (jsonResponse['result'] as List)
+ .map((e) => EVMChainTransactionModel.fromJson(e as Map<String, dynamic>, 'ETH'))
+ .toList();
+ }
+
+ return [];
+ } catch (_) {
+ return [];
+ }
+ }
+}
diff --git a/cw_base/lib/base_mnemonics_exception.dart b/cw_base/lib/base_mnemonics_exception.dart
new file mode 100644
index 0000000..83f8403
--- /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 0000000..42d93a1
--- /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<String, dynamic> 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 0000000..858c4fc
--- /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<String, dynamic> data) {
+ return BaseTransactionInfo(
+ id: data['id'] as String,
+ height: data['height'] as int,
+ ethAmount: BigInt.parse(data['amount']),
+ exponent: data['exponent'] as int,
+ ethFee: BigInt.parse(data['fee']),
+ direction: parseTransactionDirectionFromInt(data['direction'] as int),
+ date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
+ isPending: data['isPending'] as bool,
+ confirmations: data['confirmations'] as int,
+ tokenSymbol: data['tokenSymbol'] as String,
+ to: data['to'],
+ from: data['from'],
+ contractAddress: data['contractAddress'],
+ );
+ }
+
+ @override
+ String get feeCurrency => 'ETH';
+}
diff --git a/cw_base/lib/base_wallet.dart b/cw_base/lib/base_wallet.dart
new file mode 100644
index 0000000..6b26fda
--- /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<void> initErc20TokensBox() async {
+ final boxName = "${walletInfo.name.replaceAll(" ", "_")}_ ${Erc20Token.baseBoxName}";
+
+ evmChainErc20TokensBox = await CakeHive.openBox<Erc20Token>(boxName);
+ }
+
+ @override
+ void addInitialTokens([bool isMigration = false]) {
+ final initialErc20Tokens = DefaultBaseErc20Tokens().initialBaseErc20Tokens;
+
+ for (final token in initialErc20Tokens) {
+ if (evmChainErc20TokensBox.containsKey(token.contractAddress)) {
+ final existingToken = evmChainErc20TokensBox.get(token.contractAddress);
+ if (existingToken?.tag != token.tag) {
+ evmChainErc20TokensBox.put(token.contractAddress, token);
+ }
+ } else {
+ if (isMigration) token.enabled = false;
+ evmChainErc20TokensBox.put(token.contractAddress, token);
+ }
+ }
+ }
+
+ @override
+ List<String> get getDefaultTokenContractAddresses =>
+ DefaultBaseErc20Tokens().initialBaseErc20Tokens.map((e) => e.contractAddress).toList();
+
+ @override
+ Future<bool> checkIfScanProviderIsEnabled() async {
+ return (await sharedPrefs.future).getBool("use_basescan") ?? true;
+ }
+
+ @override
+ String getTransactionHistoryFileName() => 'base_transactions.json';
+
+ @override
+ Erc20Token createNewErc20TokenObject(Erc20Token token, String? iconPath) {
+ return Erc20Token(
+ name: token.name,
+ symbol: token.symbol,
+ contractAddress: token.contractAddress,
+ decimal: token.decimal,
+ enabled: token.enabled,
+ tag: token.tag ?? 'ETH',
+ iconPath: iconPath,
+ isPotentialScam: token.isPotentialScam,
+ );
+ }
+
+ @override
+ EVMChainTransactionInfo getTransactionInfo(
+ EVMChainTransactionModel transactionModel, String address) {
+ final model = BaseTransactionInfo(
+ id: transactionModel.hash,
+ height: transactionModel.blockNumber,
+ ethAmount: transactionModel.amount,
+ direction: transactionModel.from == address
+ ? TransactionDirection.outgoing
+ : TransactionDirection.incoming,
+ isPending: false,
+ date: transactionModel.date,
+ confirmations: transactionModel.confirmations,
+ ethFee: BigInt.from(transactionModel.gasUsed) * transactionModel.gasPrice,
+ exponent: transactionModel.tokenDecimal ?? 18,
+ tokenSymbol: transactionModel.tokenSymbol ?? "ETH",
+ to: transactionModel.to,
+ from: transactionModel.from,
+ contractAddress: transactionModel.contractAddress,
+ );
+ return model;
+ }
+
+ @override
+ EVMChainTransactionHistory setUpTransactionHistory(
+ WalletInfo walletInfo, String password, EncryptionFileUtils encryptionFileUtils) {
+ return BaseTransactionHistory(
+ walletInfo: walletInfo,
+ password: password,
+ encryptionFileUtils: encryptionFileUtils,
+ );
+ }
+
+ static Future<BaseWallet> open({
+ required String name,
+ required String password,
+ required WalletInfo walletInfo,
+ required EncryptionFileUtils encryptionFileUtils,
+ }) async {
+ final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type);
+ final path = await pathForWallet(name: name, type: walletInfo.type);
+
+ Map<String, dynamic>? data;
+ try {
+ final jsonSource = await encryptionFileUtils.read(path: path, password: password);
+
+ data = json.decode(jsonSource) as Map<String, dynamic>;
+ } catch (e) {
+ if (!hasKeysFile) rethrow;
+ }
+
+ final balance = EVMChainERC20Balance.fromJSON(data?['balance'] as String?) ??
+ EVMChainERC20Balance(BigInt.zero);
+
+ final WalletKeysData keysData;
+ // Migrate wallet from the old scheme to then new .keys file scheme
+ if (!hasKeysFile) {
+ final mnemonic = data!['mnemonic'] as String?;
+ final privateKey = data['private_key'] as String?;
+ final passphrase = data['passphrase'] as String?;
+
+ keysData = WalletKeysData(mnemonic: mnemonic, privateKey: privateKey, passphrase: passphrase);
+ } else {
+ keysData = await WalletKeysFile.readKeysFile(
+ name,
+ walletInfo.type,
+ password,
+ encryptionFileUtils,
+ );
+ }
+
+ return BaseWallet(
+ walletInfo: walletInfo,
+ password: password,
+ mnemonic: keysData.mnemonic,
+ privateKey: keysData.privateKey,
+ passphrase: keysData.passphrase,
+ initialBalance: balance,
+ client: BaseClient(),
+ encryptionFileUtils: encryptionFileUtils,
+ );
+ }
+}
diff --git a/cw_base/lib/base_wallet_service.dart b/cw_base/lib/base_wallet_service.dart
new file mode 100644
index 0000000..b6ad9e4
--- /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<BaseWallet> {
+ BaseWalletService(
+ super.walletInfoSource,
+ super.isDirect, {
+ required this.client,
+ });
+
+ late BaseClient client;
+
+ @override
+ WalletType getType() => WalletType.base;
+
+ @override
+ Future<BaseWallet> create(EVMChainNewWalletCredentials credentials, {bool? isTestnet}) async {
+ final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
+
+ final mnemonic = credentials.mnemonic ?? bip39.generateMnemonic(strength: strength);
+
+ final wallet = BaseWallet(
+ walletInfo: credentials.walletInfo!,
+ mnemonic: mnemonic,
+ password: credentials.password!,
+ passphrase: credentials.passphrase,
+ client: client,
+ encryptionFileUtils: encryptionFileUtilsFor(isDirect),
+ );
+
+ await wallet.init();
+ wallet.addInitialTokens();
+ await wallet.save();
+ return wallet;
+ }
+
+ @override
+ Future<BaseWallet> openWallet(String name, String password) async {
+ final walletInfo =
+ walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
+
+ try {
+ final wallet = await BaseWallet.open(
+ name: name,
+ password: password,
+ walletInfo: walletInfo,
+ encryptionFileUtils: encryptionFileUtilsFor(isDirect),
+ );
+
+ await wallet.init();
+ wallet.addInitialTokens(true);
+ await wallet.save();
+ saveBackup(name);
+ return wallet;
+ } catch (_) {
+ await restoreWalletFilesFromBackup(name);
+
+ final wallet = await BaseWallet.open(
+ name: name,
+ password: password,
+ walletInfo: walletInfo,
+ encryptionFileUtils: encryptionFileUtilsFor(isDirect),
+ );
+
+ await wallet.init();
+ wallet.addInitialTokens(true);
+ await wallet.save();
+ return wallet;
+ }
+ }
+
+ @override
+ Future<BaseWallet> restoreFromKeys(EVMChainRestoreWalletFromPrivateKey credentials,
+ {bool? isTestnet}) async {
+ final wallet = BaseWallet(
+ password: credentials.password!,
+ privateKey: credentials.privateKey,
+ walletInfo: credentials.walletInfo!,
+ client: client,
+ encryptionFileUtils: encryptionFileUtilsFor(isDirect),
+ );
+
+ await wallet.init();
+ wallet.addInitialTokens();
+ await wallet.save();
+ return wallet;
+ }
+
+ @override
+ Future<BaseWallet> restoreFromHardwareWallet(
+ EVMChainRestoreWalletFromHardware credentials) async {
+ credentials.walletInfo!.derivationInfo = DerivationInfo(
+ derivationType: DerivationType.bip39,
+ derivationPath: "m/44'/60'/${credentials.hwAccountData.accountIndex}'/0/0");
+ credentials.walletInfo!.hardwareWalletType = credentials.hardwareWalletType;
+ credentials.walletInfo!.address = credentials.hwAccountData.address;
+
+ final wallet = BaseWallet(
+ walletInfo: credentials.walletInfo!,
+ password: credentials.password!,
+ client: client,
+ encryptionFileUtils: encryptionFileUtilsFor(isDirect),
+ );
+
+ await wallet.init();
+ wallet.addInitialTokens();
+ await wallet.save();
+
+ return wallet;
+ }
+
+ @override
+ Future<BaseWallet> restoreFromSeed(EVMChainRestoreWalletFromSeedCredentials credentials,
+ {bool? isTestnet}) async {
+ if (!bip39.validateMnemonic(credentials.mnemonic)) {
+ throw BaseMnemonicIsIncorrectException();
+ }
+
+ final wallet = BaseWallet(
+ password: credentials.password!,
+ mnemonic: credentials.mnemonic,
+ walletInfo: credentials.walletInfo!,
+ passphrase: credentials.passphrase,
+ client: client,
+ encryptionFileUtils: encryptionFileUtilsFor(isDirect),
+ );
+
+ await wallet.init();
+ wallet.addInitialTokens();
+ await wallet.save();
+
+ return wallet;
+ }
+
+ @override
+ Future<void> rename(String currentName, String password, String newName) async {
+ final currentWalletInfo = walletInfoSource.values
+ .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
+ final currentWallet = await BaseWallet.open(
+ password: password,
+ name: currentName,
+ walletInfo: currentWalletInfo,
+ encryptionFileUtils: encryptionFileUtilsFor(isDirect),
+ );
+
+ await currentWallet.renameWalletFiles(newName);
+ await saveBackup(newName);
+
+ final newWalletInfo = currentWalletInfo;
+ newWalletInfo.id = WalletBase.idFor(newName, getType());
+ newWalletInfo.name = newName;
+
+ await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
+ }
+}
diff --git a/cw_base/lib/cw_base.dart b/cw_base/lib/cw_base.dart
new file mode 100644
index 0000000..298576d
--- /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 0000000..3f89992
--- /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<Erc20Token> _defaultTokens = [
+ Erc20Token(
+ name: "USD Coin",
+ symbol: "USDC",
+ contractAddress: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
+ decimal: 6,
+ enabled: true,
+ ),
+ Erc20Token(
+ name: "USDe",
+ symbol: "USDe",
+ contractAddress: "0x5d3a1Ff2b6BAb83b63cd9AD0787074081a52ef34",
+ decimal: 18,
+ enabled: true,
+ ),
+ Erc20Token(
+ name: "Dai",
+ symbol: "DAI",
+ contractAddress: "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
+ decimal: 18,
+ enabled: true,
+ ),
+ Erc20Token(
+ name: "Bridged Tether USD",
+ symbol: "USDT",
+ contractAddress: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2",
+ decimal: 6,
+ enabled: true,
+ ),
+ Erc20Token(
+ name: "Wrapped Ether",
+ symbol: "WETH",
+ contractAddress: "0x4200000000000000000000000000000000000006",
+ decimal: 18,
+ enabled: false,
+ ),
+ Erc20Token(
+ name: "Wrapped BTC",
+ symbol: "WBTC",
+ contractAddress: "0x0555E30da8f98308EdB960aa94C0Db47230d2B9c",
+ decimal: 8,
+ enabled: false,
+ ),
+ Erc20Token(
+ name: "SPX6900",
+ symbol: "SPX",
+ contractAddress: "0x50dA645f148798F68EF2d7dB7C1CB22A6819bb2C",
+ decimal: 8,
+ enabled: false,
+ ),
+ ];
+
+ List<Erc20Token> get initialBaseErc20Tokens => _defaultTokens.map((token) {
+ String? iconPath;
+ try {
+ iconPath = CryptoCurrency.all
+ .firstWhere((element) => element.title.toUpperCase() == token.symbol.toUpperCase())
+ .iconPath;
+ } catch (_) {}
+
+ return Erc20Token.copyWith(token, iconPath, 'BASE');
+ }).toList();
+}
diff --git a/cw_base/pubspec.yaml b/cw_base/pubspec.yaml
new file mode 100644
index 0000000..f94468b
--- /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 0000000..31f5980
--- /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 7a8b3b9..407e405 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 a5c2a81..eb8e5ac 100644
--- a/cw_core/lib/crypto_currency.dart
+++ b/cw_core/lib/crypto_currency.dart
@@ -115,6 +115,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
CryptoCurrency.flip,
CryptoCurrency.ndeps,
CryptoCurrency.deps,
+ CryptoCurrency.baseEth,
];
static const havenCurrencies = [
@@ -241,6 +242,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
static const deps = CryptoCurrency(title: 'DEPS', tag: 'ETH', fullName: 'Decentralized Euro Protocol Share', raw: 101, name: 'deps', iconPath: 'assets/images/deps_icon.png', decimals: 18);
static const kbtc = CryptoCurrency(title: 'KBTC', tag: 'ETH', fullName: 'Kraken Wrapped Bitcoin', raw: 102, name: 'kbtc', iconPath: 'assets/images/kbtc_icon.png', decimals: 8);
static const cbbtc = CryptoCurrency(title: 'CBBTC', tag: 'ETH', fullName: 'Coinbase Wrapped BTC', raw: 103, name: 'cbbtc', iconPath: 'assets/images/cbbtc_icon.png', decimals: 8);
+ static const baseEth = CryptoCurrency(title: 'ETH', tag: 'BASE', fullName: 'Ethereum', raw: 104, name: 'baseth', iconPath: 'assets/images/eth_icon.png', decimals: 18);
static final Map<int, CryptoCurrency> _rawCurrencyMap =
[...all, ...havenCurrencies].fold<Map<int, CryptoCurrency>>(<int, CryptoCurrency>{}, (acc, item) {
diff --git a/cw_core/lib/currency_for_wallet_type.dart b/cw_core/lib/currency_for_wallet_type.dart
index 495731a..33fee93 100644
--- a/cw_core/lib/currency_for_wallet_type.dart
+++ b/cw_core/lib/currency_for_wallet_type.dart
@@ -1,21 +1,23 @@
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/wallet_type.dart';
-CryptoCurrency currencyForWalletType(WalletType type, {bool? isTestnet}) {
+CryptoCurrency walletTypeToCryptoCurrency(WalletType type, {bool isTestnet = false}) {
switch (type) {
+ case WalletType.monero:
+ return CryptoCurrency.xmr;
case WalletType.bitcoin:
- if (isTestnet == true) {
+ if (isTestnet) {
return CryptoCurrency.tbtc;
}
return CryptoCurrency.btc;
- case WalletType.monero:
- return CryptoCurrency.xmr;
case WalletType.litecoin:
return CryptoCurrency.ltc;
case WalletType.haven:
return CryptoCurrency.xhv;
case WalletType.ethereum:
return CryptoCurrency.eth;
+ case WalletType.base:
+ return CryptoCurrency.baseEth;
case WalletType.bitcoinCash:
return CryptoCurrency.bch;
case WalletType.nano:
@@ -38,41 +40,15 @@ CryptoCurrency currencyForWalletType(WalletType type, {bool? isTestnet}) {
return CryptoCurrency.doge;
case WalletType.none:
throw Exception(
- 'Unexpected wallet type: ${type.toString()} for CryptoCurrency currencyForWalletType');
+ 'Unexpected wallet type: ${type.toString()} for CryptoCurrency walletTypeToCryptoCurrency');
}
}
-WalletType? walletTypeForCurrency(CryptoCurrency currency) {
- switch (currency) {
- case CryptoCurrency.btc:
- return WalletType.bitcoin;
- case CryptoCurrency.xmr:
- return WalletType.monero;
- case CryptoCurrency.ltc:
- return WalletType.litecoin;
- case CryptoCurrency.xhv:
- return WalletType.haven;
- case CryptoCurrency.eth:
- return WalletType.ethereum;
- case CryptoCurrency.bch:
- return WalletType.bitcoinCash;
- case CryptoCurrency.nano:
- return WalletType.nano;
- case CryptoCurrency.banano:
- return WalletType.banano;
- case CryptoCurrency.maticpoly:
- return WalletType.polygon;
- case CryptoCurrency.sol:
- return WalletType.solana;
- case CryptoCurrency.trx:
- return WalletType.tron;
- case CryptoCurrency.wow:
- return WalletType.wownero;
- case CryptoCurrency.zano:
- return WalletType.zano;
- case CryptoCurrency.dcr:
- return WalletType.decred;
- default:
- return null;
+extension CryptoCurrencyForWalletType on CryptoCurrency {
+ String? get normalizedIconPath {
+ if (this == CryptoCurrency.baseEth) {
+ return 'assets/images/base_icon.png';
+ }
+ return iconPath;
}
}
diff --git a/cw_core/lib/erc20_token.dart b/cw_core/lib/erc20_token.dart
index fd76d28..7302d4e 100644
--- a/cw_core/lib/erc20_token.dart
+++ b/cw_core/lib/erc20_token.dart
@@ -70,6 +70,7 @@ class Erc20Token extends CryptoCurrency with HiveObjectMixin {
static const boxName = 'Erc20Tokens';
static const ethereumBoxName = 'EthereumErc20Tokens';
static const polygonBoxName = 'PolygonErc20Tokens';
+ static const baseBoxName = 'BaseErc20Tokens';
@override
bool operator ==(other) =>
diff --git a/cw_core/lib/node.dart b/cw_core/lib/node.dart
index ddccced..4908641 100644
--- a/cw_core/lib/node.dart
+++ b/cw_core/lib/node.dart
@@ -106,6 +106,7 @@ class Node extends HiveObject with Keyable {
case WalletType.banano:
case WalletType.ethereum:
case WalletType.polygon:
+ case WalletType.base:
case WalletType.solana:
case WalletType.tron:
case WalletType.zano:
@@ -169,6 +170,7 @@ class Node extends HiveObject with Keyable {
case WalletType.bitcoinCash:
case WalletType.ethereum:
case WalletType.polygon:
+ case WalletType.base:
case WalletType.solana:
case WalletType.tron:
case WalletType.dogecoin:
diff --git a/cw_core/lib/wallet_base.dart b/cw_core/lib/wallet_base.dart
index b492fb0..769b14c 100644
--- a/cw_core/lib/wallet_base.dart
+++ b/cw_core/lib/wallet_base.dart
@@ -24,7 +24,7 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
WalletType get type => walletInfo.type;
- CryptoCurrency get currency => currencyForWalletType(type, isTestnet: isTestnet);
+ CryptoCurrency get currency => walletTypeToCryptoCurrency(type, isTestnet: isTestnet);
String get id => walletInfo.id;
diff --git a/cw_core/lib/wallet_type.dart b/cw_core/lib/wallet_type.dart
index 3f4fa37..01ae372 100644
--- a/cw_core/lib/wallet_type.dart
+++ b/cw_core/lib/wallet_type.dart
@@ -19,6 +19,7 @@ const walletTypes = [
WalletType.zano,
WalletType.decred,
WalletType.dogecoin,
+ WalletType.base,
];
@HiveType(typeId: WALLET_TYPE_TYPE_ID)
@@ -69,7 +70,10 @@ enum WalletType {
decred,
@HiveField(15)
- dogecoin
+ dogecoin,
+
+ @HiveField(16)
+ base,
}
int serializeToInt(WalletType type) {
@@ -104,6 +108,8 @@ int serializeToInt(WalletType type) {
return 13;
case WalletType.dogecoin:
return 14;
+ case WalletType.base:
+ return 15;
case WalletType.none:
return -1;
}
@@ -141,9 +147,10 @@ WalletType deserializeFromInt(int raw) {
return WalletType.decred;
case 14:
return WalletType.dogecoin;
+ case 15:
+ return WalletType.base;
default:
- throw Exception(
- 'Unexpected token: $raw for WalletType deserializeFromInt');
+ throw Exception('Unexpected token: $raw for WalletType deserializeFromInt');
}
}
@@ -179,6 +186,8 @@ String walletTypeToString(WalletType type) {
return 'Decred';
case WalletType.dogecoin:
return 'Dogecoin';
+ case WalletType.base:
+ return 'Base';
case WalletType.none:
return '';
}
@@ -216,52 +225,13 @@ String walletTypeToDisplayName(WalletType type) {
return 'Decred (DCR)';
case WalletType.dogecoin:
return 'Dogecoin (DOGE)';
+ case WalletType.base:
+ return 'Base (BASE)';
case WalletType.none:
return '';
}
}
-CryptoCurrency walletTypeToCryptoCurrency(WalletType type, {bool isTestnet = false}) {
- switch (type) {
- case WalletType.monero:
- return CryptoCurrency.xmr;
- case WalletType.bitcoin:
- if (isTestnet) {
- return CryptoCurrency.tbtc;
- }
- return CryptoCurrency.btc;
- case WalletType.litecoin:
- return CryptoCurrency.ltc;
- case WalletType.haven:
- return CryptoCurrency.xhv;
- case WalletType.ethereum:
- return CryptoCurrency.eth;
- case WalletType.bitcoinCash:
- return CryptoCurrency.bch;
- case WalletType.nano:
- return CryptoCurrency.nano;
- case WalletType.banano:
- return CryptoCurrency.banano;
- case WalletType.polygon:
- return CryptoCurrency.maticpoly;
- case WalletType.solana:
- return CryptoCurrency.sol;
- case WalletType.tron:
- return CryptoCurrency.trx;
- case WalletType.wownero:
- return CryptoCurrency.wow;
- case WalletType.zano:
- return CryptoCurrency.zano;
- case WalletType.decred:
- return CryptoCurrency.dcr;
- case WalletType.dogecoin:
- return CryptoCurrency.doge;
- case WalletType.none:
- throw Exception(
- 'Unexpected wallet type: ${type.toString()} for CryptoCurrency walletTypeToCryptoCurrency');
- }
-}
-
WalletType? cryptoCurrencyToWalletType(CryptoCurrency type) {
switch (type) {
case CryptoCurrency.xmr:
@@ -294,6 +264,8 @@ WalletType? cryptoCurrencyToWalletType(CryptoCurrency type) {
return WalletType.decred;
case CryptoCurrency.doge:
return WalletType.dogecoin;
+ case CryptoCurrency.baseEth:
+ return WalletType.base;
default:
return null;
}
diff --git a/cw_decred/lib/transaction_info.dart b/cw_decred/lib/transaction_info.dart
index 4afe09e..1891317 100644
--- a/cw_decred/lib/transaction_info.dart
+++ b/cw_decred/lib/transaction_info.dart
@@ -1,3 +1,4 @@
+import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/format_amount.dart';
diff --git a/cw_evm/lib/evm_chain_client.dart b/cw_evm/lib/evm_chain_client.dart
index 29a88dc..814979d 100644
--- a/cw_evm/lib/evm_chain_client.dart
+++ b/cw_evm/lib/evm_chain_client.dart
@@ -39,15 +39,14 @@ abstract class EVMChainClient {
Uri? rpcUri;
bool isModifiedNodeUri = false;
- if (node.uriRaw == 'eth.nownodes.io' || node.uriRaw == 'matic.nownodes.io') {
+ if (node.uriRaw.contains('nownodes.io')) {
isModifiedNodeUri = true;
String nowNodeApiKey = secrets.nowNodesApiKey;
rpcUri = Uri.https(node.uriRaw, '/$nowNodeApiKey');
}
- _client =
- Web3Client(isModifiedNodeUri ? rpcUri!.toString() : node.uri.toString(), client);
+ _client = Web3Client(isModifiedNodeUri ? rpcUri!.toString() : node.uri.toString(), client);
return true;
} catch (e) {
@@ -127,10 +126,11 @@ abstract class EVMChainClient {
final gasEstimate = await _client!.estimateGas(
sender: senderAddress,
to: EthereumAddress.fromHex(contractAddress),
- data: data ?? transfer.encodeCall([
- toAddress,
- value.getInWei,
- ]),
+ data: data ??
+ transfer.encodeCall([
+ toAddress,
+ value.getInWei,
+ ]),
);
return gasEstimate.toInt();
@@ -171,10 +171,10 @@ abstract class EVMChainClient {
int? gasPrice,
}) async {
assert(currency == CryptoCurrency.eth ||
- currency == CryptoCurrency.maticpoly ||
+ currency == CryptoCurrency.maticpoly || currency == CryptoCurrency.baseEth ||
contractAddress != null);
- bool isNativeToken = currency == CryptoCurrency.eth || currency == CryptoCurrency.maticpoly;
+ bool isNativeToken = currency == CryptoCurrency.eth || currency == CryptoCurrency.maticpoly || currency == CryptoCurrency.baseEth;
final Transaction transaction = createTransaction(
from: privateKey.address,
@@ -233,7 +233,6 @@ abstract class EVMChainClient {
required String contractAddress,
int? gasPrice,
}) async {
-
final Transaction transaction = createTransaction(
from: privateKey.address,
to: EthereumAddress.fromHex(contractAddress),
@@ -264,7 +263,8 @@ abstract class EVMChainClient {
feeCurrency: feeCurrency,
sendTransaction: () => sendTransaction(signedTransaction),
exponent: exponent,
- isInfiniteApproval: amount.toRadixString(16) == 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
+ isInfiniteApproval: amount.toRadixString(16) ==
+ 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
);
}
@@ -342,7 +342,7 @@ abstract class EVMChainClient {
final erc20 = ERC20(address: EthereumAddress.fromHex(contractAddress), client: _client!);
final balance = await erc20.balanceOf(userAddress);
- int exponent = (await erc20.decimals()).toInt();
+ int exponent = (await erc20.decimals()).toInt();
return EVMChainERC20Balance(balance, exponent: exponent);
} on RangeError catch (_) {
diff --git a/cw_evm/lib/evm_chain_wallet.dart b/cw_evm/lib/evm_chain_wallet.dart
index 35420a6..bc6bd37 100644
--- a/cw_evm/lib/evm_chain_wallet.dart
+++ b/cw_evm/lib/evm_chain_wallet.dart
@@ -171,25 +171,29 @@ abstract class EVMChainWalletBase
try {
// Check native balance
await _client.getBalance(_evmChainPrivateKey.address, throwOnError: true);
-
+
// Check USDC token balance
String usdcContractAddress;
- if (_client.chainId == 1) {
- // Ethereum mainnet
- usdcContractAddress = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48";
- } else if (_client.chainId == 137) {
- // Polygon mainnet
- usdcContractAddress = "0x2791bca1f2de4661ed88a30c99a7a9449aa84174";
- } else {
- // If we are not on Ethereum or Polygon, we skip ERC20 token check
- return true;
+
+ switch (_client.chainId) {
+ case 1:
+ usdcContractAddress = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48";
+ break;
+ case 137:
+ usdcContractAddress = "0x2791bca1f2de4661ed88a30c99a7a9449aa84174";
+ break;
+ case 8453:
+ usdcContractAddress = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
+ break;
+ default:
+ return true;
}
-
+
await _client.fetchERC20Balances(
_evmChainPrivateKey.address,
usdcContractAddress,
);
-
+
return true;
} catch (e) {
return false;
@@ -542,6 +546,32 @@ abstract class EVMChainWalletBase
maxFeePerGasForTransaction = gasFeesModel.maxFeePerGas;
if (output.sendAll && transactionCurrency is! Erc20Token) {
+ if (_client.chainId == 8453) {
+ // Add a safety buffer for Base chain to account for gas price fluctuations
+ // Use 1% buffer or minimum 1000 wei (0.000001 ETH), whichever is higher
+ final gasBufferPercent =
+ estimatedFeesForTransaction * BigInt.from(101) ~/ BigInt.from(100);
+ final gasBufferMin = estimatedFeesForTransaction + BigInt.from(1000);
+ final gasBuffer = gasBufferPercent > gasBufferMin ? gasBufferPercent : gasBufferMin;
+
+ totalAmount = (currencyBalance.balance - gasBuffer);
+
+ // Re-estimate gas with the correct amount to get more accurate gas estimation
+ final refinedGasFeesModel = await calculateActualEstimatedFeeForCreateTransaction(
+ amount: totalAmount,
+ receivingAddressHex: toAddress,
+ priority: _credentials.priority!,
+ contractAddress: contractAddress,
+ );
+
+ // Use the higher of the two gas estimations to be safe
+ final refinedGasFee = BigInt.from(refinedGasFeesModel.estimatedGasFee);
+ estimatedFeesForTransaction = refinedGasFee > gasBuffer ? refinedGasFee : gasBuffer;
+ estimatedGasUnitsForTransaction = refinedGasFeesModel.estimatedGasUnits;
+ maxFeePerGasForTransaction = refinedGasFeesModel.maxFeePerGas;
+ }
+
+ // Final amount calculation with the higher gas estimation
totalAmount = (currencyBalance.balance - estimatedFeesForTransaction);
}
@@ -569,11 +599,7 @@ abstract class EVMChainWalletBase
gasFee: estimatedFeesForTransaction,
priority: _credentials.priority!,
currency: transactionCurrency,
- feeCurrency: switch (_client.chainId) {
- 1 => "ETH",
- 137 => "POL",
- _ => ""
- },
+ feeCurrency: switch (_client.chainId) { 1 => "ETH", 137 => "POL", 8453 => "ETH", _ => "ETH" },
maxFeePerGas: maxFeePerGasForTransaction,
exponent: exponent,
contractAddress:
@@ -890,6 +916,8 @@ abstract class EVMChainWalletBase
/// EtherScan for Ethereum.
///
/// PolygonScan for Polygon.
+ ///
+ /// BaseScan for Base.
void updateScanProviderUsageState(bool isEnabled) {
if (isEnabled) {
_updateTransactions();
diff --git a/integration_test/components/common_test_flows.dart b/integration_test/components/common_test_flows.dart
index 9cd3b36..30cfe4d 100644
--- a/integration_test/components/common_test_flows.dart
+++ b/integration_test/components/common_test_flows.dart
@@ -3,6 +3,7 @@ import 'dart:io';
import 'package:cake_wallet/entities/seed_type.dart';
import 'package:cake_wallet/reactions/wallet_utils.dart';
import 'package:cake_wallet/wallet_types.g.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
@@ -109,7 +110,7 @@ class CommonTestFlows {
void confirmAllAvailableWalletTypeIconsDisplayCorrectly() {
for (var walletType in availableWalletTypes) {
- final imageUrl = walletTypeToCryptoCurrency(walletType).iconPath;
+ final imageUrl = walletTypeToCryptoCurrency(walletType).normalizedIconPath;
final walletIconFinder = find.image(
Image.asset(
@@ -353,6 +354,8 @@ class CommonTestFlows {
return secrets.polygonTestWalletSeeds;
case WalletType.solana:
return secrets.solanaTestWalletSeeds;
+ case WalletType.base:
+ return secrets.baseTestWalletSeeds;
case WalletType.tron:
return secrets.tronTestWalletSeeds;
case WalletType.nano:
@@ -387,6 +390,8 @@ class CommonTestFlows {
return secrets.bitcoinCashTestWalletReceiveAddress;
case WalletType.polygon:
return secrets.polygonTestWalletReceiveAddress;
+ case WalletType.base:
+ return secrets.baseTestWalletReceiveAddress;
case WalletType.solana:
return secrets.solanaTestWalletReceiveAddress;
case WalletType.tron:
diff --git a/ios/Runner/InfoBase.plist b/ios/Runner/InfoBase.plist
index 4a95339..3b11e21 100644
--- a/ios/Runner/InfoBase.plist
+++ b/ios/Runner/InfoBase.plist
@@ -302,6 +302,26 @@
<string>dogecoin-wallet</string>
</array>
</dict>
+ <dict>
+ <key>CFBundleTypeRole</key>
+ <string>Editor</string>
+ <key>CFBundleURLName</key>
+ <string>base</string>
+ <key>CFBundleURLSchemes</key>
+ <array>
+ <string>base</string>
+ </array>
+ </dict>
+ <dict>
+ <key>CFBundleTypeRole</key>
+ <string>Viewer</string>
+ <key>CFBundleURLName</key>
+ <string>base-wallet</string>
+ <key>CFBundleURLSchemes</key>
+ <array>
+ <string>base-wallet</string>
+ </array>
+ </dict>
</array>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
diff --git a/lib/base/cw_base.dart b/lib/base/cw_base.dart
new file mode 100644
index 0000000..02b9669
--- /dev/null
+++ b/lib/base/cw_base.dart
@@ -0,0 +1,238 @@
+part of 'base.dart';
+
+class CWBase extends Base {
+ @override
+ List<String> getBaseWordList(String language) => EVMChainMnemonics.englishWordlist;
+
+ WalletService createBaseWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) =>
+ BaseWalletService(walletInfoSource, isDirect, client: BaseClient());
+
+ @override
+ WalletCredentials createBaseNewWalletCredentials({
+ required String name,
+ String? mnemonic,
+ WalletInfo? walletInfo,
+ String? password,
+ String? passphrase,
+ }) =>
+ EVMChainNewWalletCredentials(
+ name: name,
+ walletInfo: walletInfo,
+ password: password,
+ mnemonic: mnemonic,
+ passphrase: passphrase,
+ );
+
+ @override
+ WalletCredentials createBaseRestoreWalletFromSeedCredentials({
+ required String name,
+ required String mnemonic,
+ required String password,
+ String? passphrase,
+ }) =>
+ EVMChainRestoreWalletFromSeedCredentials(
+ name: name,
+ password: password,
+ mnemonic: mnemonic,
+ passphrase: passphrase,
+ );
+
+ @override
+ WalletCredentials createBaseRestoreWalletFromPrivateKey({
+ required String name,
+ required String privateKey,
+ required String password,
+ }) =>
+ EVMChainRestoreWalletFromPrivateKey(name: name, password: password, privateKey: privateKey);
+
+ @override
+ WalletCredentials createBaseHardwareWalletCredentials({
+ required String name,
+ required HardwareAccountData hwAccountData,
+ WalletInfo? walletInfo,
+ }) =>
+ EVMChainRestoreWalletFromHardware(
+ name: name,
+ hwAccountData: hwAccountData,
+ walletInfo: walletInfo,
+ );
+
+ @override
+ String getAddress(WalletBase wallet) => (wallet as BaseWallet).walletAddresses.address;
+
+ @override
+ String getPrivateKey(WalletBase wallet) {
+ final privateKeyHolder = (wallet as BaseWallet).evmChainPrivateKey;
+ if (privateKeyHolder is EthPrivateKey) return bytesToHex(privateKeyHolder.privateKey);
+ return "";
+ }
+
+ @override
+ String getPublicKey(WalletBase wallet) {
+ final privateKeyInUnitInt = (wallet as BaseWallet).evmChainPrivateKey;
+ return privateKeyInUnitInt.address.hex;
+ }
+
+ @override
+ TransactionPriority getDefaultTransactionPriority() => EVMChainTransactionPriority.medium;
+
+ @override
+ TransactionPriority getBaseTransactionPrioritySlow() => EVMChainTransactionPriority.slow;
+
+ @override
+ List<TransactionPriority> getTransactionPriorities() => EVMChainTransactionPriority.all;
+
+ @override
+ TransactionPriority deserializeBaseTransactionPriority(int raw) =>
+ EVMChainTransactionPriority.deserialize(raw: raw);
+
+ Object createBaseTransactionCredentials(
+ List<Output> outputs, {
+ required TransactionPriority priority,
+ required CryptoCurrency currency,
+ int? feeRate,
+ }) =>
+ EVMChainTransactionCredentials(
+ outputs
+ .map(
+ (out) => OutputInfo(
+ fiatAmount: out.fiatAmount,
+ cryptoAmount: out.cryptoAmount,
+ address: out.address,
+ note: out.note,
+ sendAll: out.sendAll,
+ extractedAddress: out.extractedAddress,
+ isParsedAddress: out.isParsedAddress,
+ formattedCryptoAmount: out.formattedCryptoAmount,
+ ),
+ )
+ .toList(),
+ priority: priority as EVMChainTransactionPriority,
+ currency: currency,
+ feeRate: feeRate,
+ );
+
+ Object createBaseTransactionCredentialsRaw(
+ List<OutputInfo> outputs, {
+ TransactionPriority? priority,
+ required CryptoCurrency currency,
+ required int feeRate,
+ }) =>
+ EVMChainTransactionCredentials(
+ outputs,
+ priority: priority as EVMChainTransactionPriority?,
+ currency: currency,
+ feeRate: feeRate,
+ );
+
+ @override
+ int formatterBaseParseAmount(String amount) => EVMChainFormatter.parseEVMChainAmount(amount);
+
+ @override
+ double formatterBaseAmountToDouble({
+ TransactionInfo? transaction,
+ BigInt? amount,
+ int exponent = 18,
+ }) {
+ assert(transaction != null || amount != null);
+
+ if (transaction != null) {
+ transaction as EVMChainTransactionInfo;
+ return transaction.ethAmount / BigInt.from(10).pow(transaction.exponent);
+ } else {
+ return (amount!) / BigInt.from(10).pow(exponent);
+ }
+ }
+
+ @override
+ List<Erc20Token> getERC20Currencies(WalletBase wallet) => (wallet as BaseWallet).erc20Currencies;
+
+ @override
+ Future<void> addErc20Token(WalletBase wallet, CryptoCurrency token) =>
+ (wallet as BaseWallet).addErc20Token(token as Erc20Token);
+
+ @override
+ Future<void> deleteErc20Token(WalletBase wallet, CryptoCurrency token) =>
+ (wallet as BaseWallet).deleteErc20Token(token as Erc20Token);
+
+ @override
+ Future<void> removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token) =>
+ (wallet as BaseWallet).removeTokenTransactionsInHistory(token as Erc20Token);
+
+ @override
+ Future<Erc20Token?> getErc20Token(WalletBase wallet, String contractAddress) =>
+ (wallet as BaseWallet).getErc20Token(contractAddress, 'base');
+
+ @override
+ CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction) {
+ transaction as EVMChainTransactionInfo;
+ if (transaction.tokenSymbol == CryptoCurrency.baseEth.title ||
+ transaction.tokenSymbol == "BASE") {
+ return CryptoCurrency.baseEth;
+ }
+
+ wallet as BaseWallet;
+
+ return wallet.erc20Currencies.firstWhere(
+ (element) =>
+ transaction.contractAddress?.toLowerCase() == element.contractAddress?.toLowerCase(),
+ );
+ }
+
+ @override
+ void updateBaseScanUsageState(WalletBase wallet, bool isEnabled) =>
+ (wallet as BaseWallet).updateScanProviderUsageState(isEnabled);
+
+ @override
+ Web3Client? getWeb3Client(WalletBase wallet) => (wallet as BaseWallet).getWeb3Client();
+
+ @override
+ String getTokenAddress(CryptoCurrency asset) => (asset as Erc20Token).contractAddress;
+
+ @override
+ Future<PendingTransaction> createTokenApproval(
+ WalletBase wallet,
+ BigInt amount,
+ String spender,
+ CryptoCurrency token,
+ TransactionPriority priority,
+ ) =>
+ (wallet as EVMChainWallet).createApprovalTransaction(
+ amount,
+ spender,
+ token,
+ priority as EVMChainTransactionPriority,
+ "BASE",
+ );
+
+ @override
+ void setHardwareWalletService(WalletBase wallet, HardwareWalletService service) {
+ if (service is EVMChainLedgerService) {
+ ((wallet as EVMChainWallet).evmChainPrivateKey as EvmLedgerCredentials).setLedgerConnection(
+ service.ledgerConnection, wallet.walletInfo.derivationInfo?.derivationPath);
+ } else if (service is EVMChainBitboxService) {
+ ((wallet as EVMChainWallet).evmChainPrivateKey as EvmBitboxCredentials)
+ .setBitbox(service.manager, wallet.walletInfo.derivationInfo?.derivationPath);
+ }
+ }
+
+ @override
+ HardwareWalletService getLedgerHardwareWalletService(ledger.LedgerConnection connection) =>
+ EVMChainLedgerService(connection);
+
+ @override
+ HardwareWalletService getBitboxHardwareWalletService(bitbox.BitboxManager manager) =>
+ EVMChainBitboxService(manager, chainId: 8453);
+
+ @override
+ List<String> getDefaultTokenContractAddresses() =>
+ DefaultBaseErc20Tokens().initialBaseErc20Tokens.map((e) => e.contractAddress).toList();
+
+ @override
+ bool isTokenAlreadyAdded(WalletBase wallet, String contractAddress) {
+ final baseWallet = wallet as BaseWallet;
+ return baseWallet.erc20Currencies.any(
+ (element) => element.contractAddress.toLowerCase() == contractAddress.toLowerCase(),
+ );
+ }
+}
diff --git a/lib/buy/dfx/dfx_buy_provider.dart b/lib/buy/dfx/dfx_buy_provider.dart
index c88ea37..c590d82 100644
--- a/lib/buy/dfx/dfx_buy_provider.dart
+++ b/lib/buy/dfx/dfx_buy_provider.dart
@@ -135,6 +135,7 @@ class DFXBuyProvider extends BuyProvider {
switch (wallet.type) {
case WalletType.ethereum:
case WalletType.polygon:
+ case WalletType.base:
case WalletType.solana:
case WalletType.tron:
return wallet.signMessage(message);
diff --git a/lib/buy/kryptonim/kryptonim.dart b/lib/buy/kryptonim/kryptonim.dart
index d796381..cbe6846 100644
--- a/lib/buy/kryptonim/kryptonim.dart
+++ b/lib/buy/kryptonim/kryptonim.dart
@@ -218,6 +218,7 @@ class KryptonimBuyProvider extends BuyProvider {
String _normalizeBlockChain(CryptoCurrency cur) {
String? blockchain = switch (cur.tag) {
'ETH' => 'Ethereum',
+ 'BASE' => 'Base',
'POL' => 'Polygon',
'AVAXC' => 'Avalanche',
'SOL' => 'Solana',
@@ -229,6 +230,7 @@ class KryptonimBuyProvider extends BuyProvider {
CryptoCurrency.btc => 'Bitcoin',
CryptoCurrency.ltc => 'Litecoin',
CryptoCurrency.eth => 'Ethereum',
+ CryptoCurrency.baseEth => 'Base',
CryptoCurrency.maticpoly => 'Matic',
_ => null,
};
diff --git a/lib/buy/moonpay/moonpay_provider.dart b/lib/buy/moonpay/moonpay_provider.dart
index 8e33044..228f0dc 100644
--- a/lib/buy/moonpay/moonpay_provider.dart
+++ b/lib/buy/moonpay/moonpay_provider.dart
@@ -17,6 +17,7 @@ import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/themes/core/material_base_theme.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/wallet_base.dart';
diff --git a/lib/buy/robinhood/robinhood_buy_provider.dart b/lib/buy/robinhood/robinhood_buy_provider.dart
index 5ae7832..ee529d3 100644
--- a/lib/buy/robinhood/robinhood_buy_provider.dart
+++ b/lib/buy/robinhood/robinhood_buy_provider.dart
@@ -105,6 +105,7 @@ class RobinhoodBuyProvider extends BuyProvider {
switch (wallet.type) {
case WalletType.ethereum:
case WalletType.polygon:
+ case WalletType.base:
case WalletType.solana:
case WalletType.tron:
case WalletType.dogecoin:
diff --git a/lib/buy/wyre/wyre_buy_provider.dart b/lib/buy/wyre/wyre_buy_provider.dart
index a0cf1ed..a08aff1 100644
--- a/lib/buy/wyre/wyre_buy_provider.dart
+++ b/lib/buy/wyre/wyre_buy_provider.dart
@@ -4,6 +4,7 @@ import 'package:cake_wallet/order/order_source_description.dart';
import 'package:cake_wallet/buy/pairs_utils.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cw_core/crypto_currency.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cake_wallet/buy/buy_amount.dart';
import 'package:cake_wallet/buy/buy_provider.dart';
diff --git a/lib/core/payment_uris.dart b/lib/core/payment_uris.dart
index 8aa2616..be4e790 100644
--- a/lib/core/payment_uris.dart
+++ b/lib/core/payment_uris.dart
@@ -86,6 +86,21 @@ class EthereumURI extends PaymentURI {
}
}
+class BaseURI extends PaymentURI {
+ BaseURI({required super.amount, required super.address});
+
+ @override
+ String toString() {
+ var base = 'base:$address';
+
+ if (amount.isNotEmpty) {
+ base += '?amount=${amount.replaceAll(',', '.')}';
+ }
+
+ return base;
+ }
+}
+
class BitcoinCashURI extends PaymentURI {
BitcoinCashURI({required super.amount, required super.address});
diff --git a/lib/core/seed_validator.dart b/lib/core/seed_validator.dart
index 2e35e7f..2643fd0 100644
--- a/lib/core/seed_validator.dart
+++ b/lib/core/seed_validator.dart
@@ -1,3 +1,4 @@
+import 'package:cake_wallet/base/base.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/core/validator.dart';
import 'package:cake_wallet/entities/mnemonic_item.dart';
@@ -52,6 +53,8 @@ class SeedValidator extends Validator<MnemonicItem> {
return zano!.getWordList(language);
case WalletType.decred:
return decred!.getDecredWordList();
+ case WalletType.base:
+ return base!.getBaseWordList(language);
case WalletType.none:
case WalletType.haven:
return [];
diff --git a/lib/core/wallet_creation_service.dart b/lib/core/wallet_creation_service.dart
index 2cf7a7c..254f8a1 100644
--- a/lib/core/wallet_creation_service.dart
+++ b/lib/core/wallet_creation_service.dart
@@ -80,6 +80,7 @@ class WalletCreationService {
case WalletType.bitcoinCash:
case WalletType.ethereum:
case WalletType.polygon:
+ case WalletType.base:
case WalletType.solana:
case WalletType.tron:
case WalletType.dogecoin:
diff --git a/lib/di.dart b/lib/di.dart
index 01312dd..8f0a712 100644
--- a/lib/di.dart
+++ b/lib/di.dart
@@ -5,6 +5,7 @@ import 'package:cake_wallet/anonpay/anonpay_api.dart';
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
import 'package:cake_wallet/anypay/anypay_api.dart';
+import 'package:cake_wallet/base/base.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
import 'package:cake_wallet/buy/dfx/dfx_buy_provider.dart';
@@ -1241,6 +1242,8 @@ Future<void> setup({
return zano!.createZanoWalletService(_walletInfoSource);
case WalletType.decred:
return decred!.createDecredWalletService(_walletInfoSource, _unspentCoinsInfoSource);
+ case WalletType.base:
+ return base!.createBaseWalletService(_walletInfoSource, SettingsStoreBase.walletPasswordDirectInput);
case WalletType.haven:
return HavenWalletService(_walletInfoSource);
case WalletType.none:
diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart
index 6077ade..c25891e 100644
--- a/lib/entities/default_settings_migration.dart
+++ b/lib/entities/default_settings_migration.dart
@@ -47,6 +47,7 @@ const zanoDefaultNodeUri = '37.27.100.59:10500';
const moneroWorldNodeUri = '.moneroworld.com';
const decredDefaultUri = "default-spv-nodes";
const dogecoinDefaultNodeUri = 'dogecoin.stackwallet.com:50022';
+const baseDefaultNodeUri = 'base.nownodes.io';
Future<void> defaultSettingsMigration(
{required int version,
@@ -534,6 +535,15 @@ Future<void> defaultSettingsMigration(
currentNodePreferenceKey: PreferencesKey.currentDogecoinNodeIdKey,
);
break;
+ case 52:
+ await addWalletNodeList(nodes: nodes, type: WalletType.base);
+ await _changeDefaultNode(
+ nodes: nodes,
+ sharedPreferences: sharedPreferences,
+ type: WalletType.base,
+ currentNodePreferenceKey: PreferencesKey.currentBaseNodeIdKey,
+ );
+ break;
default:
break;
}
@@ -642,6 +652,8 @@ String _getDefaultNodeUri(WalletType type) {
return decredDefaultUri;
case WalletType.dogecoin:
return dogecoinDefaultNodeUri;
+ case WalletType.base:
+ return baseDefaultNodeUri;
case WalletType.banano:
case WalletType.none:
return '';
@@ -1041,6 +1053,7 @@ Future<void> checkCurrentNodes(
final currentHavenNodeId = sharedPreferences.getInt(PreferencesKey.currentHavenNodeIdKey);
final currentEthereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
final currentPolygonNodeId = sharedPreferences.getInt(PreferencesKey.currentPolygonNodeIdKey);
+ final currentBaseNodeId = sharedPreferences.getInt(PreferencesKey.currentBaseNodeIdKey);
final currentNanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
final currentNanoPowNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoPowNodeIdKey);
final currentDecredNodeId = sharedPreferences.getInt(PreferencesKey.currentDecredNodeIdKey);
@@ -1064,6 +1077,8 @@ Future<void> checkCurrentNodes(
nodeSource.values.firstWhereOrNull((node) => node.key == currentEthereumNodeId);
final currentPolygonNodeServer =
nodeSource.values.firstWhereOrNull((node) => node.key == currentPolygonNodeId);
+ final currentBaseNodeServer =
+ nodeSource.values.firstWhereOrNull((node) => node.key == currentBaseNodeId);
final currentNanoNodeServer =
nodeSource.values.firstWhereOrNull((node) => node.key == currentNanoNodeId);
final currentDecredNodeServer =
@@ -1155,6 +1170,12 @@ Future<void> checkCurrentNodes(
await sharedPreferences.setInt(PreferencesKey.currentPolygonNodeIdKey, node.key as int);
}
+ if (currentBaseNodeServer == null) {
+ final node = Node(uri: baseDefaultNodeUri, type: WalletType.base);
+ await nodeSource.add(node);
+ await sharedPreferences.setInt(PreferencesKey.currentBaseNodeIdKey, node.key as int);
+ }
+
if (currentSolanaNodeServer == null) {
final node = Node(uri: solanaDefaultNodeUri, type: WalletType.solana);
await nodeSource.add(node);
diff --git a/lib/entities/ens_record.dart b/lib/entities/ens_record.dart
index 78b0f41..b49afc0 100644
--- a/lib/entities/ens_record.dart
+++ b/lib/entities/ens_record.dart
@@ -1,3 +1,4 @@
+import 'package:cake_wallet/base/base.dart';
import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
@@ -21,6 +22,10 @@ class EnsRecord {
_client = polygon!.getWeb3Client(wallet);
}
+ if (wallet != null && wallet.type == WalletType.base) {
+ _client = base!.getWeb3Client(wallet);
+ }
+
if (_client == null) {
late final client = ProxyWrapper().getHttpIOClient();
@@ -42,6 +47,7 @@ class EnsRecord {
return await ens.withName(name).getCoinAddress(CoinType.XHV);
case WalletType.ethereum:
case WalletType.polygon:
+ case WalletType.base:
default:
return (await ens.withName(name).getAddress()).hex;
}
diff --git a/lib/entities/evm_transaction_error_fees_handler.dart b/lib/entities/evm_transaction_error_fees_handler.dart
index b802f98..85b40be 100644
--- a/lib/entities/evm_transaction_error_fees_handler.dart
+++ b/lib/entities/evm_transaction_error_fees_handler.dart
@@ -32,6 +32,50 @@ class EVMTransactionErrorFeesHandler {
String errorMessage,
double assetPriceUsd,
) {
+ // Pattern: "insufficient funds for gas * price + value: have 4728796358953246 want 4728796842182575"
+ RegExp insufficientFundsRegExp = RegExp(r'have (\d+) want (\d+)');
+ Match? insufficientFundsMatch = insufficientFundsRegExp.firstMatch(errorMessage);
+
+ if (insufficientFundsMatch != null) {
+ try {
+ // Extract the numerical strings from the new format
+ String balanceStr = insufficientFundsMatch.group(1)!;
+ String requiredStr = insufficientFundsMatch.group(2)!;
+
+ // Parse the numerical strings to BigInt
+ BigInt balanceWei = BigInt.parse(balanceStr);
+ BigInt requiredWei = BigInt.parse(requiredStr);
+
+ // Calculate overshot (how much more is needed)
+ BigInt overshotWei = requiredWei - balanceWei;
+
+ // The transaction cost is the required amount
+ BigInt txCostWei = requiredWei;
+
+ // Convert wei to ETH (1 ETH = 1e18 wei)
+ double balanceEth = balanceWei.toDouble() / 1e18;
+ double txCostEth = txCostWei.toDouble() / 1e18;
+ double overshotEth = overshotWei.toDouble() / 1e18;
+
+ // Calculate the USD values
+ double balanceUsd = balanceEth * assetPriceUsd;
+ double txCostUsd = txCostEth * assetPriceUsd;
+ double overshotUsd = overshotEth * assetPriceUsd;
+
+ return EVMTransactionErrorFeesHandler(
+ balanceWei: balanceWei.toString(),
+ balanceEth: balanceEth.toString().safeSubString(0, 12),
+ balanceUsd: balanceUsd.toString().safeSubString(0, 4),
+ txCostWei: txCostWei.toString(),
+ txCostEth: txCostEth.toString().safeSubString(0, 12),
+ txCostUsd: txCostUsd.toString().safeSubString(0, 4),
+ overshotWei: overshotWei.toString(),
+ overshotEth: overshotEth.toString().safeSubString(0, 12),
+ overshotUsd: overshotUsd.toString().safeSubString(0, 4),
+ );
+ } catch (e) {}
+ }
+
// Define Regular Expressions to extract the numerical values
RegExp balanceRegExp = RegExp(r'balance (\d+)');
RegExp txCostRegExp = RegExp(r'tx cost (\d+)');
@@ -44,40 +88,44 @@ class EVMTransactionErrorFeesHandler {
// Check if all required values are found
if (balanceMatch != null && txCostMatch != null && overshotMatch != null) {
- // Extract the numerical strings
- String balanceStr = balanceMatch.group(1)!;
- String txCostStr = txCostMatch.group(1)!;
- String overshotStr = overshotMatch.group(1)!;
-
- // Parse the numerical strings to BigInt
- BigInt balanceWei = BigInt.parse(balanceStr);
- BigInt txCostWei = BigInt.parse(txCostStr);
- BigInt overshotWei = BigInt.parse(overshotStr);
-
- // Convert wei to ETH (1 ETH = 1e18 wei)
- double balanceEth = balanceWei.toDouble() / 1e18;
- double txCostEth = txCostWei.toDouble() / 1e18;
- double overshotEth = overshotWei.toDouble() / 1e18;
-
- // Calculate the USD values
- double balanceUsd = balanceEth * assetPriceUsd;
- double txCostUsd = txCostEth * assetPriceUsd;
- double overshotUsd = overshotEth * assetPriceUsd;
-
- return EVMTransactionErrorFeesHandler(
- balanceWei: balanceWei.toString(),
- balanceEth: balanceEth.toString().safeSubString(0, 12),
- balanceUsd: balanceUsd.toString().safeSubString(0, 4),
- txCostWei: txCostWei.toString(),
- txCostEth: txCostEth.toString().safeSubString(0, 12),
- txCostUsd: txCostUsd.toString().safeSubString(0, 4),
- overshotWei: overshotWei.toString(),
- overshotEth: overshotEth.toString().safeSubString(0, 12),
- overshotUsd: overshotUsd.toString().safeSubString(0, 4),
- );
- } else {
- // If any value is missing, return an error message
- return EVMTransactionErrorFeesHandler(error: 'Could not parse the error message.');
+ try {
+ // Extract the numerical strings
+ String balanceStr = balanceMatch.group(1)!;
+ String txCostStr = txCostMatch.group(1)!;
+ String overshotStr = overshotMatch.group(1)!;
+
+ // Parse the numerical strings to BigInt
+ BigInt balanceWei = BigInt.parse(balanceStr);
+ BigInt txCostWei = BigInt.parse(txCostStr);
+ BigInt overshotWei = BigInt.parse(overshotStr);
+
+ // Convert wei to ETH (1 ETH = 1e18 wei)
+ double balanceEth = balanceWei.toDouble() / 1e18;
+ double txCostEth = txCostWei.toDouble() / 1e18;
+ double overshotEth = overshotWei.toDouble() / 1e18;
+
+ // Calculate the USD values
+ double balanceUsd = balanceEth * assetPriceUsd;
+ double txCostUsd = txCostEth * assetPriceUsd;
+ double overshotUsd = overshotEth * assetPriceUsd;
+
+ return EVMTransactionErrorFeesHandler(
+ balanceWei: balanceWei.toString(),
+ balanceEth: balanceEth.toString().safeSubString(0, 12),
+ balanceUsd: balanceUsd.toString().safeSubString(0, 4),
+ txCostWei: txCostWei.toString(),
+ txCostEth: txCostEth.toString().safeSubString(0, 12),
+ txCostUsd: txCostUsd.toString().safeSubString(0, 4),
+ overshotWei: overshotWei.toString(),
+ overshotEth: overshotEth.toString().safeSubString(0, 12),
+ overshotUsd: overshotUsd.toString().safeSubString(0, 4),
+ );
+ } catch (e) {
+ // If parsing fails, continue to error case
+ }
}
+
+ // If both parsing attempts fail, return an error message
+ return EVMTransactionErrorFeesHandler(error: 'Could not parse the error message.');
}
}
diff --git a/lib/entities/node_list.dart b/lib/entities/node_list.dart
index 40843be..90bb9dd 100644
--- a/lib/entities/node_list.dart
+++ b/lib/entities/node_list.dart
@@ -49,6 +49,9 @@ Future<List<Node>> loadDefaultNodes(WalletType type) async {
case WalletType.dogecoin:
path = 'assets/dogecoin_electrum_server_list.yml';
break;
+ case WalletType.base:
+ path = 'assets/base_node_list.yml';
+ break;
case WalletType.banano:
case WalletType.none:
path = '';
@@ -100,6 +103,7 @@ Future<void> resetToDefault(Box<Node> nodeSource) async {
final decredNodes = await loadDefaultNodes(WalletType.decred);
final zanoNodes = await loadDefaultNodes(WalletType.zano);
final dogecoinElectrumServerList = await loadDefaultNodes(WalletType.dogecoin);
+ final baseNodes = await loadDefaultNodes(WalletType.base);
final nodes = moneroNodes +
bitcoinElectrumServerList +
@@ -113,7 +117,8 @@ Future<void> resetToDefault(Box<Node> nodeSource) async {
tronNodes +
zanoNodes +
decredNodes +
- dogecoinElectrumServerList;
+ dogecoinElectrumServerList +
+ baseNodes;
await nodeSource.clear();
await nodeSource.addAll(nodes);
diff --git a/lib/entities/parse_address_from_domain.dart b/lib/entities/parse_address_from_domain.dart
index b2621a6..cbcf62d 100644
--- a/lib/entities/parse_address_from_domain.dart
+++ b/lib/entities/parse_address_from_domain.dart
@@ -114,6 +114,7 @@ class AddressResolver {
"podcast",
"pog",
"polygon",
+ "base",
"press",
"privacy",
"pro",
diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart
index b607ce7..eb678a0 100644
--- a/lib/entities/preferences_key.dart
+++ b/lib/entities/preferences_key.dart
@@ -8,6 +8,7 @@ class PreferencesKey {
static const currentZanoNodeIdKey = 'current_node_id_zano';
static const currentEthereumNodeIdKey = 'current_node_id_eth';
static const currentPolygonNodeIdKey = 'current_node_id_matic';
+ static const currentBaseNodeIdKey = 'current_node_id_base';
static const currentNanoNodeIdKey = 'current_node_id_nano';
static const currentNanoPowNodeIdKey = 'current_node_id_nano_pow';
static const currentDecredNodeIdKey = 'current_node_id_decred';
@@ -52,6 +53,7 @@ class PreferencesKey {
static const litecoinTransactionPriority = 'current_fee_priority_litecoin';
static const ethereumTransactionPriority = 'current_fee_priority_ethereum';
static const polygonTransactionPriority = 'current_fee_priority_polygon';
+ static const baseTransactionPriority = 'current_fee_priority_base';
static const bitcoinCashTransactionPriority = 'current_fee_priority_bitcoin_cash';
static const zanoTransactionPriority = 'current_fee_priority_zano';
static const wowneroTransactionPriority = 'current_fee_priority_wownero';
@@ -77,6 +79,7 @@ class PreferencesKey {
static const pinNativeTokenAtTop = 'pin_native_token_at_top';
static const useEtherscan = 'use_etherscan';
static const usePolygonScan = 'use_polygonscan';
+ static const useBaseScan = 'use_base_scan';
static const useTronGrid = 'use_trongrid';
static const useMempoolFeeAPI = 'use_mempool_fee_api';
static const defaultNanoRep = 'default_nano_representative';
diff --git a/lib/entities/priority_for_wallet_type.dart b/lib/entities/priority_for_wallet_type.dart
index c85e985..37ad2ab 100644
--- a/lib/entities/priority_for_wallet_type.dart
+++ b/lib/entities/priority_for_wallet_type.dart
@@ -1,3 +1,4 @@
+import 'package:cake_wallet/base/base.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
import 'package:cake_wallet/dogecoin/dogecoin.dart';
@@ -28,6 +29,8 @@ List<TransactionPriority> priorityForWalletType(WalletType type) {
return dogecoin!.getTransactionPriorities();
case WalletType.polygon:
return polygon!.getTransactionPriorities();
+ case WalletType.base:
+ return base!.getTransactionPriorities();
// no such thing for nano/banano/solana/tron:
case WalletType.nano:
case WalletType.banano:
diff --git a/lib/reactions/fiat_rate_update.dart b/lib/reactions/fiat_rate_update.dart
index dbc5f9a..abd8b09 100644
--- a/lib/reactions/fiat_rate_update.dart
+++ b/lib/reactions/fiat_rate_update.dart
@@ -1,4 +1,5 @@
import 'dart:async';
+import 'package:cake_wallet/base/base.dart';
import 'package:cake_wallet/core/fiat_conversion_service.dart';
import 'package:cake_wallet/entities/fiat_api_mode.dart';
import 'package:cake_wallet/ethereum/ethereum.dart';
@@ -44,6 +45,11 @@ Future<void> startFiatRateUpdate(
polygon!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
}
+ if (appStore.wallet!.type == WalletType.base) {
+ currencies =
+ base!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
+ }
+
if (appStore.wallet!.type == WalletType.solana) {
currencies =
solana!.getSPLTokenCurrencies(appStore.wallet!).where((element) => element.enabled);
diff --git a/lib/reactions/on_current_wallet_change.dart b/lib/reactions/on_current_wallet_change.dart
index 53a12b1..eed86e0 100644
--- a/lib/reactions/on_current_wallet_change.dart
+++ b/lib/reactions/on_current_wallet_change.dart
@@ -1,3 +1,4 @@
+import 'package:cake_wallet/base/base.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
@@ -130,6 +131,10 @@ void startCurrentWalletChangeReaction(
currencies =
polygon!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
}
+ if (wallet.type == WalletType.base) {
+ currencies =
+ base!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
+ }
if (wallet.type == WalletType.solana) {
currencies =
solana!.getSPLTokenCurrencies(appStore.wallet!).where((element) => element.enabled);
diff --git a/lib/reactions/wallet_connect.dart b/lib/reactions/wallet_connect.dart
index 37a0b92..7e969f3 100644
--- a/lib/reactions/wallet_connect.dart
+++ b/lib/reactions/wallet_connect.dart
@@ -8,6 +8,7 @@ bool isEVMCompatibleChain(WalletType walletType) {
switch (walletType) {
case WalletType.polygon:
case WalletType.ethereum:
+ case WalletType.base:
return true;
default:
return false;
@@ -18,6 +19,7 @@ bool isNFTACtivatedChain(WalletType walletType) {
switch (walletType) {
case WalletType.polygon:
case WalletType.ethereum:
+ case WalletType.base:
case WalletType.solana:
return true;
default:
@@ -29,6 +31,7 @@ bool isWalletConnectCompatibleChain(WalletType walletType) {
switch (walletType) {
case WalletType.polygon:
case WalletType.ethereum:
+ case WalletType.base:
case WalletType.solana:
return true;
default:
@@ -42,6 +45,8 @@ String getChainNameSpaceAndIdBasedOnWalletType(WalletType walletType) {
return EVMChainId.ethereum.chain();
case WalletType.polygon:
return EVMChainId.polygon.chain();
+ case WalletType.base:
+ return EVMChainId.base.chain();
case WalletType.solana:
return SolanaChainId.mainnet.chain();
default:
@@ -52,8 +57,8 @@ String getChainNameSpaceAndIdBasedOnWalletType(WalletType walletType) {
List<String> getChainSupportedMethodsOnWalletType(WalletType walletType) {
switch (walletType) {
case WalletType.ethereum:
- return EVMSupportedMethods.values.map((e) => e.name).toList();
case WalletType.polygon:
+ case WalletType.base:
return EVMSupportedMethods.values.map((e) => e.name).toList();
case WalletType.solana:
return SolanaSupportedMethods.values.map((e) => e.name).toList();
@@ -66,7 +71,8 @@ int getChainIdBasedOnWalletType(WalletType walletType) {
switch (walletType) {
case WalletType.polygon:
return 137;
-
+ case WalletType.base:
+ return 8453;
// For now, we return eth chain Id as the default, we'll modify as we add more wallets
case WalletType.ethereum:
default:
@@ -80,6 +86,8 @@ String getChainNameBasedOnWalletType(WalletType walletType) {
return 'eth';
case WalletType.polygon:
return 'polygon';
+ case WalletType.base:
+ return 'base';
case WalletType.solana:
return 'mainnet';
default:
@@ -93,6 +101,8 @@ String getTokenNameBasedOnWalletType(WalletType walletType) {
return 'ETH';
case WalletType.polygon:
return 'MATIC';
+ case WalletType.base:
+ return 'BASE';
case WalletType.solana:
return 'SOL';
default:
diff --git a/lib/reactions/wallet_utils.dart b/lib/reactions/wallet_utils.dart
index d5ff786..8250dfd 100644
--- a/lib/reactions/wallet_utils.dart
+++ b/lib/reactions/wallet_utils.dart
@@ -4,6 +4,7 @@ bool isBIP39Wallet(WalletType walletType) {
switch (walletType) {
case WalletType.ethereum:
case WalletType.polygon:
+ case WalletType.base:
case WalletType.solana:
case WalletType.tron:
case WalletType.bitcoin:
@@ -41,6 +42,7 @@ bool hasTokens(WalletType walletType) {
case WalletType.solana:
case WalletType.tron:
case WalletType.zano:
+ case WalletType.base:
return true;
default:
return false;
diff --git a/lib/src/screens/connect_device/select_hardware_wallet_account_page.dart b/lib/src/screens/connect_device/select_hardware_wallet_account_page.dart
index d4c40d9..691987a 100644
--- a/lib/src/screens/connect_device/select_hardware_wallet_account_page.dart
+++ b/lib/src/screens/connect_device/select_hardware_wallet_account_page.dart
@@ -10,6 +10,7 @@ import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/wallet_hardware_restore_view_model.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
@@ -148,7 +149,7 @@ class _SelectHardwareWalletAccountFormState extends State<SelectHardwareWalletAc
padding: EdgeInsets.only(top: 10),
child: SelectButton(
image: Image.asset(
- walletTypeToCryptoCurrency(_walletHardwareRestoreVM.type).iconPath ??
+ walletTypeToCryptoCurrency(_walletHardwareRestoreVM.type).normalizedIconPath ??
'',
height: 24,
width: 24,
diff --git a/lib/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart b/lib/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart
index a83f212..3f26708 100644
--- a/lib/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart
+++ b/lib/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart
@@ -38,6 +38,7 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
final ethereumIcon = Image.asset('assets/images/crypto/ethereum.png', height: 24, width: 24);
final polygonIcon = Image.asset('assets/images/crypto/polygon.png', height: 24, width: 24);
final bitcoinCashIcon = Image.asset('assets/images/crypto/bitcoin-cash.png', height: 24, width: 24);
+ final baseIcon = Image.asset('assets/images/base_icon.png', height: 24, width: 24);
final nanoIcon = Image.asset('assets/images/crypto/nano.png', height: 24, width: 24);
final bananoIcon = Image.asset('assets/images/crypto/nano.png', height: 24, width: 24);
final solanaIcon = Image.asset('assets/images/crypto/solana.png', height: 24, width: 24);
@@ -184,6 +185,8 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
return decredIcon;
case WalletType.dogecoin:
return dogeIcon;
+ case WalletType.base:
+ return baseIcon;
case WalletType.none:
return nonWalletTypeIcon;
}
diff --git a/lib/src/screens/dashboard/widgets/menu_widget.dart b/lib/src/screens/dashboard/widgets/menu_widget.dart
index 4302f72..4bf7be5 100644
--- a/lib/src/screens/dashboard/widgets/menu_widget.dart
+++ b/lib/src/screens/dashboard/widgets/menu_widget.dart
@@ -33,6 +33,7 @@ class MenuWidgetState extends State<MenuWidget> {
this.bananoIcon = Image.asset('assets/images/crypto/nano.png'),
this.bitcoinCashIcon = Image.asset('assets/images/crypto/bitcoin-cash.png'),
this.polygonIcon = Image.asset('assets/images/crypto/polygon.png'),
+ this.baseIcon = Image.asset('assets/images/base_icon.png'),
this.solanaIcon = Image.asset('assets/images/crypto/solana.png'),
this.tronIcon = Image.asset('assets/images/crypto/tron.png'),
this.wowneroIcon = Image.asset('assets/images/crypto/wownero.png'),
@@ -60,6 +61,7 @@ class MenuWidgetState extends State<MenuWidget> {
Image nanoIcon;
Image bananoIcon;
Image polygonIcon;
+ Image baseIcon;
Image solanaIcon;
Image tronIcon;
Image wowneroIcon;
@@ -247,6 +249,8 @@ class MenuWidgetState extends State<MenuWidget> {
return polygonIcon;
case WalletType.solana:
return solanaIcon;
+ case WalletType.base:
+ return baseIcon;
case WalletType.tron:
return tronIcon;
case WalletType.wownero:
diff --git a/lib/src/screens/dashboard/widgets/sign_form.dart b/lib/src/screens/dashboard/widgets/sign_form.dart
index 9fe661c..7f9f1b8 100644
--- a/lib/src/screens/dashboard/widgets/sign_form.dart
+++ b/lib/src/screens/dashboard/widgets/sign_form.dart
@@ -2,6 +2,7 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/address_text_field.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/utils/show_bar.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
diff --git a/lib/src/screens/dashboard/widgets/verify_form.dart b/lib/src/screens/dashboard/widgets/verify_form.dart
index 97e9401..318aca9 100644
--- a/lib/src/screens/dashboard/widgets/verify_form.dart
+++ b/lib/src/screens/dashboard/widgets/verify_form.dart
@@ -1,5 +1,6 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/address_text_field.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
diff --git a/lib/src/screens/new_wallet/new_wallet_type_page.dart b/lib/src/screens/new_wallet/new_wallet_type_page.dart
index 26ae913..5aab45d 100644
--- a/lib/src/screens/new_wallet/new_wallet_type_page.dart
+++ b/lib/src/screens/new_wallet/new_wallet_type_page.dart
@@ -15,6 +15,7 @@ import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/new_wallet_type_view_model.dart';
import 'package:cake_wallet/wallet_types.g.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/hardware/device_connection_type.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
@@ -142,9 +143,9 @@ class WalletTypeFormState extends State<WalletTypeForm> {
child: SelectButton(
key: ValueKey('new_wallet_type_${type.name}_button_key'),
image: Image.asset(
- walletTypeToCryptoCurrency(type).iconPath ?? '',
- height: 28,
- width: 28,
+ walletTypeToCryptoCurrency(type).normalizedIconPath ?? '',
+ height: 24,
+ width: 24,
),
text: walletTypeToDisplayName(type),
showTrailingIcon: false,
diff --git a/lib/src/screens/new_wallet/wallet_group_display_page.dart b/lib/src/screens/new_wallet/wallet_group_display_page.dart
index b3edc13..7c68bb1 100644
--- a/lib/src/screens/new_wallet/wallet_group_display_page.dart
+++ b/lib/src/screens/new_wallet/wallet_group_display_page.dart
@@ -7,7 +7,7 @@ import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/themes/core/material_base_theme.dart';
import 'package:cake_wallet/view_model/wallet_groups_display_view_model.dart';
-import 'package:cw_core/wallet_type.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
@@ -95,7 +95,7 @@ class WalletGroupsDisplayBody extends StatelessWidget {
isSelected:
walletGroupsDisplayViewModel.selectedSingleWallet == wallet,
leadingWidget: Image.asset(
- walletTypeToCryptoCurrency(wallet.type).iconPath!,
+ walletTypeToCryptoCurrency(wallet.type).normalizedIconPath!,
width: 32,
height: 32,
),
diff --git a/lib/src/screens/new_wallet/widgets/grouped_wallet_expansion_tile.dart b/lib/src/screens/new_wallet/widgets/grouped_wallet_expansion_tile.dart
index c028dc6..10fd423 100644
--- a/lib/src/screens/new_wallet/widgets/grouped_wallet_expansion_tile.dart
+++ b/lib/src/screens/new_wallet/widgets/grouped_wallet_expansion_tile.dart
@@ -1,4 +1,4 @@
-import 'package:cw_core/wallet_type.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
@@ -99,7 +99,6 @@ class GroupedWalletExpansionTile extends StatelessWidget {
final currentColor = item.isCurrent
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.surface;
- final walletTypeToCrypto = walletTypeToCryptoCurrency(item.type);
return ListTile(
contentPadding: EdgeInsets.zero,
key: ValueKey(item.name),
@@ -124,7 +123,7 @@ class GroupedWalletExpansionTile extends StatelessWidget {
: SizedBox(width: 7),
SizedBox(width: 24),
Image.asset(
- walletTypeToCrypto.iconPath!,
+ walletTypeToCryptoCurrency(item.type).normalizedIconPath!,
width: 32,
height: 32,
),
diff --git a/lib/src/screens/settings/privacy_page.dart b/lib/src/screens/settings/privacy_page.dart
index 7928914..e753efc 100644
--- a/lib/src/screens/settings/privacy_page.dart
+++ b/lib/src/screens/settings/privacy_page.dart
@@ -118,6 +118,14 @@ class PrivacyPage extends BasePage {
_privacySettingsViewModel.setUsePolygonScan(value);
},
),
+ if (_privacySettingsViewModel.canUseBaseScan)
+ SettingsSwitcherCell(
+ title: S.current.basescan_history,
+ value: _privacySettingsViewModel.useBaseScan,
+ onValueChange: (BuildContext _, bool value) {
+ _privacySettingsViewModel.setUseBaseScan(value);
+ },
+ ),
if (_privacySettingsViewModel.canUseTronGrid)
SettingsSwitcherCell(
title: S.current.trongrid_history,
diff --git a/lib/src/screens/wallet_connect/services/chain_service/eth/evm_chain_id.dart b/lib/src/screens/wallet_connect/services/chain_service/eth/evm_chain_id.dart
index 6ecd09d..dd1c7a6 100644
--- a/lib/src/screens/wallet_connect/services/chain_service/eth/evm_chain_id.dart
+++ b/lib/src/screens/wallet_connect/services/chain_service/eth/evm_chain_id.dart
@@ -1,6 +1,7 @@
enum EVMChainId {
ethereum,
polygon,
+ base,
goerli,
mumbai,
arbitrum,
@@ -17,6 +18,9 @@ extension EVMChainIdX on EVMChainId {
case EVMChainId.polygon:
name = '137';
break;
+ case EVMChainId.base:
+ name = '8453';
+ break;
case EVMChainId.goerli:
name = '5';
break;
diff --git a/lib/src/screens/wallet_connect/services/key_service/wallet_connect_key_service.dart b/lib/src/screens/wallet_connect/services/key_service/wallet_connect_key_service.dart
index a7f4045..7e48928 100644
--- a/lib/src/screens/wallet_connect/services/key_service/wallet_connect_key_service.dart
+++ b/lib/src/screens/wallet_connect/services/key_service/wallet_connect_key_service.dart
@@ -1,3 +1,4 @@
+import 'package:cake_wallet/base/base.dart';
import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart';
@@ -23,6 +24,8 @@ class KeyServiceImpl implements WalletConnectKeyService {
return ethereum!.getPrivateKey(wallet);
case WalletType.polygon:
return polygon!.getPrivateKey(wallet);
+ case WalletType.base:
+ return base!.getPrivateKey(wallet);
case WalletType.solana:
return solana!.getPrivateKey(wallet);
default:
@@ -36,6 +39,8 @@ class KeyServiceImpl implements WalletConnectKeyService {
return ethereum!.getPublicKey(wallet);
case WalletType.polygon:
return polygon!.getPublicKey(wallet);
+ case WalletType.base:
+ return base!.getPublicKey(wallet);
case WalletType.solana:
return solana!.getPublicKey(wallet);
default:
@@ -51,6 +56,7 @@ class KeyServiceImpl implements WalletConnectKeyService {
'eip155:1',
'eip155:5',
'eip155:137',
+ 'eip155:8453',
'eip155:42161',
'eip155:80001',
],
diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart
index 2dcaa8d..42000a6 100644
--- a/lib/src/screens/wallet_list/wallet_list_page.dart
+++ b/lib/src/screens/wallet_list/wallet_list_page.dart
@@ -25,6 +25,7 @@ import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
diff --git a/lib/src/widgets/bottom_sheet/payment_confirmation_bottom_sheet.dart b/lib/src/widgets/bottom_sheet/payment_confirmation_bottom_sheet.dart
index afe2e40..56f6a57 100644
--- a/lib/src/widgets/bottom_sheet/payment_confirmation_bottom_sheet.dart
+++ b/lib/src/widgets/bottom_sheet/payment_confirmation_bottom_sheet.dart
@@ -1,5 +1,6 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/utils/payment_request.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
diff --git a/lib/src/widgets/bottom_sheet/swap_confirmation_bottom_sheet.dart b/lib/src/widgets/bottom_sheet/swap_confirmation_bottom_sheet.dart
index 69f4abf..afb1b5d 100644
--- a/lib/src/widgets/bottom_sheet/swap_confirmation_bottom_sheet.dart
+++ b/lib/src/widgets/bottom_sheet/swap_confirmation_bottom_sheet.dart
@@ -9,6 +9,7 @@ import 'package:cake_wallet/src/widgets/bottom_sheet/swap_details_bottom_sheet.d
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cake_wallet/utils/address_formatter.dart';
import 'package:cake_wallet/utils/debounce.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/crypto_amount_format.dart';
import 'package:flutter/material.dart';
@@ -136,7 +137,7 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
@override
Widget build(BuildContext context) {
- final detectedCurrencyName = walletTypeToCryptoCurrency(widget.paymentFlowResult.walletType!);
+ final detectedCurrency = walletTypeToCryptoCurrency(widget.paymentFlowResult.walletType!);
return Form(
key: _formKey,
@@ -159,7 +160,7 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
const SizedBox(width: 12),
CakeImageWidget(
imageUrl:
- walletTypeToCryptoCurrency(widget.paymentFlowResult.walletType!).iconPath!,
+ detectedCurrency.iconPath!,
width: 32,
height: 32,
),
@@ -168,7 +169,7 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
const SizedBox(height: 16),
SwapConfirmationTextfield(
key: ValueKey('swap_confirmation_bottomsheet_amount_textfield_key'),
- hintText: 'Amount (${detectedCurrencyName})',
+ hintText: 'Amount (${detectedCurrency})',
focusNode: _amountFocus,
controller: _amountController,
keyboardType: TextInputType.numberWithOptions(decimal: true, signed: false),
@@ -226,7 +227,7 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
min != null
? Text(
key: ValueKey('min_limit_text_key'),
- S.of(context).min_value(min, detectedCurrencyName.toString()),
+ S.of(context).min_value(min, detectedCurrency.toString()),
style: Theme.of(context).textTheme.bodySmall!.copyWith(
fontSize: 10,
height: 1.2,
@@ -238,7 +239,7 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
max != null
? Text(
key: ValueKey('max_limit_text_key'),
- S.of(context).max_value(max, detectedCurrencyName.toString()),
+ S.of(context).max_value(max, detectedCurrency.toString()),
style: Theme.of(context).textTheme.bodySmall!.copyWith(
fontSize: 10,
height: 1.2,
diff --git a/lib/src/widgets/bottom_sheet/wallet_switcher_bottom_sheet.dart b/lib/src/widgets/bottom_sheet/wallet_switcher_bottom_sheet.dart
index e99e344..1b4886a 100644
--- a/lib/src/widgets/bottom_sheet/wallet_switcher_bottom_sheet.dart
+++ b/lib/src/widgets/bottom_sheet/wallet_switcher_bottom_sheet.dart
@@ -1,4 +1,5 @@
import 'package:cake_wallet/generated/i18n.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart';
@@ -94,7 +95,7 @@ class _WalletSwitcherContent extends StatelessWidget {
child: Row(
children: [
Image.asset(
- walletTypeToCryptoCurrency(wallet.type).iconPath!,
+ walletTypeToCryptoCurrency(wallet.type).normalizedIconPath!,
width: 32,
height: 32,
),
diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart
index 4b2b117..6b3dbc1 100644
--- a/lib/store/settings_store.dart
+++ b/lib/store/settings_store.dart
@@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';
+import 'package:cake_wallet/base/base.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/core/utilities.dart';
import 'package:cake_wallet/decred/decred.dart';
@@ -108,6 +109,7 @@ abstract class SettingsStoreBase with Store {
required this.sortBalanceBy,
required this.pinNativeTokenAtTop,
required this.useEtherscan,
+ required this.useBaseScan,
required this.usePolygonScan,
required this.useTronGrid,
required this.useMempoolFeeAPI,
@@ -139,6 +141,7 @@ abstract class SettingsStoreBase with Store {
TransactionPriority? initialLitecoinTransactionPriority,
TransactionPriority? initialEthereumTransactionPriority,
TransactionPriority? initialPolygonTransactionPriority,
+ TransactionPriority? initialBaseTransactionPriority,
TransactionPriority? initialBitcoinCashTransactionPriority,
TransactionPriority? initialZanoTransactionPriority,
TransactionPriority? initialDecredTransactionPriority,
@@ -224,6 +227,10 @@ abstract class SettingsStoreBase with Store {
priority[WalletType.polygon] = initialPolygonTransactionPriority;
}
+ if (initialBaseTransactionPriority != null) {
+ priority[WalletType.base] = initialBaseTransactionPriority;
+ }
+
if (initialBitcoinCashTransactionPriority != null) {
priority[WalletType.bitcoinCash] = initialBitcoinCashTransactionPriority;
}
@@ -291,6 +298,9 @@ abstract class SettingsStoreBase with Store {
case WalletType.polygon:
key = PreferencesKey.polygonTransactionPriority;
break;
+ case WalletType.base:
+ key = PreferencesKey.baseTransactionPriority;
+ break;
case WalletType.zano:
key = PreferencesKey.zanoTransactionPriority;
break;
@@ -453,6 +463,11 @@ abstract class SettingsStoreBase with Store {
(bool usePolygonScan) =>
_sharedPreferences.setBool(PreferencesKey.usePolygonScan, usePolygonScan));
+ reaction(
+ (_) => useBaseScan,
+ (bool useBaseScan) =>
+ _sharedPreferences.setBool(PreferencesKey.useBaseScan, useBaseScan));
+
reaction((_) => useTronGrid,
(bool useTronGrid) => _sharedPreferences.setBool(PreferencesKey.useTronGrid, useTronGrid));
@@ -808,6 +823,9 @@ abstract class SettingsStoreBase with Store {
@observable
bool usePolygonScan;
+ @observable
+ bool useBaseScan;
+
@observable
bool useTronGrid;
@@ -949,6 +967,7 @@ abstract class SettingsStoreBase with Store {
TransactionPriority? litecoinTransactionPriority;
TransactionPriority? ethereumTransactionPriority;
TransactionPriority? polygonTransactionPriority;
+ TransactionPriority? baseTransactionPriority;
TransactionPriority? bitcoinCashTransactionPriority;
TransactionPriority? wowneroTransactionPriority;
TransactionPriority? zanoTransactionPriority;
@@ -970,6 +989,10 @@ abstract class SettingsStoreBase with Store {
polygonTransactionPriority = polygon?.deserializePolygonTransactionPriority(
sharedPreferences.getInt(PreferencesKey.polygonTransactionPriority)!);
}
+ if (sharedPreferences.getInt(PreferencesKey.baseTransactionPriority) != null) {
+ baseTransactionPriority = base?.deserializeBaseTransactionPriority(
+ sharedPreferences.getInt(PreferencesKey.baseTransactionPriority)!);
+ }
if (sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority) != null) {
bitcoinCashTransactionPriority = bitcoinCash?.deserializeBitcoinCashTransactionPriority(
sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority)!);
@@ -996,6 +1019,7 @@ abstract class SettingsStoreBase with Store {
wowneroTransactionPriority ??= wownero?.getDefaultTransactionPriority();
decredTransactionPriority ??= decred?.getDecredTransactionPriorityMedium();
polygonTransactionPriority ??= polygon?.getDefaultTransactionPriority();
+ baseTransactionPriority ??= base?.getDefaultTransactionPriority();
zanoTransactionPriority ??= zano?.getDefaultTransactionPriority();
final currentBalanceDisplayMode = BalanceDisplayMode.deserialize(
@@ -1042,6 +1066,7 @@ abstract class SettingsStoreBase with Store {
: defaultSeedPhraseLength;
final useEtherscan = sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true;
final usePolygonScan = sharedPreferences.getBool(PreferencesKey.usePolygonScan) ?? true;
+ final useBaseScan = sharedPreferences.getBool(PreferencesKey.useBaseScan) ?? true;
final useTronGrid = sharedPreferences.getBool(PreferencesKey.useTronGrid) ?? true;
final useMempoolFeeAPI = sharedPreferences.getBool(PreferencesKey.useMempoolFeeAPI) ?? true;
final defaultNanoRep = sharedPreferences.getString(PreferencesKey.defaultNanoRep) ?? "";
@@ -1087,6 +1112,7 @@ abstract class SettingsStoreBase with Store {
sharedPreferences.getInt(PreferencesKey.currentBitcoinCashNodeIdKey);
final ethereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
final polygonNodeId = sharedPreferences.getInt(PreferencesKey.currentPolygonNodeIdKey);
+ final baseNodeId = sharedPreferences.getInt(PreferencesKey.currentBaseNodeIdKey);
final nanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
final nanoPowNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoPowNodeIdKey);
final solanaNodeId = sharedPreferences.getInt(PreferencesKey.currentSolanaNodeIdKey);
@@ -1107,6 +1133,8 @@ abstract class SettingsStoreBase with Store {
nodeSource.values.firstWhereOrNull((e) => e.uriRaw == ethereumDefaultNodeUri);
final polygonNode = nodeSource.get(polygonNodeId) ??
nodeSource.values.firstWhereOrNull((e) => e.uriRaw == polygonDefaultNodeUri);
+ final baseNode = nodeSource.get(baseNodeId) ??
+ nodeSource.values.firstWhereOrNull((e) => e.uriRaw == baseDefaultNodeUri);
final bitcoinCashElectrumServer = nodeSource.get(bitcoinCashElectrumServerId) ??
nodeSource.values.firstWhereOrNull((e) => e.uriRaw == cakeWalletBitcoinCashDefaultNodeUri);
final nanoNode = nodeSource.get(nanoNodeId) ??
@@ -1180,6 +1208,10 @@ abstract class SettingsStoreBase with Store {
nodes[WalletType.polygon] = polygonNode;
}
+ if (baseNode != null) {
+ nodes[WalletType.base] = baseNode;
+ }
+
if (bitcoinCashElectrumServer != null) {
nodes[WalletType.bitcoinCash] = bitcoinCashElectrumServer;
}
@@ -1358,6 +1390,7 @@ abstract class SettingsStoreBase with Store {
pinNativeTokenAtTop: pinNativeTokenAtTop,
useEtherscan: useEtherscan,
usePolygonScan: usePolygonScan,
+ useBaseScan: useBaseScan,
useTronGrid: useTronGrid,
useMempoolFeeAPI: useMempoolFeeAPI,
defaultNanoRep: defaultNanoRep,
@@ -1404,6 +1437,7 @@ abstract class SettingsStoreBase with Store {
shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
initialEthereumTransactionPriority: ethereumTransactionPriority,
initialPolygonTransactionPriority: polygonTransactionPriority,
+ initialBaseTransactionPriority: baseTransactionPriority,
initialSyncMode: savedSyncMode,
initialSyncAll: savedSyncAll,
shouldShowYatPopup: shouldShowYatPopup,
@@ -1455,6 +1489,11 @@ abstract class SettingsStoreBase with Store {
priority[WalletType.polygon] = polygon!.deserializePolygonTransactionPriority(
sharedPreferences.getInt(PreferencesKey.polygonTransactionPriority)!);
}
+ if (base != null &&
+ sharedPreferences.getInt(PreferencesKey.baseTransactionPriority) != null) {
+ priority[WalletType.base] = base!.deserializeBaseTransactionPriority(
+ sharedPreferences.getInt(PreferencesKey.baseTransactionPriority)!);
+ }
if (bitcoinCash != null &&
sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority) != null) {
priority[WalletType.bitcoinCash] = bitcoinCash!.deserializeBitcoinCashTransactionPriority(
@@ -1548,6 +1587,7 @@ abstract class SettingsStoreBase with Store {
pinNativeTokenAtTop = sharedPreferences.getBool(PreferencesKey.pinNativeTokenAtTop) ?? true;
useEtherscan = sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true;
usePolygonScan = sharedPreferences.getBool(PreferencesKey.usePolygonScan) ?? true;
+ useBaseScan = sharedPreferences.getBool(PreferencesKey.useBaseScan) ?? true;
useTronGrid = sharedPreferences.getBool(PreferencesKey.useTronGrid) ?? true;
useMempoolFeeAPI = sharedPreferences.getBool(PreferencesKey.useMempoolFeeAPI) ?? true;
defaultNanoRep = sharedPreferences.getString(PreferencesKey.defaultNanoRep) ?? "";
@@ -1578,6 +1618,7 @@ abstract class SettingsStoreBase with Store {
final havenNodeId = sharedPreferences.getInt(PreferencesKey.currentHavenNodeIdKey);
final ethereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
final polygonNodeId = sharedPreferences.getInt(PreferencesKey.currentPolygonNodeIdKey);
+ final baseNodeId = sharedPreferences.getInt(PreferencesKey.currentBaseNodeIdKey);
final nanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
final solanaNodeId = sharedPreferences.getInt(PreferencesKey.currentSolanaNodeIdKey);
final tronNodeId = sharedPreferences.getInt(PreferencesKey.currentTronNodeIdKey);
@@ -1591,6 +1632,7 @@ abstract class SettingsStoreBase with Store {
final havenNode = nodeSource.get(havenNodeId);
final ethereumNode = nodeSource.get(ethereumNodeId);
final polygonNode = nodeSource.get(polygonNodeId);
+ final baseNode = nodeSource.get(baseNodeId);
final bitcoinCashNode = nodeSource.get(bitcoinCashElectrumServerId);
final nanoNode = nodeSource.get(nanoNodeId);
final solanaNode = nodeSource.get(solanaNodeId);
@@ -1624,6 +1666,10 @@ abstract class SettingsStoreBase with Store {
nodes[WalletType.polygon] = polygonNode;
}
+ if (baseNode != null) {
+ nodes[WalletType.base] = baseNode;
+ }
+
if (bitcoinCashNode != null) {
nodes[WalletType.bitcoinCash] = bitcoinCashNode;
}
@@ -1784,6 +1830,9 @@ abstract class SettingsStoreBase with Store {
case WalletType.polygon:
await _sharedPreferences.setInt(PreferencesKey.currentPolygonNodeIdKey, node.key as int);
break;
+ case WalletType.base:
+ await _sharedPreferences.setInt(PreferencesKey.currentBaseNodeIdKey, node.key as int);
+ break;
case WalletType.solana:
await _sharedPreferences.setInt(PreferencesKey.currentSolanaNodeIdKey, node.key as int);
break;
diff --git a/lib/utils/qr_util.dart b/lib/utils/qr_util.dart
index 26b3000..7e7aec0 100644
--- a/lib/utils/qr_util.dart
+++ b/lib/utils/qr_util.dart
@@ -28,9 +28,11 @@ String getQrImage(WalletType type) {
return 'assets/images/dcr_chain_qr.svg';
case WalletType.dogecoin:
return 'assets/images/doge_chain_qr.svg';
+ case WalletType.base:
+ return 'assets/images/base_chain_QR.svg';
case WalletType.banano:
case WalletType.haven:
case WalletType.none:
- return 'assets/images/qr-cake.svg';
+ return 'assets/images/qr-cake.png';
}
}
diff --git a/lib/utils/token_utilities.dart b/lib/utils/token_utilities.dart
index 969b596..c9a8182 100644
--- a/lib/utils/token_utilities.dart
+++ b/lib/utils/token_utilities.dart
@@ -1,3 +1,4 @@
+import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cw_core/cake_hive.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/erc20_token.dart';
@@ -13,14 +14,14 @@ class TokenUtilities {
Box<WalletInfo> walletInfoSource,
) async {
final evmWallets = walletInfoSource.values.where(
- (w) => w.type == WalletType.ethereum || w.type == WalletType.polygon,
+ (w) => isEVMCompatibleChain(w.type),
);
final seen = <String>{};
final unique = <Erc20Token>[];
for (final wallet in evmWallets) {
- final chain = wallet.type == WalletType.ethereum ? 'ETH' : 'POL';
+ final chain = getTokenNameBasedOnWalletType(wallet.type);
final box = await _openEvmTokensBoxFor(wallet);
for (final t in box.values.where((t) => t.enabled)) {
@@ -88,6 +89,7 @@ class TokenUtilities {
switch (walletType) {
case WalletType.ethereum:
case WalletType.polygon:
+ case WalletType.base:
final tokens = await loadAllUniqueEvmTokens(walletInfoSource);
for (final t in tokens) {
if (t.contractAddress.toLowerCase() == lower) return t;
@@ -117,6 +119,7 @@ class TokenUtilities {
final boxName = switch (walletInfo.type) {
WalletType.ethereum => '${walletKey}_${Erc20Token.ethereumBoxName}',
WalletType.polygon => '${walletKey}_${Erc20Token.polygonBoxName}',
+ WalletType.base => '${walletKey}_${Erc20Token.baseBoxName}',
_ => '${walletKey}_${Erc20Token.ethereumBoxName}',
};
diff --git a/lib/view_model/advanced_privacy_settings_view_model.dart b/lib/view_model/advanced_privacy_settings_view_model.dart
index 620e186..efc2270 100644
--- a/lib/view_model/advanced_privacy_settings_view_model.dart
+++ b/lib/view_model/advanced_privacy_settings_view_model.dart
@@ -39,6 +39,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
case WalletType.bitcoinCash:
case WalletType.dogecoin:
case WalletType.polygon:
+ case WalletType.base:
case WalletType.solana:
case WalletType.tron:
return true;
@@ -79,6 +80,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
WalletType.bitcoinCash,
WalletType.ethereum,
WalletType.polygon,
+ WalletType.base,
WalletType.tron,
WalletType.solana,
WalletType.monero,
diff --git a/lib/view_model/anon_invoice_page_view_model.dart b/lib/view_model/anon_invoice_page_view_model.dart
index bf519c5..22a40a4 100644
--- a/lib/view_model/anon_invoice_page_view_model.dart
+++ b/lib/view_model/anon_invoice_page_view_model.dart
@@ -5,6 +5,7 @@ import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/utils/qr_util.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/receive_page_option.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cw_core/crypto_currency.dart';
diff --git a/lib/view_model/buy/buy_view_model.dart b/lib/view_model/buy/buy_view_model.dart
index 785a759..4c1b547 100644
--- a/lib/view_model/buy/buy_view_model.dart
+++ b/lib/view_model/buy/buy_view_model.dart
@@ -3,6 +3,7 @@ import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
import 'package:cake_wallet/buy/wyre/wyre_buy_provider.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/store/settings_store.dart';
diff --git a/lib/view_model/contact_list/contact_list_view_model.dart b/lib/view_model/contact_list/contact_list_view_model.dart
index 730d073..27aee91 100644
--- a/lib/view_model/contact_list/contact_list_view_model.dart
+++ b/lib/view_model/contact_list/contact_list_view_model.dart
@@ -10,6 +10,7 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/utils/mobx.dart';
import 'package:cw_core/crypto_currency.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:hive/hive.dart';
diff --git a/lib/view_model/dashboard/balance_view_model.dart b/lib/view_model/dashboard/balance_view_model.dart
index f09d82b..78701b4 100644
--- a/lib/view_model/dashboard/balance_view_model.dart
+++ b/lib/view_model/dashboard/balance_view_model.dart
@@ -166,6 +166,7 @@ abstract class BalanceViewModelBase with Store {
case WalletType.haven:
case WalletType.ethereum:
case WalletType.polygon:
+ case WalletType.base:
case WalletType.solana:
case WalletType.tron:
return S.current.xmr_full_balance;
diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart
index 3a6cf2d..265a483 100644
--- a/lib/view_model/dashboard/dashboard_view_model.dart
+++ b/lib/view_model/dashboard/dashboard_view_model.dart
@@ -873,6 +873,7 @@ abstract class DashboardViewModelBase with Store {
case WalletType.bitcoinCash:
case WalletType.ethereum:
case WalletType.polygon:
+ case WalletType.base:
case WalletType.solana:
case WalletType.nano:
case WalletType.banano:
diff --git a/lib/view_model/dashboard/home_settings_view_model.dart b/lib/view_model/dashboard/home_settings_view_model.dart
index 44b9b9b..c54d8b1 100644
--- a/lib/view_model/dashboard/home_settings_view_model.dart
+++ b/lib/view_model/dashboard/home_settings_view_model.dart
@@ -1,5 +1,6 @@
import 'dart:convert';
+import 'package:cake_wallet/base/base.dart';
import 'package:cake_wallet/core/fiat_conversion_service.dart';
import 'package:cake_wallet/entities/fiat_api_mode.dart';
import 'package:cake_wallet/entities/erc20_token_info_moralis.dart';
@@ -99,6 +100,18 @@ abstract class HomeSettingsViewModelBase with Store {
await polygon!.addErc20Token(_balanceViewModel.wallet, polygonToken);
}
+ if (_balanceViewModel.wallet.type == WalletType.base) {
+ final baseToken = Erc20Token(
+ name: token.name,
+ symbol: token.title,
+ decimal: token.decimals,
+ contractAddress: contractAddress.toLowerCase(),
+ iconPath: token.iconPath,
+ isPotentialScam: token.isPotentialScam,
+ );
+ await base!.addErc20Token(_balanceViewModel.wallet, baseToken);
+ }
+
if (_balanceViewModel.wallet.type == WalletType.solana) {
final splToken = token.copyWith(enabled: true);
await solana!.addSPLToken(
@@ -136,6 +149,10 @@ abstract class HomeSettingsViewModelBase with Store {
return polygon!.isTokenAlreadyAdded(_balanceViewModel.wallet, contractAddress);
}
+ if (_balanceViewModel.wallet.type == WalletType.base) {
+ return base!.isTokenAlreadyAdded(_balanceViewModel.wallet, contractAddress);
+ }
+
if (_balanceViewModel.wallet.type == WalletType.solana) {
return solana!.isTokenAlreadyAdded(_balanceViewModel.wallet, contractAddress);
}
@@ -163,6 +180,10 @@ abstract class HomeSettingsViewModelBase with Store {
await polygon!.deleteErc20Token(_balanceViewModel.wallet, token as Erc20Token);
}
+ if (_balanceViewModel.wallet.type == WalletType.base) {
+ await base!.deleteErc20Token(_balanceViewModel.wallet, token as Erc20Token);
+ }
+
if (_balanceViewModel.wallet.type == WalletType.solana) {
await solana!.deleteSPLToken(_balanceViewModel.wallet, token);
}
@@ -189,16 +210,14 @@ abstract class HomeSettingsViewModelBase with Store {
return false;
}
- bool isEthereum = _balanceViewModel.wallet.type == WalletType.ethereum;
-
bool isPotentialScamViaMoralis = await _isPotentialScamTokenViaMoralis(
contractAddress,
- isEthereum ? 'eth' : 'polygon',
+ getChainNameBasedOnWalletType(_balanceViewModel.wallet.type),
);
bool isUnverifiedContract = await _isContractUnverified(
contractAddress,
- isEthereum: isEthereum,
+ chainId: getChainIdBasedOnWalletType(_balanceViewModel.wallet.type).toString(),
);
final showWarningForContractAddress = isPotentialScamViaMoralis || isUnverifiedContract;
@@ -219,6 +238,9 @@ abstract class HomeSettingsViewModelBase with Store {
case WalletType.polygon:
defaultTokenAddresses = polygon!.getDefaultTokenContractAddresses();
break;
+ case WalletType.base:
+ defaultTokenAddresses = base!.getDefaultTokenContractAddresses();
+ break;
case WalletType.solana:
defaultTokenAddresses = solana!.getDefaultTokenContractAddresses();
break;
@@ -306,13 +328,13 @@ abstract class HomeSettingsViewModelBase with Store {
Future<bool> _isContractUnverified(
String contractAddress, {
- required bool isEthereum,
+ required String chainId,
}) async {
final uri = Uri.https(
"api.etherscan.io",
"/v2/api",
{
- "chainid": isEthereum ? "1" : "137",
+ "chainid": chainId,
"module": "contract",
"action": "getsourcecode",
"address": contractAddress,
@@ -354,6 +376,10 @@ abstract class HomeSettingsViewModelBase with Store {
return await polygon!.getErc20Token(_balanceViewModel.wallet, contractAddress);
}
+ if (_balanceViewModel.wallet.type == WalletType.base) {
+ return await base!.getErc20Token(_balanceViewModel.wallet, contractAddress);
+ }
+
if (_balanceViewModel.wallet.type == WalletType.solana) {
return await solana!.getSPLToken(_balanceViewModel.wallet, contractAddress);
}
@@ -395,6 +421,11 @@ abstract class HomeSettingsViewModelBase with Store {
if (!value) polygon!.removeTokenTransactionsInHistory(_balanceViewModel.wallet, token);
}
+ if (_balanceViewModel.wallet.type == WalletType.base) {
+ base!.addErc20Token(_balanceViewModel.wallet, token as Erc20Token);
+ if (!value) base!.removeTokenTransactionsInHistory(_balanceViewModel.wallet, token);
+ }
+
if (_balanceViewModel.wallet.type == WalletType.solana) {
final address = solana!.getTokenAddress(token);
solana!.addSPLToken(_balanceViewModel.wallet, token, address);
@@ -448,6 +479,14 @@ abstract class HomeSettingsViewModelBase with Store {
..sort(_sortFunc));
}
+ if (_balanceViewModel.wallet.type == WalletType.base) {
+ tokens.addAll(base!
+ .getERC20Currencies(_balanceViewModel.wallet)
+ .where((element) => _matchesSearchText(element))
+ .toList()
+ ..sort(_sortFunc));
+ }
+
if (_balanceViewModel.wallet.type == WalletType.solana) {
tokens.addAll(solana!
.getSPLTokenCurrencies(_balanceViewModel.wallet)
@@ -489,7 +528,7 @@ abstract class HomeSettingsViewModelBase with Store {
bool _matchesSearchText(CryptoCurrency asset) {
final address = getTokenAddressBasedOnWallet(asset);
- // The homes settings would only be displayed for either of Tron, Ethereum, Polygon or Solana Wallets.
+ // The homes settings would only be displayed for either of Tron, Ethereum, Polygon, Base or Solana Wallets.
if (address == null) return false;
return searchText.isEmpty ||
@@ -515,11 +554,15 @@ abstract class HomeSettingsViewModelBase with Store {
return polygon!.getTokenAddress(asset);
}
+ if (_balanceViewModel.wallet.type == WalletType.base) {
+ return base!.getTokenAddress(asset);
+ }
+
if (_balanceViewModel.wallet.type == WalletType.zano) {
return zano!.getZanoAssetAddress(asset);
}
- // We return null if it's neither Tron, Polygon, Ethereum or Solana wallet (which is actually impossible because we only display home settings for either of these three wallets).
+ // We return null if it's neither Tron, Polygon, Base, Ethereum or Solana wallet (which is actually impossible because we only display home settings for either of these four wallets).
return null;
}
}
diff --git a/lib/view_model/dashboard/transaction_list_item.dart b/lib/view_model/dashboard/transaction_list_item.dart
index b59011c..874da36 100644
--- a/lib/view_model/dashboard/transaction_list_item.dart
+++ b/lib/view_model/dashboard/transaction_list_item.dart
@@ -1,3 +1,4 @@
+import 'package:cake_wallet/base/base.dart';
import 'package:cake_wallet/decred/decred.dart';
import 'package:cake_wallet/entities/balance_display_mode.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
@@ -134,6 +135,11 @@ class TransactionListItem extends ActionListItem with Keyable {
return asset;
}
+ if (balanceViewModel.wallet.type == WalletType.base) {
+ final asset = base!.assetOfTransaction(balanceViewModel.wallet, transaction);
+ return asset;
+ }
+
if (balanceViewModel.wallet.type == WalletType.solana) {
final asset = solana!.assetOfTransaction(balanceViewModel.wallet, transaction);
return asset;
@@ -186,6 +192,13 @@ class TransactionListItem extends ActionListItem with Keyable {
cryptoAmount: polygon!.formatterPolygonAmountToDouble(transaction: transaction),
price: price);
break;
+ case WalletType.base:
+ final asset = base!.assetOfTransaction(balanceViewModel.wallet, transaction);
+ final price = balanceViewModel.fiatConvertationStore.prices[asset];
+ amount = calculateFiatAmountRaw(
+ cryptoAmount: base!.formatterBaseAmountToDouble(transaction: transaction),
+ price: price);
+ break;
case WalletType.nano:
amount = calculateFiatAmountRaw(
cryptoAmount: double.parse(nanoUtil!.getRawAsUsableString(
diff --git a/lib/view_model/exchange/exchange_trade_view_model.dart b/lib/view_model/exchange/exchange_trade_view_model.dart
index 3ff7037..635bf5c 100644
--- a/lib/view_model/exchange/exchange_trade_view_model.dart
+++ b/lib/view_model/exchange/exchange_trade_view_model.dart
@@ -17,6 +17,7 @@ import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/xoswap_exchange_provider.dart';
import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/generated/i18n.dart';
+import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_item.dart';
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
import 'package:cake_wallet/store/dashboard/trades_store.dart';
@@ -306,6 +307,9 @@ abstract class ExchangeTradeViewModelBase with Store {
wallet.currency == CryptoCurrency.maticpoly &&
tradeFrom?.tag == CryptoCurrency.maticpoly.tag;
+ bool _isBaseToken() =>
+ wallet.currency == CryptoCurrency.baseEth && tradeFrom?.tag == CryptoCurrency.baseEth.title;
+
bool _isTronToken() =>
wallet.currency == CryptoCurrency.trx && tradeFrom?.tag == CryptoCurrency.trx.title;
@@ -317,7 +321,8 @@ abstract class ExchangeTradeViewModelBase with Store {
_isEthToken() ||
_isPolygonToken() ||
_isSplToken() ||
- _isTronToken();
+ _isTronToken() ||
+ _isBaseToken();
}
PaymentURI? get paymentUri {
@@ -343,6 +348,8 @@ abstract class ExchangeTradeViewModelBase with Store {
// TODO: Expand ERC681URI support to Polygon(modify decoding flow for QRs, pay anything, and deep link handling)
case WalletType.polygon:
return PolygonURI(amount: amount, address: inputAddress);
+ case WalletType.base:
+ return BaseURI(amount: amount, address: inputAddress);
case WalletType.solana:
return SolanaURI(amount: amount, address: inputAddress);
case WalletType.tron:
@@ -377,7 +384,7 @@ abstract class ExchangeTradeViewModelBase with Store {
contractAddress: null,
);
} else {
- if (wallet.type == WalletType.polygon || wallet.type == WalletType.ethereum) {
+ if (isEVMCompatibleChain(wallet.type)) {
final erc20Token = TokenUtilities.findErc20Token(currency, wallet);
if (erc20Token != null) {
diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart
index 8c12088..4ee5117 100644
--- a/lib/view_model/exchange/exchange_view_model.dart
+++ b/lib/view_model/exchange/exchange_view_model.dart
@@ -878,6 +878,10 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
depositCurrency = CryptoCurrency.maticpoly;
receiveCurrency = CryptoCurrency.xmr;
break;
+ case WalletType.base:
+ depositCurrency = CryptoCurrency.baseEth;
+ receiveCurrency = CryptoCurrency.xmr;
+ break;
case WalletType.solana:
depositCurrency = CryptoCurrency.sol;
receiveCurrency = CryptoCurrency.xmr;
diff --git a/lib/view_model/node_list/node_create_or_edit_view_model.dart b/lib/view_model/node_list/node_create_or_edit_view_model.dart
index a851180..67a0887 100644
--- a/lib/view_model/node_list/node_create_or_edit_view_model.dart
+++ b/lib/view_model/node_list/node_create_or_edit_view_model.dart
@@ -80,6 +80,7 @@ abstract class NodeCreateOrEditViewModelBase with Store {
switch (_walletType) {
case WalletType.ethereum:
case WalletType.polygon:
+ case WalletType.base:
case WalletType.solana:
case WalletType.banano:
case WalletType.nano:
diff --git a/lib/view_model/restore/wallet_restore_from_qr_code.dart b/lib/view_model/restore/wallet_restore_from_qr_code.dart
index 5fe46da..c3364ff 100644
--- a/lib/view_model/restore/wallet_restore_from_qr_code.dart
+++ b/lib/view_model/restore/wallet_restore_from_qr_code.dart
@@ -3,11 +3,13 @@ import 'dart:convert';
import 'package:cake_wallet/core/seed_validator.dart';
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
import 'package:cake_wallet/entities/qr_scanner.dart';
+import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/restore/restore_mode.dart';
import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/generated/i18n.dart';
@@ -29,6 +31,7 @@ class WalletRestoreFromQRCode {
'litecoin_wallet': WalletType.litecoin,
'ethereum-wallet': WalletType.ethereum,
'polygon-wallet': WalletType.polygon,
+ 'base-wallet': WalletType.base,
'nano-wallet': WalletType.nano,
'nano_wallet': WalletType.nano,
'bitcoincash': WalletType.bitcoinCash,
@@ -206,15 +209,7 @@ class WalletRestoreFromQRCode {
: throw Exception('Unexpected restore mode: spend_key or view_key is invalid');
}
- if (type == WalletType.ethereum && credentials.containsKey('private_key')) {
- final privateKey = credentials['private_key'] as String;
- if (privateKey.isEmpty) {
- throw Exception('Unexpected restore mode: private_key');
- }
- return WalletRestoreMode.keys;
- }
-
- if (type == WalletType.polygon && credentials.containsKey('private_key')) {
+ if (isEVMCompatibleChain(type) && credentials.containsKey('private_key')) {
final privateKey = credentials['private_key'] as String;
if (privateKey.isEmpty) {
throw Exception('Unexpected restore mode: private_key');
diff --git a/lib/view_model/send/fees_view_model.dart b/lib/view_model/send/fees_view_model.dart
index f56212d..ff87883 100644
--- a/lib/view_model/send/fees_view_model.dart
+++ b/lib/view_model/send/fees_view_model.dart
@@ -1,3 +1,4 @@
+import 'package:cake_wallet/base/base.dart';
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
import 'package:cake_wallet/decred/decred.dart';
import 'package:cake_wallet/dogecoin/dogecoin.dart';
@@ -90,6 +91,8 @@ abstract class FeesViewModelBase extends WalletChangeListenerViewModel with Stor
return transactionPriority == bitcoinCash!.getBitcoinCashTransactionPrioritySlow();
case WalletType.polygon:
return transactionPriority == polygon!.getPolygonTransactionPrioritySlow();
+ case WalletType.base:
+ return transactionPriority == base!.getBaseTransactionPrioritySlow();
case WalletType.decred:
return transactionPriority == decred!.getDecredTransactionPrioritySlow();
case WalletType.dogecoin:
@@ -198,6 +201,9 @@ abstract class FeesViewModelBase extends WalletChangeListenerViewModel with Stor
case WalletType.polygon:
_settingsStore.priority[wallet.type] = polygon!.getDefaultTransactionPriority();
break;
+ case WalletType.base:
+ _settingsStore.priority[wallet.type] = base!.getDefaultTransactionPriority();
+ break;
default:
break;
}
diff --git a/lib/view_model/send/output.dart b/lib/view_model/send/output.dart
index 5e7c8d4..a599ae8 100644
--- a/lib/view_model/send/output.dart
+++ b/lib/view_model/send/output.dart
@@ -1,3 +1,4 @@
+import 'package:cake_wallet/base/base.dart';
import 'package:cake_wallet/decred/decred.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart';
@@ -114,6 +115,9 @@ abstract class OutputBase with Store {
case WalletType.polygon:
_amount = polygon!.formatterPolygonParseAmount(_cryptoAmount);
break;
+ case WalletType.base:
+ _amount = base!.formatterBaseParseAmount(_cryptoAmount);
+ break;
case WalletType.wownero:
_amount = wownero!.formatterWowneroParseAmount(amount: _cryptoAmount);
break;
@@ -196,6 +200,10 @@ abstract class OutputBase with Store {
return polygon!.formatterPolygonAmountToDouble(amount: BigInt.from(fee));
}
+ if (_wallet.type == WalletType.base) {
+ return base!.formatterBaseAmountToDouble(amount: BigInt.from(fee));
+ }
+
if (_wallet.type == WalletType.zano) {
return zano!.formatterIntAmountToDouble(
amount: fee, currency: cryptoCurrencyHandler(), forFee: true);
@@ -328,6 +336,7 @@ abstract class OutputBase with Store {
case WalletType.monero:
case WalletType.ethereum:
case WalletType.polygon:
+ case WalletType.base:
case WalletType.solana:
case WalletType.tron:
case WalletType.haven:
diff --git a/lib/view_model/send/send_template_view_model.dart b/lib/view_model/send/send_template_view_model.dart
index 106da72..4315486 100644
--- a/lib/view_model/send/send_template_view_model.dart
+++ b/lib/view_model/send/send_template_view_model.dart
@@ -1,6 +1,7 @@
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/view_model/send/template_view_model.dart';
import 'package:cw_core/crypto_currency.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/entities/template.dart';
@@ -55,6 +56,7 @@ abstract class SendTemplateViewModelBase with Store {
_wallet.type != WalletType.haven &&
_wallet.type != WalletType.ethereum &&
_wallet.type != WalletType.polygon &&
+ _wallet.type != WalletType.base &&
_wallet.type != WalletType.solana &&
_wallet.type != WalletType.tron;
diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart
index 2a33f04..55bfabb 100644
--- a/lib/view_model/send/send_view_model.dart
+++ b/lib/view_model/send/send_view_model.dart
@@ -1,5 +1,6 @@
import 'dart:async';
+import 'package:cake_wallet/base/base.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/core/address_validator.dart';
import 'package:cake_wallet/core/amount_validator.dart';
@@ -45,6 +46,7 @@ import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_mod
import 'package:cake_wallet/wownero/wownero.dart';
import 'package:cake_wallet/zano/zano.dart';
import 'package:cw_core/crypto_currency.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/exceptions.dart';
import 'package:cw_core/pending_transaction.dart';
import 'package:cw_core/sync_status.dart';
@@ -215,6 +217,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
switch (type) {
case WalletType.ethereum:
case WalletType.polygon:
+ case WalletType.base:
case WalletType.tron:
case WalletType.solana:
return wallet.currency;
@@ -708,6 +711,9 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
case WalletType.polygon:
return polygon!.createPolygonTransactionCredentials(outputs,
priority: priority!, currency: selectedCryptoCurrency);
+ case WalletType.base:
+ return base!.createBaseTransactionCredentials(outputs,
+ priority: priority!, currency: selectedCryptoCurrency);
case WalletType.solana:
return solana!
.createSolanaTransactionCredentials(outputs, currency: selectedCryptoCurrency);
@@ -833,6 +839,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
}
if (walletType == WalletType.ethereum ||
walletType == WalletType.polygon ||
+ walletType == WalletType.base ||
walletType == WalletType.haven) {
if (errorMessage.contains('gas required exceeds allowance')) {
return S.current.gas_exceeds_allowance;
diff --git a/lib/view_model/settings/privacy_settings_view_model.dart b/lib/view_model/settings/privacy_settings_view_model.dart
index 22032b7..150ccdd 100644
--- a/lib/view_model/settings/privacy_settings_view_model.dart
+++ b/lib/view_model/settings/privacy_settings_view_model.dart
@@ -1,3 +1,4 @@
+import 'package:cake_wallet/base/base.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
import 'package:cake_wallet/entities/exchange_api_mode.dart';
@@ -75,6 +76,9 @@ abstract class PrivacySettingsViewModelBase with Store {
@computed
bool get usePolygonScan => _settingsStore.usePolygonScan;
+ @computed
+ bool get useBaseScan => _settingsStore.useBaseScan;
+
@computed
bool get useTronGrid => _settingsStore.useTronGrid;
@@ -112,6 +116,8 @@ abstract class PrivacySettingsViewModelBase with Store {
bool get canUsePolygonScan => _wallet.type == WalletType.polygon;
+ bool get canUseBaseScan => _wallet.type == WalletType.base;
+
bool get canUseTronGrid => _wallet.type == WalletType.tron;
bool get canUseMempoolFeeAPI => _wallet.type == WalletType.bitcoin;
@@ -177,6 +183,12 @@ abstract class PrivacySettingsViewModelBase with Store {
polygon!.updatePolygonScanUsageState(_wallet, value);
}
+ @action
+ void setUseBaseScan(bool value) {
+ _settingsStore.useBaseScan = value;
+ base!.updateBaseScanUsageState(_wallet, value);
+ }
+
@action
void setUseTronGrid(bool value) {
_settingsStore.useTronGrid = value;
diff --git a/lib/view_model/transaction_details_view_model.dart b/lib/view_model/transaction_details_view_model.dart
index e55bc04..dde5905 100644
--- a/lib/view_model/transaction_details_view_model.dart
+++ b/lib/view_model/transaction_details_view_model.dart
@@ -1,5 +1,6 @@
import 'package:cake_wallet/tron/tron.dart';
import 'package:cake_wallet/wownero/wownero.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/transaction_info.dart';
@@ -73,6 +74,9 @@ abstract class TransactionDetailsViewModelBase with Store {
case WalletType.polygon:
_addPolygonListItems(tx, dateFormat);
break;
+ case WalletType.base:
+ _addBaseListItems(tx, dateFormat);
+ break;
case WalletType.solana:
_addSolanaListItems(tx, dateFormat);
break;
@@ -198,6 +202,8 @@ abstract class TransactionDetailsViewModelBase with Store {
return 'https://${wallet.isTestnet ? "testnet" : "dcrdata"}.decred.org/tx/${txId.split(':')[0]}';
case WalletType.dogecoin:
return 'https://blockchair.com/dogecoin/transaction/${txId}';
+ case WalletType.base:
+ return 'https://basescan.org/tx/${txId}';
case WalletType.none:
return '';
}
@@ -233,6 +239,8 @@ abstract class TransactionDetailsViewModelBase with Store {
return S.current.view_transaction_on + 'explorer.zano.org';
case WalletType.decred:
return S.current.view_transaction_on + 'dcrdata.decred.org';
+ case WalletType.base:
+ return S.current.view_transaction_on + 'basescan.org';
case WalletType.none:
return '';
}
@@ -548,6 +556,56 @@ abstract class TransactionDetailsViewModelBase with Store {
items.addAll(_items);
}
+ void _addBaseListItems(TransactionInfo tx, DateFormat dateFormat) {
+ final _items = [
+ StandartListItem(
+ title: S.current.transaction_details_transaction_id,
+ value: tx.txHash,
+ key: ValueKey('standard_list_item_transaction_details_id_key'),
+ ),
+ StandartListItem(
+ title: S.current.transaction_details_date,
+ value: dateFormat.format(tx.date),
+ key: ValueKey('standard_list_item_transaction_details_date_key'),
+ ),
+ StandartListItem(
+ title: S.current.confirmations,
+ value: tx.confirmations.toString(),
+ key: ValueKey('standard_list_item_transaction_confirmations_key'),
+ ),
+ StandartListItem(
+ title: S.current.transaction_details_height,
+ value: '${tx.height}',
+ key: ValueKey('standard_list_item_transaction_details_height_key'),
+ ),
+ StandartListItem(
+ title: S.current.transaction_details_amount,
+ value: tx.amountFormatted(),
+ key: ValueKey('standard_list_item_transaction_details_amount_key'),
+ ),
+ if (tx.feeFormatted()?.isNotEmpty ?? false)
+ StandartListItem(
+ title: S.current.transaction_details_fee,
+ value: tx.feeFormatted()!,
+ key: ValueKey('standard_list_item_transaction_details_fee_key'),
+ ),
+ if (showRecipientAddress && tx.to != null && tx.direction == TransactionDirection.outgoing)
+ StandartListItem(
+ title: S.current.transaction_details_recipient_address,
+ value: tx.to!,
+ key: ValueKey('standard_list_item_transaction_details_recipient_address_key'),
+ ),
+ if (tx.direction == TransactionDirection.incoming && tx.from != null)
+ StandartListItem(
+ title: S.current.transaction_details_source_address,
+ value: tx.from!,
+ key: ValueKey('standard_list_item_transaction_details_source_address_key'),
+ ),
+ ];
+
+ items.addAll(_items);
+ }
+
void _addSolanaListItems(TransactionInfo tx, DateFormat dateFormat) {
final _items = [
StandartListItem(
diff --git a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
index 49ee117..7f2a78b 100644
--- a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
+++ b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
@@ -1,6 +1,7 @@
import 'dart:developer' as dev;
import 'dart:core';
+import 'package:cake_wallet/base/base.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/core/fiat_conversion_service.dart';
import 'package:cake_wallet/core/payment_uris.dart';
@@ -30,6 +31,7 @@ import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_i
import 'package:cake_wallet/wownero/wownero.dart';
import 'package:cw_core/amount_converter.dart';
import 'package:cw_core/currency.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:intl/intl.dart';
import 'package:mobx/mobx.dart';
@@ -38,7 +40,6 @@ part 'wallet_address_list_view_model.g.dart';
class WalletAddressListViewModel = WalletAddressListViewModelBase with _$WalletAddressListViewModel;
-
abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewModel with Store {
WalletAddressListViewModelBase({
required AppStore appStore,
@@ -143,6 +144,8 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
return DecredURI(amount: amount, address: address.address);
case WalletType.dogecoin:
return DogeURI(amount: amount, address: address.address);
+ case WalletType.base:
+ return BaseURI(amount: amount, address: address.address);
case WalletType.none:
throw Exception('Unexpected type: ${type.toString()}');
}
@@ -263,6 +266,12 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
}
+ if (wallet.type == WalletType.base) {
+ final primaryAddress = base!.getAddress(wallet);
+
+ addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
+ }
+
if (wallet.type == WalletType.solana) {
final primaryAddress = solana!.getAddress(wallet);
@@ -435,7 +444,12 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
'assets/images/zano_icon.svg',
'assets/images/more_tokens.svg',
];
-
+ case WalletType.base:
+ return [
+ 'assets/images/eth_icon.svg',
+ 'assets/images/usdc_icon.svg',
+ 'assets/images/more_tokens.svg',
+ ];
default:
return [];
}
@@ -457,6 +471,8 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
return 'assets/images/trx_chain_mono.svg';
case WalletType.zano:
return 'assets/images/zano_chain_mono.svg';
+ case WalletType.base:
+ return 'assets/images/base_chain_mono.svg';
default:
return 'assets/images/eth_chain_mono.svg';
}
diff --git a/lib/view_model/wallet_keys_view_model.dart b/lib/view_model/wallet_keys_view_model.dart
index 1f76204..f429cd9 100644
--- a/lib/view_model/wallet_keys_view_model.dart
+++ b/lib/view_model/wallet_keys_view_model.dart
@@ -126,6 +126,7 @@ abstract class WalletKeysViewModelBase with Store {
break;
case WalletType.ethereum:
case WalletType.polygon:
+ case WalletType.base:
case WalletType.solana:
case WalletType.tron:
items.addAll([
@@ -252,6 +253,8 @@ abstract class WalletKeysViewModelBase with Store {
return 'banano-wallet';
case WalletType.polygon:
return 'polygon-wallet';
+ case WalletType.base:
+ return 'base-wallet';
case WalletType.solana:
return 'solana-wallet';
case WalletType.tron:
diff --git a/lib/view_model/wallet_new_vm.dart b/lib/view_model/wallet_new_vm.dart
index 6e7a4cb..7ee0c03 100644
--- a/lib/view_model/wallet_new_vm.dart
+++ b/lib/view_model/wallet_new_vm.dart
@@ -1,3 +1,4 @@
+import 'package:cake_wallet/base/base.dart';
import 'package:cake_wallet/core/new_wallet_arguments.dart';
import 'package:cake_wallet/dogecoin/dogecoin.dart';
import 'package:cake_wallet/ethereum/ethereum.dart';
@@ -90,6 +91,13 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
mnemonic: newWalletArguments!.mnemonic,
passphrase: passphrase,
);
+ case WalletType.base:
+ return base!.createBaseNewWalletCredentials(
+ name: name,
+ password: walletPassword,
+ mnemonic: newWalletArguments!.mnemonic,
+ passphrase: passphrase,
+ );
case WalletType.bitcoinCash:
return bitcoinCash!.createBitcoinCashNewWalletCredentials(
name: name,
diff --git a/lib/view_model/wallet_restore_view_model.dart b/lib/view_model/wallet_restore_view_model.dart
index cd10d14..3a5716b 100644
--- a/lib/view_model/wallet_restore_view_model.dart
+++ b/lib/view_model/wallet_restore_view_model.dart
@@ -1,3 +1,4 @@
+import 'package:cake_wallet/base/base.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
import 'package:cake_wallet/core/generate_wallet_password.dart';
@@ -51,6 +52,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
case WalletType.haven:
case WalletType.ethereum:
case WalletType.polygon:
+ case WalletType.base:
case WalletType.decred:
case WalletType.bitcoin:
availableModes = [WalletRestoreMode.seed, WalletRestoreMode.keys];
@@ -90,6 +92,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
late final bool hasRestoreFromPrivateKey = [
WalletType.ethereum,
WalletType.polygon,
+ WalletType.base,
WalletType.nano,
WalletType.banano,
WalletType.solana,
@@ -174,6 +177,13 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
password: password,
passphrase: passphrase,
);
+ case WalletType.base:
+ return base!.createBaseRestoreWalletFromSeedCredentials(
+ name: name,
+ mnemonic: seed,
+ password: password,
+ passphrase: passphrase,
+ );
case WalletType.solana:
return solana!.createSolanaRestoreWalletFromSeedCredentials(
name: name,
@@ -261,6 +271,12 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
password: password,
privateKey: options['private_key'] as String,
);
+ case WalletType.base:
+ return base!.createBaseRestoreWalletFromPrivateKey(
+ name: name,
+ password: password,
+ privateKey: options['private_key'] as String,
+ );
case WalletType.solana:
return solana!.createSolanaRestoreWalletFromPrivateKey(
name: name,
diff --git a/model_generator.sh b/model_generator.sh
index 4493e62..7b72092 100755
--- a/model_generator.sh
+++ b/model_generator.sh
@@ -1,7 +1,7 @@
#!/bin/bash
set -x -e
-for cwcoin in cw_{core,evm,monero,bitcoin,nano,bitcoin_cash,solana,tron,wownero,zano,decred,dogecoin}
+for cwcoin in cw_{core,evm,monero,bitcoin,nano,bitcoin_cash,solana,tron,wownero,zano,decred,dogecoin,base}
do
if [[ "x$1" == "xasync" ]];
then
diff --git a/pubspec_base.yaml b/pubspec_base.yaml
index c5b8480..db229a3 100644
--- a/pubspec_base.yaml
+++ b/pubspec_base.yaml
@@ -235,6 +235,7 @@ flutter:
- assets/zano_node_list.yml
- assets/decred_node_list.yml
- assets/dogecoin_electrum_server_list.yml
+ - assets/base_node_list.yml
- assets/text/
- assets/faq/
- assets/animation/
diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb
index f4c102c..2ec9aa8 100644
--- a/res/values/strings_ar.arb
+++ b/res/values/strings_ar.arb
@@ -88,6 +88,7 @@
"backup_password": "كلمة مرور النسخ الاحتياطي",
"balance": "توازن",
"balance_page": "صفحة التوازن",
+ "basescan_history": "تاريخ باسيسكان",
"bill_amount": "مبلغ الفاتورة",
"billing_address_info": "إذا طُلب منك عنوان إرسال فواتير ، فأدخل عنوان الشحن الخاص بك",
"biometric_auth_reason": "امسح بصمة إصبعك للمصادقة",
diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb
index 0fb5c6d..c6d4eb0 100644
--- a/res/values/strings_bg.arb
+++ b/res/values/strings_bg.arb
@@ -88,6 +88,7 @@
"backup_password": "Парола за възстановяване",
"balance": "Баланс",
"balance_page": "Страница за баланс",
+ "basescan_history": "История на басескан",
"bill_amount": "Искана сума",
"billing_address_info": "Ако Ви попитат за билинг адрес, въведето своя адрес за доставка",
"biometric_auth_reason": "Сканирайте своя пръстов отпечатък",
diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb
index 334c2a8..473cb61 100644
--- a/res/values/strings_cs.arb
+++ b/res/values/strings_cs.arb
@@ -88,6 +88,7 @@
"backup_password": "Heslo pro zálohy",
"balance": "Zůstatek",
"balance_page": "Stránka zůstatku",
+ "basescan_history": "Historie basescan",
"bill_amount": "Účtovaná částka",
"billing_address_info": "Při dotazu na fakturační adresu, zadejte svou doručovací adresu",
"biometric_auth_reason": "Naskenujte otisk prstu pro ověření",
diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb
index eb590b2..c1b0ae8 100644
--- a/res/values/strings_de.arb
+++ b/res/values/strings_de.arb
@@ -88,6 +88,7 @@
"backup_password": "Passwort sichern",
"balance": "Saldo",
"balance_page": "Guthaben-Seite",
+ "basescan_history": "Baseskangeschichte",
"bill_amount": "Rechnungsbetrag",
"billing_address_info": "Wenn Sie nach einer Rechnungsadresse gefragt werden, geben Sie bitte Ihre Lieferadresse an",
"biometric_auth_reason": "Scannen Sie Ihren Fingerabdruck zur Authentifizierung",
diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb
index 2848ce6..c6b0a6d 100644
--- a/res/values/strings_en.arb
+++ b/res/values/strings_en.arb
@@ -88,6 +88,7 @@
"backup_password": "Backup password",
"balance": "Balance",
"balance_page": "Balance Page",
+ "basescan_history": "BaseScan History",
"bill_amount": "Bill Amount",
"billing_address_info": "If asked for a billing address, provide your shipping address",
"biometric_auth_reason": "Scan your fingerprint to authenticate",
diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb
index 834c849..95d7bc3 100644
--- a/res/values/strings_es.arb
+++ b/res/values/strings_es.arb
@@ -88,6 +88,7 @@
"backup_password": "Contraseña de respaldo",
"balance": "Balance",
"balance_page": "Página de saldo",
+ "basescan_history": "Historia de Basescan",
"bill_amount": "Importe de la factura",
"billing_address_info": "Si se le solicita una dirección de facturación, proporcione su dirección de envío",
"biometric_auth_reason": "Escanee su huella digital para autenticar",
diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb
index c1f54b3..b35883a 100644
--- a/res/values/strings_fr.arb
+++ b/res/values/strings_fr.arb
@@ -88,6 +88,7 @@
"backup_password": "Mot de passe de sauvegarde",
"balance": "Solde",
"balance_page": "Page Solde",
+ "basescan_history": "HISTOIRE DE BASESCAN",
"bill_amount": "Montant de la facture",
"billing_address_info": "Si une adresse de facturation vous est demandée, indiquez votre adresse de livraison",
"biometric_auth_reason": "Scannez votre empreinte digitale pour vous authentifier",
diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb
index e9069a4..cfe6f04 100644
--- a/res/values/strings_ha.arb
+++ b/res/values/strings_ha.arb
@@ -88,6 +88,7 @@
"backup_password": "Ajiyayyen kalmar sirri",
"balance": "Ma'auni",
"balance_page": "Ma'auni Page",
+ "basescan_history": "Tarihin Basescan",
"bill_amount": "Adadin Bill",
"billing_address_info": "Idan an nemi adireshin biyan kuɗi, samar da adireshin jigilar kaya",
"biometric_auth_reason": "Duba hoton yatsa don tantancewa",
diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb
index 0515899..efb87f4 100644
--- a/res/values/strings_hi.arb
+++ b/res/values/strings_hi.arb
@@ -88,6 +88,7 @@
"backup_password": "बैकअप पासवर्ड",
"balance": "संतुलन",
"balance_page": "बैलेंस पेज",
+ "basescan_history": "बेसस्कैन इतिहास",
"bill_amount": "बिल राशि",
"billing_address_info": "यदि बिलिंग पता मांगा जाए, तो अपना शिपिंग पता प्रदान करें",
"biometric_auth_reason": "प्रमाणित करने के लिए अपने फ़िंगरप्रिंट को स्कैन करें",
diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb
index b65dd07..c1feb69 100644
--- a/res/values/strings_hr.arb
+++ b/res/values/strings_hr.arb
@@ -88,6 +88,7 @@
"backup_password": "Lozinka za sigurnosnu kopiju",
"balance": "Uravnotežiti",
"balance_page": "Stranica sa stanjem",
+ "basescan_history": "Basescan povijest",
"bill_amount": "Iznos računa",
"billing_address_info": "Ako se od vas zatraži adresa za naplatu, navedite svoju adresu za dostavu",
"biometric_auth_reason": "Skenirajte svoj otisak prsta za autentifikaciju",
diff --git a/res/values/strings_hy.arb b/res/values/strings_hy.arb
index 12fec6e..98de9b9 100644
--- a/res/values/strings_hy.arb
+++ b/res/values/strings_hy.arb
@@ -88,6 +88,7 @@
"backup_password": "Կրկնօրինակի գաղտնաբառ",
"balance": "Հաշվեկշիռ",
"balance_page": "Հաշվեկշռի էջ",
+ "basescan_history": "Բասեսկանի պատմություն",
"bill_amount": "Հաշիվը",
"billing_address_info": "Եթե խնդրեն հաշվեվճարի հասցե, ապա տրամադրեք ձեր առաքման հասցեն",
"biometric_auth_reason": "Սկանավեք ձեր մատնահետքը նույնականացման համար",
diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb
index 6cc5152..9b55aa4 100644
--- a/res/values/strings_id.arb
+++ b/res/values/strings_id.arb
@@ -88,6 +88,7 @@
"backup_password": "Kata sandi cadangan",
"balance": "Keseimbangan",
"balance_page": "Halaman Saldo",
+ "basescan_history": "Sejarah Basescan",
"bill_amount": "Jumlah Tagihan",
"billing_address_info": "Jika diminta alamat billing, berikan alamat pengiriman Anda",
"biometric_auth_reason": "Pindai sidik jari Anda untuk mengautentikasi",
diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb
index 9ceff0c..a91c2db 100644
--- a/res/values/strings_it.arb
+++ b/res/values/strings_it.arb
@@ -88,6 +88,7 @@
"backup_password": "Backup password",
"balance": "Saldo",
"balance_page": "Pagina di saldo",
+ "basescan_history": "Storia di Basescan",
"bill_amount": "Importo della ricevuta",
"billing_address_info": "Se ti viene richiesto un indirizzo di fatturazione, fornisci il tuo indirizzo di spedizione",
"biometric_auth_reason": "Scansiona la tua impronta per autenticarti",
diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb
index 7babd77..0928806 100644
--- a/res/values/strings_ja.arb
+++ b/res/values/strings_ja.arb
@@ -88,6 +88,7 @@
"backup_password": "バックアップパスワード",
"balance": "バランス",
"balance_page": "残高ページ",
+ "basescan_history": "Basescanの歴史",
"bill_amount": "請求額",
"billing_address_info": "請求先住所を尋ねられた場合は、配送先住所を入力してください",
"biometric_auth_reason": "प指紋をスキャンして認証する",
diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb
index b7c00cd..c69cef3 100644
--- a/res/values/strings_ko.arb
+++ b/res/values/strings_ko.arb
@@ -88,6 +88,7 @@
"backup_password": "백업 비밀번호",
"balance": "잔액",
"balance_page": "잔액 페이지",
+ "basescan_history": "바스 스칸 역사",
"bill_amount": "청구 금액",
"billing_address_info": "청구지 주소를 묻는 경우 배송 주소를 제공하세요",
"biometric_auth_reason": "인증하려면 지문을 스캔하세요",
diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb
index 4005cc3..46c3087 100644
--- a/res/values/strings_my.arb
+++ b/res/values/strings_my.arb
@@ -88,6 +88,7 @@
"backup_password": "စကားဝှက်ကို အရန်သိမ်းဆည်းပါ။",
"balance": "လက်ကျန်ငေှ",
"balance_page": "လက်ကျန်စာမျက်နှာ",
+ "basescan_history": "Bascan သမိုင်း",
"bill_amount": "ဘီလ်ပမာဏ",
"billing_address_info": "ငွေပေးချေရမည့်လိပ်စာကို တောင်းဆိုပါက သင့်ပို့ဆောင်ရေးလိပ်စာကို ပေးပါ။",
"biometric_auth_reason": "စစ်မှန်ကြောင်းအထောက်အထားပြရန် သင့်လက်ဗွေကို စကန်ဖတ်ပါ။",
diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb
index df67a94..8bd0f36 100644
--- a/res/values/strings_nl.arb
+++ b/res/values/strings_nl.arb
@@ -88,6 +88,7 @@
"backup_password": "Reservewachtwoord",
"balance": "Evenwicht",
"balance_page": "Saldo pagina",
+ "basescan_history": "BASESCAN GESCHIEDENIS",
"bill_amount": "Bill bedrag",
"billing_address_info": "Als u om een factuuradres wordt gevraagd, geef dan uw verzendadres op",
"biometric_auth_reason": "Scan uw vingerafdruk om te verifiëren",
diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb
index 273cf14..9c67ed7 100644
--- a/res/values/strings_pl.arb
+++ b/res/values/strings_pl.arb
@@ -88,6 +88,7 @@
"backup_password": "Hasło kpoii zapasowej",
"balance": "Saldo",
"balance_page": "Strona salda",
+ "basescan_history": "Historia Basescan",
"bill_amount": "Kwota rachunku",
"billing_address_info": "Jeśli zostaniesz poproszony o podanie adresu rozliczeniowego, podaj swój adres wysyłki",
"biometric_auth_reason": "Zeskanuj swój odcisk palca, aby uwierzytelnić",
diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb
index 10eef44..520d326 100644
--- a/res/values/strings_pt.arb
+++ b/res/values/strings_pt.arb
@@ -88,6 +88,7 @@
"backup_password": "Senha de backup",
"balance": "Equilíbrio",
"balance_page": "Página de saldo",
+ "basescan_history": "História de Basescan",
"bill_amount": "Valor da conta",
"billing_address_info": "Se for solicitado um endereço de cobrança, forneça seu endereço de entrega",
"biometric_auth_reason": "Digitalize sua impressão digital para autenticar",
diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb
index 419da09..939ceb0 100644
--- a/res/values/strings_ru.arb
+++ b/res/values/strings_ru.arb
@@ -88,6 +88,7 @@
"backup_password": "Пароль резервной копии",
"balance": "Баланс",
"balance_page": "Страница баланса",
+ "basescan_history": "Басесканская история",
"bill_amount": "Сумма счета",
"billing_address_info": "Если вас попросят указать платежный адрес, укажите адрес доставки",
"biometric_auth_reason": "Отсканируйте свой отпечаток пальца для аутентификации",
diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb
index d84e169..ff9c693 100644
--- a/res/values/strings_th.arb
+++ b/res/values/strings_th.arb
@@ -88,6 +88,7 @@
"backup_password": "รหัสผ่านสำรองข้อมูล",
"balance": "สมดุล",
"balance_page": "หน้ายอดคงเหลือ",
+ "basescan_history": "ประวัติศาสตร์ Basescan",
"bill_amount": "จำนวนบิล",
"billing_address_info": "ถ้าถูกร้องขอที่อยู่สำหรับการวางบิล ให้ใช้ที่อยู่จัดส่งของคุณ",
"biometric_auth_reason": "สแกนลายนิ้วมือของคุณเพื่อยืนยันตัวตน",
diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb
index 0c597a8..972b42b 100644
--- a/res/values/strings_tl.arb
+++ b/res/values/strings_tl.arb
@@ -88,6 +88,7 @@
"backup_password": "Backup na password",
"balance": "Balanse",
"balance_page": "Pahina ng Balanse",
+ "basescan_history": "Kasaysayan ng Basescan",
"bill_amount": "Halaga ng Bill",
"billing_address_info": "Kung humihingi ng billing address, ibigay ang iyong shipping address",
"biometric_auth_reason": "I-scan ang iyong fingerprint para ma-authenticate",
diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb
index 1ab66df..2db6318 100644
--- a/res/values/strings_tr.arb
+++ b/res/values/strings_tr.arb
@@ -88,6 +88,7 @@
"backup_password": "Yedek parolası",
"balance": "Denge",
"balance_page": "Bakiye Sayfası",
+ "basescan_history": "Basescan tarihi",
"bill_amount": "Fatura Tutarı",
"billing_address_info": "Eğer fatura adresi istenirse, kargo adresinizi girin",
"biometric_auth_reason": "Kimlik doğrulaması için parmak izini okutun",
diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb
index 00477ee..76d6daf 100644
--- a/res/values/strings_uk.arb
+++ b/res/values/strings_uk.arb
@@ -88,6 +88,7 @@
"backup_password": "Пароль резервної копії",
"balance": "Балансувати",
"balance_page": "Сторінка балансу",
+ "basescan_history": "Базесканська історія",
"bill_amount": "Сума рахунку",
"billing_address_info": "Якщо буде запропоновано платіжну адресу, вкажіть свою адресу доставки",
"biometric_auth_reason": "Відскануйте свій відбиток пальця для аутентифікації",
diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb
index 479fbc5..4799a29 100644
--- a/res/values/strings_ur.arb
+++ b/res/values/strings_ur.arb
@@ -88,6 +88,7 @@
"backup_password": "بیک اپ پاس ورڈ",
"balance": "بقیہ",
"balance_page": "بیلنس صفحہ",
+ "basescan_history": "باسکی تاریخ",
"bill_amount": "بل رقم",
"billing_address_info": "اگر آپ سے بلنگ کا پتہ پوچھا جائے تو اپنا شپنگ ایڈریس فراہم کریں۔",
"biometric_auth_reason": "تصدیق کرنے کے لیے اپنے فنگر پرنٹ کو اسکین کریں۔",
diff --git a/res/values/strings_vi.arb b/res/values/strings_vi.arb
index f7435ce..b9942e8 100644
--- a/res/values/strings_vi.arb
+++ b/res/values/strings_vi.arb
@@ -88,6 +88,7 @@
"backup_password": "Mật khẩu sao lưu",
"balance": "Số dư",
"balance_page": "Trang số dư",
+ "basescan_history": "Lịch sử Basescan",
"bill_amount": "Số tiền hóa đơn",
"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",
"biometric_auth_reason": "Quét vân tay để xác thực",
diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb
index 0e02afc..94b96be 100644
--- a/res/values/strings_yo.arb
+++ b/res/values/strings_yo.arb
@@ -88,6 +88,7 @@
"backup_password": "Ṣẹ̀dà ọ̀rọ̀ aṣínà",
"balance": "Iwọntunwọnsi",
"balance_page": "Oju-iwe iwọntunwọnsi",
+ "basescan_history": "Itan Basiki",
"bill_amount": "Iye ìwé owó",
"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í",
"biometric_auth_reason": "Ya ìka ọwọ́ yín láti ṣe ìfẹ̀rílàdí",
diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb
index ab51211..6b4cbcb 100644
--- a/res/values/strings_zh.arb
+++ b/res/values/strings_zh.arb
@@ -88,6 +88,7 @@
"backup_password": "备份密码",
"balance": "平衡",
"balance_page": "余额页",
+ "basescan_history": "Basescan历史",
"bill_amount": "账单金额",
"billing_address_info": "如果要求提供帐单地址,请提供您的送货地址",
"biometric_auth_reason": "扫描指纹进行身份认证",
diff --git a/scripts/android/pubspec_gen.sh b/scripts/android/pubspec_gen.sh
index 8170b7f..66cd4d6 100755
--- a/scripts/android/pubspec_gen.sh
+++ b/scripts/android/pubspec_gen.sh
@@ -10,7 +10,7 @@ case $APP_ANDROID_TYPE in
CONFIG_ARGS="--monero"
;;
$CAKEWALLET)
- CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --zano --decred --dogecoin"
+ CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --zano --decred --dogecoin --base"
;;
esac
diff --git a/scripts/build_bitbox_flutter.sh b/scripts/build_bitbox_flutter.sh
index 3f0a563..d21014a 100755
--- a/scripts/build_bitbox_flutter.sh
+++ b/scripts/build_bitbox_flutter.sh
@@ -2,7 +2,7 @@
git clone https://github.com/konstantinullrich/bitbox_flutter
cd bitbox_flutter
-bash ./build_bindings.sh --dont-install
+bash ./build_bindings.sh
FILE=api.aar
if [ -f "$FILE" ]; then
diff --git a/scripts/ios/app_config.sh b/scripts/ios/app_config.sh
index 78a8f7d..f69c9f3 100755
--- a/scripts/ios/app_config.sh
+++ b/scripts/ios/app_config.sh
@@ -31,7 +31,7 @@ case $APP_IOS_TYPE in
;;
$CAKEWALLET)
- CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --zano --decred --dogecoin"
+ CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --zano --decred --dogecoin --base"
;;
esac
diff --git a/scripts/linux/app_config.sh b/scripts/linux/app_config.sh
index 55cfef6..b579f2c 100755
--- a/scripts/linux/app_config.sh
+++ b/scripts/linux/app_config.sh
@@ -13,7 +13,7 @@ CONFIG_ARGS=""
case $APP_LINUX_TYPE in
$CAKEWALLET)
- CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --dogecoin";;
+ CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --dogecoin --base";;
esac
cp -rf pubspec_description.yaml pubspec.yaml
diff --git a/scripts/macos/app_config.sh b/scripts/macos/app_config.sh
index dfea3de..0ae44f2 100755
--- a/scripts/macos/app_config.sh
+++ b/scripts/macos/app_config.sh
@@ -36,7 +36,7 @@ case $APP_MACOS_TYPE in
$MONERO_COM)
CONFIG_ARGS="--monero";;
$CAKEWALLET)
- CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --dogecoin";;
+ CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --dogecoin --base";;
esac
cp -rf pubspec_description.yaml pubspec.yaml
diff --git a/tool/configure.dart b/tool/configure.dart
index f003ba1..50930c5 100644
--- a/tool/configure.dart
+++ b/tool/configure.dart
@@ -12,6 +12,7 @@ const wowneroOutputPath = 'lib/wownero/wownero.dart';
const zanoOutputPath = 'lib/zano/zano.dart';
const decredOutputPath = 'lib/decred/decred.dart';
const dogecoinOutputPath = 'lib/dogecoin/dogecoin.dart';
+const baseOutputPath = 'lib/base/base.dart';
const walletTypesPath = 'lib/wallet_types.g.dart';
const secureStoragePath = 'lib/core/secure_storage.dart';
const pubspecDefaultPath = 'pubspec_default.yaml';
@@ -32,6 +33,7 @@ Future<void> main(List<String> args) async {
final hasZano = args.contains('${prefix}zano');
final hasDecred = args.contains('${prefix}decred');
final hasDogecoin = args.contains('${prefix}dogecoin');
+ final hasBase = args.contains('${prefix}base');
final excludeFlutterSecureStorage = args.contains('${prefix}excludeFlutterSecureStorage');
await generateBitcoin(hasBitcoin);
@@ -47,6 +49,7 @@ Future<void> main(List<String> args) async {
// await generateBanano(hasEthereum);
await generateDecred(hasDecred);
await generateDogecoin(hasDogecoin);
+ await generateBase(hasBase);
await generatePubspec(
hasMonero: hasMonero,
@@ -63,6 +66,7 @@ Future<void> main(List<String> args) async {
hasZano: hasZano,
hasDecred: hasDecred,
hasDogecoin: hasDogecoin,
+ hasBase: hasBase,
);
await generateWalletTypes(
hasMonero: hasMonero,
@@ -78,6 +82,7 @@ Future<void> main(List<String> args) async {
hasZano: hasZano,
hasDecred: hasDecred,
hasDogecoin: hasDogecoin,
+ hasBase: hasBase,
);
await injectSecureStorage(!excludeFlutterSecureStorage);
}
@@ -1497,6 +1502,132 @@ abstract class DogeCoin {
await outputFile.writeAsString(output);
}
+Future<void> generateBase(bool hasImplementation) async {
+ final outputFile = File(baseOutputPath);
+ const baseCommonHeaders = """
+import 'package:cake_wallet/view_model/send/output.dart';
+import 'package:cw_core/crypto_currency.dart';
+import 'package:cw_core/erc20_token.dart';
+import 'package:cw_core/hardware/hardware_account_data.dart';
+import 'package:cw_core/hardware/hardware_wallet_service.dart';
+import 'package:cw_core/output_info.dart';
+import 'package:cw_core/pending_transaction.dart';
+import 'package:cw_core/transaction_info.dart';
+import 'package:cw_core/transaction_priority.dart';
+import 'package:cw_core/wallet_base.dart';
+import 'package:cw_core/wallet_credentials.dart';
+import 'package:cw_core/wallet_info.dart';
+import 'package:cw_core/wallet_service.dart';
+import 'package:hive/hive.dart';
+import 'package:ledger_flutter_plus/ledger_flutter_plus.dart' as ledger;
+import 'package:bitbox_flutter/bitbox_flutter.dart' as bitbox;
+import 'package:web3dart/web3dart.dart';
+
+""";
+ const baseCWHeaders = """
+import 'package:cw_evm/evm_chain_formatter.dart';
+import 'package:cw_evm/evm_chain_mnemonics.dart';
+import 'package:cw_evm/evm_chain_transaction_credentials.dart';
+import 'package:cw_evm/evm_chain_transaction_info.dart';
+import 'package:cw_evm/evm_chain_transaction_priority.dart';
+import 'package:cw_evm/evm_chain_wallet_creation_credentials.dart';
+import 'package:cw_evm/hardware/evm_chain_ledger_credentials.dart';
+import 'package:cw_evm/hardware/evm_chain_bitbox_credentials.dart';
+import 'package:cw_evm/evm_chain_wallet.dart';
+import 'package:cw_evm/hardware/evm_chain_bitbox_service.dart';
+import 'package:cw_evm/hardware/evm_chain_ledger_service.dart';
+
+import 'package:cw_base/base_client.dart';
+import 'package:cw_base/base_wallet.dart';
+import 'package:cw_base/base_wallet_service.dart';
+import 'package:cw_base/default_base_erc20_tokens.dart';
+import 'package:eth_sig_util/util/utils.dart';
+
+""";
+ const baseCwPart = "part 'cw_base.dart';";
+ const baseContent = """
+abstract class Base {
+ List<String> getBaseWordList(String language);
+ WalletService createBaseWalletService(Box<WalletInfo> walletInfoSource, bool isDirect);
+ WalletCredentials createBaseNewWalletCredentials(
+ {required String name,
+ WalletInfo? walletInfo,
+ String? password,
+ String? mnemonic,
+ String? passphrase});
+ WalletCredentials createBaseRestoreWalletFromSeedCredentials(
+ {required String name,
+ required String mnemonic,
+ required String password,
+ String? passphrase});
+ WalletCredentials createBaseRestoreWalletFromPrivateKey(
+ {required String name, required String privateKey, required String password});
+ WalletCredentials createBaseHardwareWalletCredentials(
+ {required String name, required HardwareAccountData hwAccountData, WalletInfo? walletInfo});
+ String getAddress(WalletBase wallet);
+ String getPrivateKey(WalletBase wallet);
+ String getPublicKey(WalletBase wallet);
+ TransactionPriority getDefaultTransactionPriority();
+ TransactionPriority getBaseTransactionPrioritySlow();
+ List<TransactionPriority> getTransactionPriorities();
+ TransactionPriority deserializeBaseTransactionPriority(int raw);
+
+ Object createBaseTransactionCredentials(
+ List<Output> outputs, {
+ required TransactionPriority priority,
+ required CryptoCurrency currency,
+ int? feeRate,
+ });
+
+ Object createBaseTransactionCredentialsRaw(
+ List<OutputInfo> outputs, {
+ TransactionPriority? priority,
+ required CryptoCurrency currency,
+ required int feeRate,
+ });
+
+ int formatterBaseParseAmount(String amount);
+ double formatterBaseAmountToDouble(
+ {TransactionInfo? transaction, BigInt? amount, int exponent = 18});
+ List<Erc20Token> getERC20Currencies(WalletBase wallet);
+ Future<void> addErc20Token(WalletBase wallet, CryptoCurrency token);
+ Future<void> deleteErc20Token(WalletBase wallet, CryptoCurrency token);
+ Future<void> removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token);
+ Future<Erc20Token?> getErc20Token(WalletBase wallet, String contractAddress);
+
+ Future<PendingTransaction> createTokenApproval(WalletBase wallet, BigInt amount, String spender,
+ CryptoCurrency token, TransactionPriority priority);
+
+ CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction);
+ void updateBaseScanUsageState(WalletBase wallet, bool isEnabled);
+ Web3Client? getWeb3Client(WalletBase wallet);
+ String getTokenAddress(CryptoCurrency asset);
+
+ void setHardwareWalletService(WalletBase wallet, HardwareWalletService service);
+ HardwareWalletService getLedgerHardwareWalletService(ledger.LedgerConnection connection);
+ HardwareWalletService getBitboxHardwareWalletService(bitbox.BitboxManager manager);
+ List<String> getDefaultTokenContractAddresses();
+ bool isTokenAlreadyAdded(WalletBase wallet, String contractAddress);
+}
+ """;
+
+ const baseEmptyDefinition = 'Base? base;\n';
+ const baseCWDefinition = 'Base? base = CWBase();\n';
+
+ final output = '$baseCommonHeaders\n' +
+ (hasImplementation ? '$baseCWHeaders\n' : '\n') +
+ (hasImplementation ? '$baseCwPart\n\n' : '\n') +
+ (hasImplementation ? baseCWDefinition : baseEmptyDefinition) +
+ '\n' +
+ baseContent;
+
+ if (outputFile.existsSync()) {
+ await outputFile.delete();
+ }
+
+ await outputFile.writeAsString(output);
+}
+
Future<void> generatePubspec({
required bool hasMonero,
required bool hasBitcoin,
@@ -1512,6 +1643,7 @@ Future<void> generatePubspec({
required bool hasZano,
required bool hasDecred,
required bool hasDogecoin,
+ required bool hasBase,
}) async {
const cwCore = """
cw_core:
@@ -1580,6 +1712,11 @@ Future<void> generatePubspec({
cw_dogecoin:
path: ./cw_dogecoin
""";
+ const cwBase = """
+ cw_base:
+ path: ./cw_base
+ """;
+
final inputFile = File(pubspecOutputPath);
final inputText = await inputFile.readAsString();
final inputLines = inputText.split('\n');
@@ -1649,6 +1786,10 @@ Future<void> generatePubspec({
output += '\n$cwDogecoin';
}
+ if (hasBase) {
+ output += '\n$cwBase';
+ }
+
final outputLines = output.split('\n');
inputLines.insertAll(dependenciesIndex + 1, outputLines);
final outputContent = inputLines.join('\n');
@@ -1675,6 +1816,7 @@ Future<void> generateWalletTypes({
required bool hasZano,
required bool hasDecred,
required bool hasDogecoin,
+ required bool hasBase,
}) async {
final walletTypesFile = File(walletTypesPath);
@@ -1706,6 +1848,10 @@ Future<void> generateWalletTypes({
outputContent += '\tWalletType.dogecoin,\n';
}
+ if (hasBase) {
+ outputContent += '\tWalletType.base,\n';
+ }
+
if (hasBitcoinCash) {
outputContent += '\tWalletType.bitcoinCash,\n';
}
diff --git a/tool/utils/secret_key.dart b/tool/utils/secret_key.dart
index 3bd2275..6a009ff 100644
--- a/tool/utils/secret_key.dart
+++ b/tool/utils/secret_key.dart
@@ -55,8 +55,8 @@ class SecretKey {
SecretKey('litecoinTestWalletSeeds', () => ''),
SecretKey('bitcoinCashTestWalletSeeds', () => ''),
SecretKey('polygonTestWalletSeeds', () => ''),
+ SecretKey('baseTestWalletSeeds', () => ''),
SecretKey('solanaTestWalletSeeds', () => ''),
- SecretKey('polygonTestWalletSeeds', () => ''),
SecretKey('tronTestWalletSeeds', () => ''),
SecretKey('nanoTestWalletSeeds', () => ''),
SecretKey('wowneroTestWalletSeeds', () => ''),
@@ -66,6 +66,7 @@ class SecretKey {
SecretKey('litecoinTestWalletReceiveAddress', () => ''),
SecretKey('bitcoinCashTestWalletReceiveAddress', () => ''),
SecretKey('polygonTestWalletReceiveAddress', () => ''),
+ SecretKey('baseTestWalletReceiveAddress', () => ''),
SecretKey('solanaTestWalletReceiveAddress', () => ''),
SecretKey('tronTestWalletReceiveAddress', () => ''),
SecretKey('nanoTestWalletReceiveAddress', () => ''),
Why this scored 28/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.