mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-17 05:41:29 +02:00
CW-1194-Add-Arbitrum-Wallet (#2587)
* feat: initialize cw_arbitrum package with essential files - Implement ArbitrumClient for transaction handling - Add ArbitrumWallet and related classes for wallet management - Establish transaction history and info classes for Arbitrum transactions - Add ArbitrumWalletService for wallet operations and management * feat: Add arbitrum related secrets to workflow * feat: Add nodes for arbitrum * feat: Add Arbitrum support to cryptocurrency handling * refactor: Update Arbitrum currency references from 'ARB' to 'ETH' * feat: Add Arbitrum support to wallet type and transaction handling * feat: Add Arbitrum URL schemes to Android and iOS configurations * feat: Add Arbitrum SVG icons and a WebP image * feat: Setup Arbitrum proxy * fix: Add localization * feat: Integrate Arbitrum support across various configurations and files - Updated cakewallet.bat to include Arbitrum in app configuration. - Added Arbitrum node list to pubspec_base.yaml. - Modified Android, iOS, Linux, and macOS app configuration scripts to support Arbitrum. - Introduced Arbitrum output path and generation logic in configure.dart. - Enhanced wallet type and pubspec generation to include Arbitrum. - Added Arbitrum-related secrets in secret_key.dart. * feat: Enhance Arbitrum integration with new URI class and updates across various components - Introduced ArbitrumURI class for handling Arbitrum payment URIs. - Updated seed validator, wallet creation service, and other components to support Arbitrum. - Added Arbitrum node handling in default settings migration and node list management. - Enhanced preferences and settings store to include Arbitrum-specific configurations. - Updated QR utility and token utilities to accommodate Arbitrum functionalities. * feat: Add Arbitrum support to various buy providers - Updated DFXBuyProvider, KryptonimBuyProvider, OnRamperBuyProvider, and RobinhoodBuyProvider to include Arbitrum wallet type and blockchain references. - Enhanced integration for Arbitrum across multiple components to ensure consistent support. * feat: Implement Arbitrum support in wallet selection and settings - Added Arbitrum wallet type handling in common test flows and wallet services. - Integrated Arbitrum icons in desktop wallet selection and menu widgets. - Updated privacy settings to include ArbiScan options. - Enhanced key service to support Arbitrum private and public key retrieval. * feat: Expand Arbitrum support across various view models and components - Integrated Arbitrum wallet type handling in advanced privacy settings, transaction details, and wallet management. - Added Arbitrum-specific methods for transaction and wallet creation, restoration, and fee management. - Updated dashboard and home settings to accommodate Arbitrum functionalities, including token management and transaction display. - Enhanced exchange and send view models to support Arbitrum transactions and currency handling. - Included Arbitrum in wallet address list and privacy settings for improved user experience. * refactor: Simplify token initialization in Arbitrum wallet service - Removed the migration flag from the addInitialTokens method in ArbitrumWalletService. - Updated addInitialTokens method in ArbitrumWallet to handle existing tokens more efficiently. - Enhanced initial token retrieval logic in DefaultArbitrumErc20Tokens to prioritize iconPath assignment. - Improved token update logic in BaseWallet for consistency across wallet services. * chore: Update Dart SDK constraints in pubspec.yaml * fix: Update file paths for arbitrum assets * fix: Correct box name formatting in Arbitrum wallet initialization * Update .github/workflows/pr_test_build_android.yml [skip ci * Update .github/workflows/pr_test_build_android.yml * fix: Add missing evm params * fix: Remove duplicate method declaration * refactor: simplify isSwapsXyzSendingEVMTokenSwap check * fix: Update arbitrum configs with sql storage update * feat: Add estimated fee retrieval for Arbitrum and revamp priority fee handling for EVMChainWallet * refactor: Update transaction priority handling and estimated fee calculations across EVMChain chains, also remove priority fee levels for Arbitrum --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
co-authored by
Omar Hatem
parent
569ee04449
commit
2da3ba5730
@@ -164,6 +164,7 @@ jobs:
|
||||
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 arbitrumTestWalletSeeds = '${{ secrets.ARBITRUM_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
|
||||
@@ -178,6 +179,7 @@ jobs:
|
||||
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 arbitrumTestWalletReceiveAddress = '${{ secrets.ARBITRUM_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
|
||||
|
||||
@@ -21,6 +21,7 @@ jobs:
|
||||
"cw_nano"
|
||||
"cw_polygon"
|
||||
"cw_base"
|
||||
"cw_arbitrum"
|
||||
"cw_solana"
|
||||
"cw_tron"
|
||||
"cw_wownero"
|
||||
|
||||
@@ -158,6 +158,7 @@ jobs:
|
||||
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 arbitrumTestWalletSeeds = '${{ secrets.ARBITRUM_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
|
||||
@@ -170,6 +171,7 @@ jobs:
|
||||
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 arbitrumTestWalletReceiveAddress = '${{ secrets.ARBITRUM_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
|
||||
|
||||
@@ -151,6 +151,7 @@ jobs:
|
||||
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 arbitrumTestWalletSeeds = '${{ secrets.ARBITRUM_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
|
||||
@@ -164,6 +165,7 @@ jobs:
|
||||
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 arbitrumTestWalletReceiveAddress = '${{ secrets.ARBITRUM_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
|
||||
|
||||
@@ -142,6 +142,7 @@ lib/zano/zano.dart
|
||||
lib/decred/decred.dart
|
||||
lib/dogecoin/dogecoin.dart
|
||||
lib/base/base.dart
|
||||
lib/arbitrum/arbitrum.dart
|
||||
|
||||
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x.png
|
||||
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x~ipad.png
|
||||
|
||||
@@ -26,6 +26,7 @@ Cake Wallet includes support for several cryptocurrencies, including:
|
||||
* Bitcoin Cash (BCH)
|
||||
* Polygon (POL)
|
||||
* Base (BASE)
|
||||
* Arbitrum (ARB)
|
||||
* Solana (SOL)
|
||||
* Tron (TRX)
|
||||
* Nano (XNO)
|
||||
|
||||
@@ -25,6 +25,7 @@ analyzer:
|
||||
lib/wownero/cw_wownero.dart,
|
||||
lib/zano/cw_zano.dart,
|
||||
lib/base/cw_base.dart,
|
||||
lib/arbitrum/cw_arbitrum.dart,
|
||||
]
|
||||
language:
|
||||
strict-casts: true
|
||||
|
||||
@@ -109,6 +109,9 @@
|
||||
<data android:scheme="base" />
|
||||
<data android:scheme="base-wallet" />
|
||||
<data android:scheme="base_wallet" />
|
||||
<data android:scheme="arbitrum" />
|
||||
<data android:scheme="arbitrum-wallet" />
|
||||
<data android:scheme="arbitrum_wallet" />
|
||||
</intent-filter>
|
||||
<!-- nano-gpt link scheme -->
|
||||
<intent-filter android:autoVerify="true">
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
-
|
||||
uri: arbitrum.nownodes.io
|
||||
is_default: true
|
||||
useSSL: true
|
||||
isEnabledForAutoSwitching: true
|
||||
-
|
||||
uri: arbitrum.drpc.org
|
||||
useSSL: true
|
||||
-
|
||||
uri: arbitrum-one-rpc.publicnode.com
|
||||
useSSL: true
|
||||
isEnabledForAutoSwitching: true
|
||||
@@ -0,0 +1,27 @@
|
||||
<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"/>
|
||||
<g clip-path="url(#clip0_10599_14372)">
|
||||
<g clip-path="url(#clip1_10599_14372)">
|
||||
<mask id="mask0_10599_14372" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="8" y="6" width="29" height="33">
|
||||
<path d="M36.6426 6.52493H8.0957V38.0249H36.6426V6.52493Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_10599_14372)">
|
||||
<path d="M24.7616 24.6685L23.1998 28.85C23.1538 28.966 23.1538 29.0948 23.1998 29.2108L25.8819 36.4077L28.9857 34.6552L25.2625 24.6685C25.1768 24.4366 24.8473 24.4366 24.7616 24.6685Z" fill="black"/>
|
||||
<path d="M27.8845 17.6328C27.7989 17.4009 27.4694 17.4009 27.3838 17.6328L25.822 21.8144C25.7758 21.9304 25.7758 22.0593 25.822 22.1752L30.2173 33.9596L33.3211 32.207L27.8845 17.6328Z" fill="black"/>
|
||||
<path d="M22.3691 8.47717C22.4482 8.47717 22.5207 8.4965 22.5932 8.53516L34.4284 15.2166C34.5668 15.2939 34.6525 15.4357 34.6525 15.5903V28.9531C34.6525 29.1078 34.5668 29.2495 34.4284 29.3268L22.5932 36.0147C22.5273 36.0533 22.4482 36.0727 22.3691 36.0727C22.2901 36.0727 22.2175 36.0533 22.1451 36.0147L10.3165 29.3333C10.1781 29.2559 10.0924 29.1142 10.0924 28.9596V15.5903C10.0924 15.4357 10.1781 15.2939 10.3165 15.2166L22.1517 8.53516C22.2175 8.4965 22.2967 8.47717 22.3691 8.47717ZM22.3691 6.52493C21.9474 6.52493 21.5256 6.63446 21.15 6.84708L9.31481 13.5285C8.56358 13.9538 8.0957 14.7398 8.0957 15.5903V28.9531C8.0957 29.8036 8.56358 30.5961 9.31481 31.0214L21.15 37.7027C21.5256 37.9154 21.9474 38.0249 22.3691 38.0249C22.7908 38.0249 23.2127 37.9154 23.5883 37.7027L35.4234 31.0214C36.1813 30.5961 36.6426 29.81 36.6426 28.9531V15.5903C36.6426 14.7398 36.1747 13.9473 35.4234 13.5221L23.5949 6.84708C23.2127 6.63446 22.7908 6.52493 22.3691 6.52493Z" fill="black"/>
|
||||
<path d="M14.5483 33.9725L15.6357 31.0603L17.8235 32.8386L15.7806 34.6683L14.5483 33.9725Z" fill="black"/>
|
||||
<path d="M21.374 14.6368H18.3757C18.1517 14.6368 17.9473 14.7721 17.8748 14.9783L11.4432 32.2199L14.547 33.9723L21.631 14.9848C21.6903 14.8172 21.5651 14.6368 21.374 14.6368Z" fill="black"/>
|
||||
<path d="M26.6255 14.6368H23.6272C23.4032 14.6368 23.1988 14.7721 23.1264 14.9783L15.7788 34.6682L18.8826 36.4208L26.876 14.9912C26.9418 14.8172 26.8101 14.6368 26.6255 14.6368Z" fill="black"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_10599_14372">
|
||||
<rect width="31.5" height="31.5" fill="white" transform="translate(6.52502 6.52499)"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip1_10599_14372">
|
||||
<rect width="28.5469" height="31.5" fill="white" transform="translate(8.1001 6.52496)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.0956 0C16.524 6.76387e-06 16.9526 0.111199 17.3409 0.327187L29.3571 7.10812C30.1203 7.54011 30.5955 8.34516 30.5956 9.20906V22.7841C30.5956 23.6545 30.127 24.4533 29.3571 24.8853L17.334 31.6725C16.9524 31.8885 16.524 32 16.0956 32C15.6672 32 15.2387 31.8885 14.8571 31.6725L2.83402 24.8853C2.07086 24.4533 1.59558 23.6481 1.59558 22.7841V9.20906C1.59562 8.34513 2.07089 7.54666 2.83402 7.11469L14.8571 0.327187C15.2387 0.111222 15.6672 0 16.0956 0ZM16.0956 1.98312C16.022 1.98312 15.9415 2.00291 15.8746 2.04219L3.85152 8.82969C3.71102 8.90822 3.62406 9.05205 3.62402 9.20906V22.7906C3.62402 22.9476 3.71095 23.0917 3.85152 23.1703L5.70027 24.2144L11.53 8.5875C11.6036 8.37808 11.8111 8.24063 12.0387 8.24062H15.0846C15.2787 8.24064 15.4058 8.42389 15.3456 8.59406L8.85339 25.9953L8.85496 25.9962L9.25527 24.9247L10.4159 25.8681L16.8646 8.5875C16.9382 8.37805 17.1461 8.24062 17.3737 8.24062H20.4196C20.607 8.24072 20.7409 8.42397 20.674 8.60062L13.2575 28.4831L15.8681 29.9578C15.9416 29.997 16.0153 30.0169 16.0956 30.0169C16.1759 30.0169 16.2562 29.997 16.3231 29.9578L18.9596 28.4675L16.9393 23.0459C16.8925 22.9281 16.8925 22.7972 16.9393 22.6794L18.5259 18.4316C18.6129 18.196 18.9475 18.196 19.0346 18.4316L22.1121 26.6862L23.3631 25.9794L19.6031 15.8987C19.5562 15.781 19.5562 15.65 19.6031 15.5322L21.1896 11.2841C21.2766 11.0485 21.6113 11.0486 21.6984 11.2841L26.5156 24.1978L28.3462 23.1637C28.4868 23.0852 28.5737 22.9412 28.5737 22.7841V9.20906C28.5737 9.05203 28.4868 8.90821 28.3462 8.82969L16.3231 2.04219C16.2495 2.00291 16.1759 1.98313 16.0956 1.98312Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
+2
-2
@@ -1,5 +1,5 @@
|
||||
@echo off
|
||||
set cw_win_app_config=--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --dogecoin --base
|
||||
set cw_win_app_config=--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --dogecoin --base --arbitrum
|
||||
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 cw_base.) 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 cw_arbitrum.) do (
|
||||
cd %%i
|
||||
call flutter pub get > nul
|
||||
call dart run build_runner build --delete-conflicting-outputs > nul
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
/pubspec.lock
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
build/
|
||||
@@ -0,0 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: "fcf2c11572af6f390246c056bc905eca609533a0"
|
||||
channel: "[user-branch]"
|
||||
|
||||
project_type: package
|
||||
@@ -0,0 +1,3 @@
|
||||
## 0.0.1
|
||||
|
||||
* TODO: Describe initial release.
|
||||
@@ -0,0 +1 @@
|
||||
TODO: Add your license here.
|
||||
@@ -0,0 +1,39 @@
|
||||
<!--
|
||||
This README describes the package. If you publish this package to pub.dev,
|
||||
this README's contents appear on the landing page for your package.
|
||||
|
||||
For information about how to write a good package README, see the guide for
|
||||
[writing package pages](https://dart.dev/tools/pub/writing-package-pages).
|
||||
|
||||
For general information about developing packages, see the Dart guide for
|
||||
[creating packages](https://dart.dev/guides/libraries/create-packages)
|
||||
and the Flutter guide for
|
||||
[developing packages and plugins](https://flutter.dev/to/develop-packages).
|
||||
-->
|
||||
|
||||
TODO: Put a short description of the package here that helps potential users
|
||||
know whether this package might be useful for them.
|
||||
|
||||
## Features
|
||||
|
||||
TODO: List what your package can do. Maybe include images, gifs, or videos.
|
||||
|
||||
## Getting started
|
||||
|
||||
TODO: List prerequisites and provide or point to information on how to
|
||||
start using the package.
|
||||
|
||||
## Usage
|
||||
|
||||
TODO: Include short and useful examples for package users. Add longer examples
|
||||
to `/example` folder.
|
||||
|
||||
```dart
|
||||
const like = 'sample';
|
||||
```
|
||||
|
||||
## Additional information
|
||||
|
||||
TODO: Tell users more about the package: where to find more information, how to
|
||||
contribute to the package, how to file issues, what response they can expect
|
||||
from the package authors, and more.
|
||||
@@ -0,0 +1,4 @@
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
@@ -0,0 +1,5 @@
|
||||
class ArbitrumMnemonicIsIncorrectException implements Exception {
|
||||
@override
|
||||
String toString() =>
|
||||
'Arbitrum mnemonic has incorrect format. Mnemonic should contain 12 or 24 words separated by space.';
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:cw_evm/.secrets.g.dart' as secrets;
|
||||
import 'package:cw_evm/evm_chain_client.dart';
|
||||
import 'package:cw_evm/evm_chain_transaction_model.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:web3dart/web3dart.dart';
|
||||
|
||||
class ArbitrumClient 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 => 42161;
|
||||
|
||||
@override
|
||||
Future<List<EVMChainTransactionModel>> fetchTransactions(
|
||||
String address, {
|
||||
String? contractAddress,
|
||||
}) async {
|
||||
try {
|
||||
final response = await client.get(
|
||||
Uri.https("api.etherscan.io", "/v2/api", {
|
||||
"chainid": "$chainId",
|
||||
"module": "account",
|
||||
"action": contractAddress != null ? "tokentx" : "txlist",
|
||||
if (contractAddress != null) "contractaddress": contractAddress,
|
||||
"address": address,
|
||||
"apikey": secrets.etherScanApiKey,
|
||||
}),
|
||||
);
|
||||
|
||||
final jsonResponse = json.decode(response.body) as Map<String, dynamic>;
|
||||
|
||||
if (response.statusCode >= 200 && response.statusCode < 300 && jsonResponse['status'] != 0) {
|
||||
final res = (jsonResponse['result'] as List);
|
||||
|
||||
res.removeWhere((e) => e['value'] == '0');
|
||||
|
||||
return res
|
||||
.map((e) => EVMChainTransactionModel.fromJson(e as Map<String, dynamic>, 'ETH'))
|
||||
.toList();
|
||||
}
|
||||
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<EVMChainTransactionModel>> fetchInternalTransactions(String address) async {
|
||||
try {
|
||||
final response = await client.get(
|
||||
Uri.https("api.etherscan.io", "/v2/api", {
|
||||
"chainid": "$chainId",
|
||||
"module": "account",
|
||||
"action": "txlistinternal",
|
||||
"address": address,
|
||||
"apikey": secrets.etherScanApiKey,
|
||||
}),
|
||||
);
|
||||
|
||||
final jsonResponse = json.decode(response.body) as Map<String, dynamic>;
|
||||
|
||||
if (response.statusCode >= 200 && response.statusCode < 300 && jsonResponse['status'] != 0) {
|
||||
return (jsonResponse['result'] as List)
|
||||
.map((e) => EVMChainTransactionModel.fromJson(e as Map<String, dynamic>, 'ETH'))
|
||||
.toList();
|
||||
}
|
||||
|
||||
return [];
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,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_arbitrum/arbitrum_transaction_info.dart';
|
||||
|
||||
class ArbitrumTransactionHistory extends EVMChainTransactionHistory {
|
||||
ArbitrumTransactionHistory({
|
||||
required super.walletInfo,
|
||||
required super.password,
|
||||
required super.encryptionFileUtils,
|
||||
});
|
||||
|
||||
@override
|
||||
String getTransactionHistoryFileName() => 'arbitrum_transactions.json';
|
||||
|
||||
@override
|
||||
EVMChainTransactionInfo getTransactionInfo(Map<String, dynamic> val) =>
|
||||
ArbitrumTransactionInfo.fromJson(val);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_evm/evm_chain_transaction_info.dart';
|
||||
|
||||
class ArbitrumTransactionInfo extends EVMChainTransactionInfo {
|
||||
ArbitrumTransactionInfo({
|
||||
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 ArbitrumTransactionInfo.fromJson(Map<String, dynamic> data) {
|
||||
return ArbitrumTransactionInfo(
|
||||
id: data['id'] as String,
|
||||
height: data['height'] as int,
|
||||
ethAmount: BigInt.parse(data['amount']),
|
||||
exponent: data['exponent'] as int,
|
||||
ethFee: BigInt.parse(data['fee']),
|
||||
direction: parseTransactionDirectionFromInt(data['direction'] as int),
|
||||
date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
|
||||
isPending: data['isPending'] as bool,
|
||||
confirmations: data['confirmations'] as int,
|
||||
tokenSymbol: data['tokenSymbol'] as String,
|
||||
to: data['to'],
|
||||
from: data['from'],
|
||||
contractAddress: data['contractAddress'],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String get feeCurrency => 'ETH';
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:cw_arbitrum/arbitrum_client.dart';
|
||||
import 'package:cw_arbitrum/arbitrum_transaction_history.dart';
|
||||
import 'package:cw_arbitrum/arbitrum_transaction_info.dart';
|
||||
import 'package:cw_arbitrum/default_arbitrum_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 ArbitrumWallet extends EVMChainWallet {
|
||||
ArbitrumWallet({
|
||||
required super.walletInfo,
|
||||
required super.password,
|
||||
required super.derivationInfo,
|
||||
super.mnemonic,
|
||||
super.initialBalance,
|
||||
super.privateKey,
|
||||
required super.client,
|
||||
required super.encryptionFileUtils,
|
||||
super.passphrase,
|
||||
}) : super(nativeCurrency: CryptoCurrency.arbEth);
|
||||
|
||||
@override
|
||||
bool get hasPriorityFee => false;
|
||||
|
||||
@override
|
||||
Future<void> initErc20TokensBox() async {
|
||||
final boxName = "${walletInfo.name.replaceAll(" ", "_")}_${Erc20Token.arbitrumBoxName}";
|
||||
|
||||
evmChainErc20TokensBox = await CakeHive.openBox<Erc20Token>(boxName);
|
||||
}
|
||||
|
||||
@override
|
||||
void addInitialTokens() {
|
||||
final initialErc20Tokens = DefaultArbitrumErc20Tokens().initialArbitrumErc20Tokens;
|
||||
|
||||
for (final token in initialErc20Tokens) {
|
||||
if (!evmChainErc20TokensBox.containsKey(token.contractAddress)) {
|
||||
evmChainErc20TokensBox.put(token.contractAddress, token);
|
||||
} else {
|
||||
// update existing token
|
||||
final existingToken = evmChainErc20TokensBox.get(token.contractAddress);
|
||||
evmChainErc20TokensBox.put(
|
||||
token.contractAddress,
|
||||
Erc20Token.copyWith(token, enabled: existingToken!.enabled),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
List<String> get getDefaultTokenContractAddresses => DefaultArbitrumErc20Tokens()
|
||||
.initialArbitrumErc20Tokens
|
||||
.map((e) => e.contractAddress)
|
||||
.toList();
|
||||
|
||||
@override
|
||||
Future<bool> checkIfScanProviderIsEnabled() async {
|
||||
return (await sharedPrefs.future).getBool("use_arbiscan") ?? true;
|
||||
}
|
||||
|
||||
@override
|
||||
String getTransactionHistoryFileName() => 'arbitrum_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 = ArbitrumTransactionInfo(
|
||||
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 ArbitrumTransactionHistory(
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
encryptionFileUtils: encryptionFileUtils,
|
||||
);
|
||||
}
|
||||
|
||||
static Future<ArbitrumWallet> 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 ArbitrumWallet(
|
||||
walletInfo: walletInfo,
|
||||
derivationInfo: await walletInfo.getDerivationInfo(),
|
||||
password: password,
|
||||
mnemonic: keysData.mnemonic,
|
||||
privateKey: keysData.privateKey,
|
||||
passphrase: keysData.passphrase,
|
||||
initialBalance: balance,
|
||||
client: ArbitrumClient(),
|
||||
encryptionFileUtils: encryptionFileUtils,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:cw_arbitrum/arbirtrum_mnemonics_exception.dart';
|
||||
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_arbitrum/arbitrum_wallet.dart';
|
||||
import 'package:cw_arbitrum/arbitrum_client.dart';
|
||||
|
||||
class ArbitrumWalletService extends EVMChainWalletService<ArbitrumWallet> {
|
||||
ArbitrumWalletService(super.isDirect, {required this.client});
|
||||
|
||||
late ArbitrumClient client;
|
||||
|
||||
@override
|
||||
WalletType getType() => WalletType.arbitrum;
|
||||
|
||||
@override
|
||||
Future<ArbitrumWallet> create(EVMChainNewWalletCredentials credentials, {bool? isTestnet}) async {
|
||||
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
|
||||
|
||||
final mnemonic = credentials.mnemonic ?? bip39.generateMnemonic(strength: strength);
|
||||
|
||||
final wallet = ArbitrumWallet(
|
||||
walletInfo: credentials.walletInfo!,
|
||||
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
|
||||
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<ArbitrumWallet> openWallet(String name, String password) async {
|
||||
final walletInfo = await WalletInfo.get(name, getType());
|
||||
if (walletInfo == null) {
|
||||
throw Exception('Wallet not found');
|
||||
}
|
||||
|
||||
try {
|
||||
final wallet = await ArbitrumWallet.open(
|
||||
name: name,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
||||
);
|
||||
|
||||
await wallet.init();
|
||||
wallet.addInitialTokens();
|
||||
await wallet.save();
|
||||
saveBackup(name);
|
||||
return wallet;
|
||||
} catch (_) {
|
||||
await restoreWalletFilesFromBackup(name);
|
||||
|
||||
final wallet = await ArbitrumWallet.open(
|
||||
name: name,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
||||
);
|
||||
|
||||
await wallet.init();
|
||||
wallet.addInitialTokens();
|
||||
await wallet.save();
|
||||
return wallet;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ArbitrumWallet> restoreFromKeys(
|
||||
EVMChainRestoreWalletFromPrivateKey credentials, {
|
||||
bool? isTestnet,
|
||||
}) async {
|
||||
final wallet = ArbitrumWallet(
|
||||
password: credentials.password!,
|
||||
privateKey: credentials.privateKey,
|
||||
walletInfo: credentials.walletInfo!,
|
||||
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
|
||||
client: client,
|
||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
||||
);
|
||||
|
||||
await wallet.init();
|
||||
wallet.addInitialTokens();
|
||||
await wallet.save();
|
||||
return wallet;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ArbitrumWallet> restoreFromHardwareWallet(
|
||||
EVMChainRestoreWalletFromHardware credentials,
|
||||
) async {
|
||||
final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
|
||||
derivationInfo.derivationType = DerivationType.bip39;
|
||||
derivationInfo.derivationPath = "m/44'/60'/${credentials.hwAccountData.accountIndex}'/0/0";
|
||||
await derivationInfo.save();
|
||||
credentials.walletInfo!.hardwareWalletType = credentials.hardwareWalletType;
|
||||
credentials.walletInfo!.address = credentials.hwAccountData.address;
|
||||
await credentials.walletInfo!.save();
|
||||
|
||||
final wallet = ArbitrumWallet(
|
||||
walletInfo: credentials.walletInfo!,
|
||||
derivationInfo: derivationInfo,
|
||||
password: credentials.password!,
|
||||
client: client,
|
||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
||||
);
|
||||
|
||||
await wallet.init();
|
||||
wallet.addInitialTokens();
|
||||
await wallet.save();
|
||||
|
||||
return wallet;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ArbitrumWallet> restoreFromSeed(
|
||||
EVMChainRestoreWalletFromSeedCredentials credentials, {
|
||||
bool? isTestnet,
|
||||
}) async {
|
||||
if (!bip39.validateMnemonic(credentials.mnemonic)) {
|
||||
throw ArbitrumMnemonicIsIncorrectException();
|
||||
}
|
||||
|
||||
final wallet = ArbitrumWallet(
|
||||
password: credentials.password!,
|
||||
mnemonic: credentials.mnemonic,
|
||||
walletInfo: credentials.walletInfo!,
|
||||
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
|
||||
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 = await WalletInfo.get(currentName, getType());
|
||||
if (currentWalletInfo == null) {
|
||||
throw Exception('Wallet not found');
|
||||
}
|
||||
final currentWallet = await ArbitrumWallet.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;
|
||||
|
||||
newWalletInfo.save();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/// A Calculator.
|
||||
class Calculator {
|
||||
/// Returns [value] plus 1.
|
||||
int addOne(int value) => value + 1;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
|
||||
class DefaultArbitrumErc20Tokens {
|
||||
final List<Erc20Token> _defaultTokens = [
|
||||
Erc20Token(
|
||||
name: "USD Coin",
|
||||
symbol: "USDC",
|
||||
contractAddress: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
|
||||
decimal: 6,
|
||||
enabled: true,
|
||||
),
|
||||
Erc20Token(
|
||||
name: "USDC.e",
|
||||
symbol: "USDC.e",
|
||||
contractAddress: "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8",
|
||||
decimal: 6,
|
||||
enabled: true,
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Wrapped BTC",
|
||||
symbol: "WBTC",
|
||||
contractAddress: "0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f",
|
||||
decimal: 8,
|
||||
enabled: true,
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Chainlink Token",
|
||||
symbol: "LINK",
|
||||
contractAddress: "0xf97f4df75117a78c1A5a0DBb814Af92458539FB4",
|
||||
decimal: 18,
|
||||
enabled: true,
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Wrapped liquid staked Ether 2.0",
|
||||
symbol: "wstETH",
|
||||
contractAddress: "0x0fBcbaEA96Ce0cF7Ee00A8c19c3ab6f5Dc8E1921",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
),
|
||||
Erc20Token(
|
||||
name: "Wrapped Ether",
|
||||
symbol: "WETH",
|
||||
contractAddress: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
),
|
||||
Erc20Token(
|
||||
name: "DAI",
|
||||
symbol: "DAI",
|
||||
contractAddress: "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
),
|
||||
];
|
||||
|
||||
List<Erc20Token> get initialArbitrumErc20Tokens => _defaultTokens.map((token) {
|
||||
String? iconPath;
|
||||
if (token.iconPath?.isEmpty ?? true) {
|
||||
try {
|
||||
iconPath = CryptoCurrency.all
|
||||
.firstWhere((element) => element.title.toUpperCase() == token.symbol.toUpperCase())
|
||||
.iconPath;
|
||||
} catch (_) {}
|
||||
} else {
|
||||
iconPath = token.iconPath;
|
||||
}
|
||||
|
||||
return Erc20Token.copyWith(token, icon: iconPath, tag: 'ARB');
|
||||
}).toList();
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
name: cw_arbitrum
|
||||
description: "A new Flutter package project."
|
||||
version: 0.0.1
|
||||
publish_to: none
|
||||
homepage: https://cakewallet.com
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.6 <4.0.0'
|
||||
flutter: ">=1.17.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
cw_core:
|
||||
path: ../cw_core
|
||||
cw_ethereum:
|
||||
path: ../cw_ethereum
|
||||
cw_evm:
|
||||
path: ../cw_evm
|
||||
web3dart: ^2.7.1
|
||||
hive: ^2.2.3
|
||||
bip39: ^1.0.6
|
||||
collection: ^1.17.1
|
||||
|
||||
dependency_overrides:
|
||||
web3dart:
|
||||
git:
|
||||
url: https://github.com/cake-tech/web3dart.git
|
||||
ref: cake
|
||||
watcher: ^1.1.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^2.0.0
|
||||
build_runner: ^2.4.15
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
# The following section is specific to Flutter packages.
|
||||
flutter:
|
||||
|
||||
# To add assets to your package, add an assets section, like this:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
#
|
||||
# For details regarding assets in packages, see
|
||||
# https://flutter.dev/to/asset-from-package
|
||||
#
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/to/resolution-aware-images
|
||||
|
||||
# To add custom fonts to your package, add a fonts section here,
|
||||
# in this "flutter" section. Each entry in this list should have a
|
||||
# "family" key with the font family name, and a "fonts" key with a
|
||||
# list giving the asset and other descriptors for the font. For
|
||||
# example:
|
||||
# fonts:
|
||||
# - family: Schyler
|
||||
# fonts:
|
||||
# - asset: fonts/Schyler-Regular.ttf
|
||||
# - asset: fonts/Schyler-Italic.ttf
|
||||
# style: italic
|
||||
# - family: Trajan Pro
|
||||
# fonts:
|
||||
# - asset: fonts/TrajanPro.ttf
|
||||
# - asset: fonts/TrajanPro_Bold.ttf
|
||||
# weight: 700
|
||||
#
|
||||
# For details regarding fonts in packages, see
|
||||
# https://flutter.dev/to/font-from-package
|
||||
@@ -0,0 +1,12 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:cw_arbitrum/cw_arbitrum.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);
|
||||
});
|
||||
}
|
||||
@@ -45,9 +45,13 @@ class BaseWallet extends EVMChainWallet {
|
||||
for (final token in initialErc20Tokens) {
|
||||
if (!evmChainErc20TokensBox.containsKey(token.contractAddress)) {
|
||||
evmChainErc20TokensBox.put(token.contractAddress, token);
|
||||
} else { // update existing token
|
||||
} else {
|
||||
// update existing token
|
||||
final existingToken = evmChainErc20TokensBox.get(token.contractAddress);
|
||||
evmChainErc20TokensBox.put(token.contractAddress, Erc20Token.copyWith(token, enabled: existingToken!.enabled));
|
||||
evmChainErc20TokensBox.put(
|
||||
token.contractAddress,
|
||||
Erc20Token.copyWith(token, enabled: existingToken!.enabled),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,7 +62,7 @@ class BaseWallet extends EVMChainWallet {
|
||||
|
||||
@override
|
||||
Future<bool> checkIfScanProviderIsEnabled() async {
|
||||
return (await sharedPrefs.future).getBool("use_basescan") ?? true;
|
||||
return (await sharedPrefs.future).getBool("use_basescan") ?? true;
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -117,6 +117,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
|
||||
CryptoCurrency.deps,
|
||||
CryptoCurrency.baseEth,
|
||||
CryptoCurrency.usde,
|
||||
CryptoCurrency.arbEth,
|
||||
];
|
||||
|
||||
static const havenCurrencies = [
|
||||
@@ -245,6 +246,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
|
||||
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/crypto/base_icon.webp', decimals: 18);
|
||||
static const usde = CryptoCurrency(title: 'USDE', tag: 'BASE', fullName: 'Ethena USDE', raw: 105, name: 'usde', iconPath: 'assets/images/crypto/ethena-usde-logo.png', decimals: 18);
|
||||
static const arbEth = CryptoCurrency(title: 'ETH', tag: 'ARB', fullName: 'Arbitrum', raw: 106, name: 'arbeth', iconPath: 'assets/images/crypto/arbitrum.webp', decimals: 18);
|
||||
|
||||
static final Map<int, CryptoCurrency> _rawCurrencyMap =
|
||||
[...all, ...havenCurrencies].fold<Map<int, CryptoCurrency>>(<int, CryptoCurrency>{}, (acc, item) {
|
||||
|
||||
@@ -18,6 +18,8 @@ CryptoCurrency walletTypeToCryptoCurrency(WalletType type, {bool isTestnet = fal
|
||||
return CryptoCurrency.eth;
|
||||
case WalletType.base:
|
||||
return CryptoCurrency.baseEth;
|
||||
case WalletType.arbitrum:
|
||||
return CryptoCurrency.arbEth;
|
||||
case WalletType.bitcoinCash:
|
||||
return CryptoCurrency.bch;
|
||||
case WalletType.nano:
|
||||
|
||||
@@ -71,6 +71,7 @@ class Erc20Token extends CryptoCurrency with HiveObjectMixin {
|
||||
static const ethereumBoxName = 'EthereumErc20Tokens';
|
||||
static const polygonBoxName = 'PolygonErc20Tokens';
|
||||
static const baseBoxName = 'BaseErc20Tokens';
|
||||
static const arbitrumBoxName = 'ArbitrumErc20Tokens';
|
||||
|
||||
@override
|
||||
bool operator ==(other) =>
|
||||
|
||||
@@ -107,6 +107,7 @@ class Node extends HiveObject with Keyable {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.arbitrum:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
case WalletType.zano:
|
||||
@@ -171,6 +172,7 @@ class Node extends HiveObject with Keyable {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.arbitrum:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
case WalletType.dogecoin:
|
||||
|
||||
@@ -86,6 +86,8 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
|
||||
|
||||
int calculateEstimatedFee(TransactionPriority priority, int? amount);
|
||||
|
||||
Future<void> updateEstimatedFeesParams(TransactionPriority priority) async {}
|
||||
|
||||
// void fetchTransactionsAsync(
|
||||
// void Function(TransactionType transaction) onTransactionLoaded,
|
||||
// {void Function() onFinished});
|
||||
|
||||
@@ -20,6 +20,7 @@ const walletTypes = [
|
||||
WalletType.decred,
|
||||
WalletType.dogecoin,
|
||||
WalletType.base,
|
||||
WalletType.arbitrum,
|
||||
];
|
||||
|
||||
@HiveType(typeId: WALLET_TYPE_TYPE_ID)
|
||||
@@ -74,6 +75,9 @@ enum WalletType {
|
||||
|
||||
@HiveField(16)
|
||||
base,
|
||||
|
||||
@HiveField(17)
|
||||
arbitrum,
|
||||
}
|
||||
|
||||
int serializeToInt(WalletType type) {
|
||||
@@ -110,6 +114,8 @@ int serializeToInt(WalletType type) {
|
||||
return 14;
|
||||
case WalletType.base:
|
||||
return 15;
|
||||
case WalletType.arbitrum:
|
||||
return 16;
|
||||
case WalletType.none:
|
||||
return -1;
|
||||
}
|
||||
@@ -149,6 +155,8 @@ WalletType deserializeFromInt(int raw) {
|
||||
return WalletType.dogecoin;
|
||||
case 15:
|
||||
return WalletType.base;
|
||||
case 16:
|
||||
return WalletType.arbitrum;
|
||||
default:
|
||||
throw Exception('Unexpected token: $raw for WalletType deserializeFromInt');
|
||||
}
|
||||
@@ -188,6 +196,8 @@ String walletTypeToString(WalletType type) {
|
||||
return 'Dogecoin';
|
||||
case WalletType.base:
|
||||
return 'Base';
|
||||
case WalletType.arbitrum:
|
||||
return 'Arbitrum';
|
||||
case WalletType.none:
|
||||
return '';
|
||||
}
|
||||
@@ -227,6 +237,8 @@ String walletTypeToDisplayName(WalletType type) {
|
||||
return 'Dogecoin (DOGE)';
|
||||
case WalletType.base:
|
||||
return 'Base (BASE)';
|
||||
case WalletType.arbitrum:
|
||||
return 'Arbitrum (ARB)';
|
||||
case WalletType.none:
|
||||
return '';
|
||||
}
|
||||
@@ -266,6 +278,8 @@ WalletType? cryptoCurrencyToWalletType(CryptoCurrency type) {
|
||||
return WalletType.dogecoin;
|
||||
case CryptoCurrency.baseEth:
|
||||
return WalletType.base;
|
||||
case CryptoCurrency.arbEth:
|
||||
return WalletType.arbitrum;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ abstract class EVMChainClient {
|
||||
required BigInt gasFee,
|
||||
required int estimatedGasUnits,
|
||||
required int maxFeePerGas,
|
||||
required EVMChainTransactionPriority priority,
|
||||
required EVMChainTransactionPriority? priority,
|
||||
required CryptoCurrency currency,
|
||||
required String feeCurrency,
|
||||
required int exponent,
|
||||
@@ -172,15 +172,20 @@ abstract class EVMChainClient {
|
||||
int? gasPrice,
|
||||
}) async {
|
||||
assert(currency == CryptoCurrency.eth ||
|
||||
currency == CryptoCurrency.maticpoly || currency == CryptoCurrency.baseEth ||
|
||||
currency == CryptoCurrency.maticpoly ||
|
||||
currency == CryptoCurrency.baseEth ||
|
||||
currency == CryptoCurrency.arbEth ||
|
||||
contractAddress != null);
|
||||
|
||||
bool isNativeToken = currency == CryptoCurrency.eth || currency == CryptoCurrency.maticpoly || currency == CryptoCurrency.baseEth;
|
||||
bool isNativeToken = currency == CryptoCurrency.eth ||
|
||||
currency == CryptoCurrency.maticpoly ||
|
||||
currency == CryptoCurrency.baseEth ||
|
||||
currency == CryptoCurrency.arbEth;
|
||||
|
||||
final Transaction transaction = createTransaction(
|
||||
from: privateKey.address,
|
||||
to: EthereumAddress.fromHex(toAddress),
|
||||
maxPriorityFeePerGas: EtherAmount.fromInt(EtherUnit.gwei, priority.tip),
|
||||
maxPriorityFeePerGas: priority != null ? EtherAmount.fromInt(EtherUnit.gwei, priority.tip) : null,
|
||||
amount: isNativeToken ? EtherAmount.inWei(amount) : EtherAmount.zero(),
|
||||
data: data != null ? hexToBytes(data) : null,
|
||||
maxGas: estimatedGasUnits,
|
||||
@@ -229,7 +234,7 @@ abstract class EVMChainClient {
|
||||
required String feeCurrency,
|
||||
required int estimatedGasUnits,
|
||||
required int maxFeePerGas,
|
||||
required EVMChainTransactionPriority priority,
|
||||
required EVMChainTransactionPriority? priority,
|
||||
required int exponent,
|
||||
required String contractAddress,
|
||||
int? gasPrice,
|
||||
@@ -237,7 +242,7 @@ abstract class EVMChainClient {
|
||||
final Transaction transaction = createTransaction(
|
||||
from: privateKey.address,
|
||||
to: EthereumAddress.fromHex(contractAddress),
|
||||
maxPriorityFeePerGas: EtherAmount.fromInt(EtherUnit.gwei, priority.tip),
|
||||
maxPriorityFeePerGas:priority != null ? EtherAmount.fromInt(EtherUnit.gwei, priority.tip) : null,
|
||||
amount: EtherAmount.zero(),
|
||||
maxGas: estimatedGasUnits,
|
||||
maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
|
||||
|
||||
+183
-151
@@ -115,11 +115,13 @@ abstract class EVMChainWalletBase
|
||||
|
||||
late final EVMChainClient _client;
|
||||
|
||||
int gasPrice = 0;
|
||||
int? gasBaseFee = 0;
|
||||
int estimatedGasUnits = 0;
|
||||
bool hasPriorityFee = true;
|
||||
|
||||
Timer? _updateFeesTimer;
|
||||
@observable
|
||||
String? nativeTxEstimatedFee;
|
||||
|
||||
@observable
|
||||
String? erc20TxEstimatedFee;
|
||||
|
||||
bool _isTransactionUpdating;
|
||||
|
||||
@@ -168,6 +170,16 @@ abstract class EVMChainWalletBase
|
||||
EncryptionFileUtils encryptionFileUtils,
|
||||
);
|
||||
|
||||
String _getUSDCContractAddress() {
|
||||
return switch (_client.chainId) {
|
||||
1 => "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
|
||||
137 => "0x2791bca1f2de4661ed88a30c99a7a9449aa84174",
|
||||
8453 => "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
|
||||
42161 => "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
|
||||
_ => throw Exception("Unsupported chain ID: ${_client.chainId}"),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> checkNodeHealth() async {
|
||||
try {
|
||||
@@ -175,26 +187,9 @@ abstract class EVMChainWalletBase
|
||||
await _client.getBalance(_evmChainPrivateKey.address, throwOnError: true);
|
||||
|
||||
// Check USDC token balance
|
||||
String usdcContractAddress;
|
||||
String usdcContractAddress = _getUSDCContractAddress();
|
||||
|
||||
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,
|
||||
);
|
||||
await _client.fetchERC20Balances(_evmChainPrivateKey.address, usdcContractAddress);
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
@@ -279,30 +274,77 @@ abstract class EVMChainWalletBase
|
||||
}
|
||||
|
||||
@override
|
||||
int calculateEstimatedFee(TransactionPriority priority, int? amount) {
|
||||
{
|
||||
try {
|
||||
int calculateEstimatedFee(TransactionPriority priority, int? amount) => 0;
|
||||
|
||||
@override
|
||||
Future<void> updateEstimatedFeesParams(TransactionPriority priority) async =>
|
||||
await _getEstimatedFees(priority);
|
||||
|
||||
Future<void> _getEstimatedFees(TransactionPriority? priority) async {
|
||||
final nativeFee = await _getNativeTxFee(priority);
|
||||
nativeTxEstimatedFee = nativeFee.toString();
|
||||
|
||||
final erc20Fee = await _getErc20TxFee(priority);
|
||||
erc20TxEstimatedFee = erc20Fee.toString();
|
||||
|
||||
printV('Native Estimated Fee: $nativeTxEstimatedFee');
|
||||
printV('ERC20 Estimated Fee: $erc20TxEstimatedFee');
|
||||
}
|
||||
|
||||
Future<int> _getNativeTxFee(TransactionPriority? priority) async {
|
||||
try {
|
||||
int priorityFee = 0;
|
||||
if (hasPriorityFee) {
|
||||
if (priority is EVMChainTransactionPriority) {
|
||||
final priorityFee = EtherAmount.fromInt(EtherUnit.gwei, priority.tip).getInWei.toInt();
|
||||
|
||||
int maxFeePerGas;
|
||||
if (gasBaseFee != null) {
|
||||
// MaxFeePerGas with EIP1559;
|
||||
maxFeePerGas = gasBaseFee! + priorityFee;
|
||||
} else {
|
||||
// MaxFeePerGas with gasPrice;
|
||||
maxFeePerGas = gasPrice;
|
||||
printV('MaxFeePerGas with gasPrice: $maxFeePerGas');
|
||||
}
|
||||
|
||||
final totalGasFee = estimatedGasUnits * maxFeePerGas;
|
||||
return totalGasFee;
|
||||
priorityFee = EtherAmount.fromInt(EtherUnit.gwei, priority.tip).getInWei.toInt();
|
||||
}
|
||||
|
||||
return 0;
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
final gasPrice = await _client.getGasUnitPrice();
|
||||
final gasBaseFee = await _client.getGasBaseFee();
|
||||
|
||||
final gasUnits = await _client.getEstimatedGasUnitsForTransaction(
|
||||
senderAddress: evmChainPrivateKey.address,
|
||||
toAddress: evmChainPrivateKey.address,
|
||||
gasPrice: EtherAmount.fromInt(EtherUnit.wei, gasPrice),
|
||||
value: EtherAmount.fromBigInt(EtherUnit.wei, BigInt.from(0.0000000001)),
|
||||
);
|
||||
|
||||
int maxFeePerGas = gasBaseFee != null ? (gasBaseFee + priorityFee) : (gasPrice + priorityFee);
|
||||
final totalGasFee = gasUnits * maxFeePerGas;
|
||||
return totalGasFee;
|
||||
} catch (e) {
|
||||
printV(e.toString());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Future<int> _getErc20TxFee(TransactionPriority? priority) async {
|
||||
try {
|
||||
int priorityFee = 0;
|
||||
if (hasPriorityFee) {
|
||||
if (priority is EVMChainTransactionPriority) {
|
||||
priorityFee = EtherAmount.fromInt(EtherUnit.gwei, priority.tip).getInWei.toInt();
|
||||
}
|
||||
}
|
||||
|
||||
final gasPrice = await _client.getGasUnitPrice();
|
||||
final gasBaseFee = await _client.getGasBaseFee();
|
||||
|
||||
final gasUnits = await _client.getEstimatedGasUnitsForTransaction(
|
||||
senderAddress: evmChainPrivateKey.address,
|
||||
toAddress: evmChainPrivateKey.address,
|
||||
contractAddress: _getUSDCContractAddress(), // Using USDC for default estimation
|
||||
gasPrice: EtherAmount.fromInt(EtherUnit.wei, gasPrice),
|
||||
value: EtherAmount.fromBigInt(EtherUnit.wei, BigInt.from(0.0000000001)),
|
||||
);
|
||||
|
||||
int maxFeePerGas = gasBaseFee != null ? (gasBaseFee + priorityFee) : (gasPrice + priorityFee);
|
||||
final totalGasFee = gasUnits * maxFeePerGas;
|
||||
return totalGasFee;
|
||||
} catch (e) {
|
||||
printV(e.toString());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,76 +358,81 @@ abstract class EVMChainWalletBase
|
||||
required amount,
|
||||
required String? contractAddress,
|
||||
required String receivingAddressHex,
|
||||
required TransactionPriority priority,
|
||||
required TransactionPriority? priority,
|
||||
Uint8List? data,
|
||||
}) async {
|
||||
try {
|
||||
if (priority is EVMChainTransactionPriority) {
|
||||
final priorityFee = EtherAmount.fromInt(EtherUnit.gwei, priority.tip).getInWei.toInt();
|
||||
|
||||
int maxFeePerGas;
|
||||
int adjustedGasPrice;
|
||||
|
||||
bool isPolygon = _client.chainId == 137;
|
||||
|
||||
if (gasBaseFee != null) {
|
||||
// MaxFeePerGas with EIP1559;
|
||||
maxFeePerGas = gasBaseFee! + priorityFee;
|
||||
} else {
|
||||
// MaxFeePerGas with gasPrice
|
||||
maxFeePerGas = gasPrice + priorityFee;
|
||||
int priorityFee = 0;
|
||||
if (hasPriorityFee && priority != null) {
|
||||
if (priority is EVMChainTransactionPriority) {
|
||||
priorityFee = EtherAmount.fromInt(EtherUnit.gwei, priority.tip).getInWei.toInt();
|
||||
}
|
||||
|
||||
adjustedGasPrice = maxFeePerGas;
|
||||
|
||||
// Polygon has a minimum priority fee of 25 gwei
|
||||
if (isPolygon) {
|
||||
int minPriorityFee = 25;
|
||||
int minPriorityFeeWei =
|
||||
EtherAmount.fromInt(EtherUnit.gwei, minPriorityFee).getInWei.toInt();
|
||||
|
||||
// Calculate user selected priority-based additional fee on top of minimum
|
||||
int additionalPriorityFee = 0;
|
||||
switch (priority) {
|
||||
case EVMChainTransactionPriority.slow:
|
||||
// We use minimum priority fee only
|
||||
additionalPriorityFee = 0;
|
||||
break;
|
||||
case EVMChainTransactionPriority.medium:
|
||||
// We add 15 gwei on top of minimum
|
||||
additionalPriorityFee = EtherAmount.fromInt(EtherUnit.gwei, 15).getInWei.toInt();
|
||||
break;
|
||||
case EVMChainTransactionPriority.fast:
|
||||
// We add 35 gwei on top of minimum
|
||||
additionalPriorityFee = EtherAmount.fromInt(EtherUnit.gwei, 35).getInWei.toInt();
|
||||
break;
|
||||
}
|
||||
|
||||
int totalPriorityFee = minPriorityFeeWei + additionalPriorityFee;
|
||||
adjustedGasPrice = gasPrice + totalPriorityFee;
|
||||
maxFeePerGas = gasPrice + totalPriorityFee;
|
||||
}
|
||||
|
||||
final estimatedGas = await _client.getEstimatedGasUnitsForTransaction(
|
||||
contractAddress: contractAddress,
|
||||
senderAddress: _evmChainPrivateKey.address,
|
||||
value: EtherAmount.fromBigInt(EtherUnit.wei, amount!),
|
||||
gasPrice: EtherAmount.fromInt(EtherUnit.wei, adjustedGasPrice),
|
||||
toAddress: EthereumAddress.fromHex(receivingAddressHex),
|
||||
maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
|
||||
data: data,
|
||||
);
|
||||
|
||||
final totalGasFee = estimatedGas * adjustedGasPrice;
|
||||
|
||||
return GasParamsHandler(
|
||||
estimatedGasUnits: estimatedGas,
|
||||
estimatedGasFee: totalGasFee,
|
||||
maxFeePerGas: maxFeePerGas,
|
||||
gasPrice: adjustedGasPrice,
|
||||
);
|
||||
}
|
||||
return GasParamsHandler.zero();
|
||||
|
||||
final gasBaseFee = await _client.getGasBaseFee();
|
||||
final gasPrice = await _client.getGasUnitPrice();
|
||||
|
||||
int maxFeePerGas;
|
||||
int adjustedGasPrice;
|
||||
|
||||
bool isPolygon = _client.chainId == 137;
|
||||
|
||||
if (gasBaseFee != null) {
|
||||
// MaxFeePerGas with EIP1559;
|
||||
maxFeePerGas = gasBaseFee + priorityFee;
|
||||
} else {
|
||||
// MaxFeePerGas with gasPrice
|
||||
maxFeePerGas = gasPrice + priorityFee;
|
||||
}
|
||||
|
||||
adjustedGasPrice = maxFeePerGas;
|
||||
|
||||
// Polygon has a minimum priority fee of 25 gwei
|
||||
if (isPolygon) {
|
||||
int minPriorityFee = 25;
|
||||
int minPriorityFeeWei =
|
||||
EtherAmount.fromInt(EtherUnit.gwei, minPriorityFee).getInWei.toInt();
|
||||
|
||||
// Calculate user selected priority-based additional fee on top of minimum
|
||||
int additionalPriorityFee = 0;
|
||||
switch (priority) {
|
||||
case EVMChainTransactionPriority.slow:
|
||||
// We use minimum priority fee only
|
||||
additionalPriorityFee = 0;
|
||||
break;
|
||||
case EVMChainTransactionPriority.medium:
|
||||
// We add 15 gwei on top of minimum
|
||||
additionalPriorityFee = EtherAmount.fromInt(EtherUnit.gwei, 15).getInWei.toInt();
|
||||
break;
|
||||
case EVMChainTransactionPriority.fast:
|
||||
// We add 35 gwei on top of minimum
|
||||
additionalPriorityFee = EtherAmount.fromInt(EtherUnit.gwei, 35).getInWei.toInt();
|
||||
break;
|
||||
}
|
||||
|
||||
int totalPriorityFee = minPriorityFeeWei + additionalPriorityFee;
|
||||
adjustedGasPrice = gasPrice + totalPriorityFee;
|
||||
maxFeePerGas = gasPrice + totalPriorityFee;
|
||||
}
|
||||
|
||||
final estimatedGas = await _client.getEstimatedGasUnitsForTransaction(
|
||||
contractAddress: contractAddress,
|
||||
senderAddress: _evmChainPrivateKey.address,
|
||||
value: EtherAmount.fromBigInt(EtherUnit.wei, amount!),
|
||||
gasPrice: EtherAmount.fromInt(EtherUnit.wei, adjustedGasPrice),
|
||||
toAddress: EthereumAddress.fromHex(receivingAddressHex),
|
||||
maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
|
||||
data: data,
|
||||
);
|
||||
|
||||
final totalGasFee = estimatedGas * adjustedGasPrice;
|
||||
|
||||
return GasParamsHandler(
|
||||
estimatedGasUnits: estimatedGas,
|
||||
estimatedGasFee: totalGasFee,
|
||||
maxFeePerGas: maxFeePerGas,
|
||||
gasPrice: adjustedGasPrice,
|
||||
);
|
||||
} catch (e) {
|
||||
return GasParamsHandler.zero();
|
||||
}
|
||||
@@ -400,7 +447,6 @@ abstract class EVMChainWalletBase
|
||||
Future<void> close({bool shouldCleanup = false}) async {
|
||||
_client.stop();
|
||||
_transactionsUpdateTimer?.cancel();
|
||||
_updateFeesTimer?.cancel();
|
||||
}
|
||||
|
||||
@action
|
||||
@@ -440,30 +486,16 @@ abstract class EVMChainWalletBase
|
||||
|
||||
await _updateBalance();
|
||||
await _updateTransactions();
|
||||
await _updateEstimatedGasFeeParams();
|
||||
await _getEstimatedFees(
|
||||
hasPriorityFee ? EVMChainTransactionPriority.medium : null,
|
||||
); // We're using medium priority for default estimation
|
||||
|
||||
_updateFeesTimer ??= Timer.periodic(const Duration(seconds: 30), (timer) async {
|
||||
await _updateEstimatedGasFeeParams();
|
||||
});
|
||||
syncStatus = SyncedSyncStatus();
|
||||
} catch (e) {
|
||||
syncStatus = FailedSyncStatus();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _updateEstimatedGasFeeParams() async {
|
||||
gasBaseFee = await _client.getGasBaseFee();
|
||||
|
||||
gasPrice = await _client.getGasUnitPrice();
|
||||
|
||||
estimatedGasUnits = await _client.getEstimatedGasUnitsForTransaction(
|
||||
senderAddress: _evmChainPrivateKey.address,
|
||||
toAddress: _evmChainPrivateKey.address,
|
||||
gasPrice: EtherAmount.fromInt(EtherUnit.wei, gasPrice),
|
||||
value: EtherAmount.fromBigInt(EtherUnit.wei, BigInt.one),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PendingTransaction> createTransaction(Object credentials) async {
|
||||
final _credentials = credentials as EVMChainTransactionCredentials;
|
||||
@@ -511,7 +543,7 @@ abstract class EVMChainWalletBase
|
||||
final gasFeesModel = await calculateActualEstimatedFeeForCreateTransaction(
|
||||
amount: totalAmount,
|
||||
receivingAddressHex: toAddress,
|
||||
priority: _credentials.priority!,
|
||||
priority: _credentials.priority,
|
||||
contractAddress: contractAddress,
|
||||
);
|
||||
|
||||
@@ -539,7 +571,7 @@ abstract class EVMChainWalletBase
|
||||
final gasFeesModel = await calculateActualEstimatedFeeForCreateTransaction(
|
||||
amount: totalAmount,
|
||||
receivingAddressHex: toAddress,
|
||||
priority: _credentials.priority!,
|
||||
priority: _credentials.priority,
|
||||
contractAddress: contractAddress,
|
||||
);
|
||||
|
||||
@@ -562,7 +594,7 @@ abstract class EVMChainWalletBase
|
||||
final refinedGasFeesModel = await calculateActualEstimatedFeeForCreateTransaction(
|
||||
amount: totalAmount,
|
||||
receivingAddressHex: toAddress,
|
||||
priority: _credentials.priority!,
|
||||
priority: _credentials.priority,
|
||||
contractAddress: contractAddress,
|
||||
);
|
||||
|
||||
@@ -577,7 +609,7 @@ abstract class EVMChainWalletBase
|
||||
totalAmount = (currencyBalance.balance - estimatedFeesForTransaction);
|
||||
}
|
||||
|
||||
// check the fees on the base currency (Eth/Polygon)
|
||||
// check the fees on the base currency
|
||||
if (estimatedFeesForTransaction > balance[currency]!.balance) {
|
||||
throw EVMChainTransactionFeesException(currency.title);
|
||||
}
|
||||
@@ -599,9 +631,9 @@ abstract class EVMChainWalletBase
|
||||
toAddress: toAddress,
|
||||
amount: totalAmount,
|
||||
gasFee: estimatedFeesForTransaction,
|
||||
priority: _credentials.priority!,
|
||||
priority: _credentials.priority,
|
||||
currency: transactionCurrency,
|
||||
feeCurrency: switch (_client.chainId) { 1 => "ETH", 137 => "POL", 8453 => "ETH", _ => "ETH" },
|
||||
feeCurrency: switch (_client.chainId) { 137 => "POL", _ => "ETH" },
|
||||
maxFeePerGas: maxFeePerGasForTransaction,
|
||||
exponent: exponent,
|
||||
contractAddress:
|
||||
@@ -614,12 +646,11 @@ abstract class EVMChainWalletBase
|
||||
}
|
||||
|
||||
Future<PendingTransaction> createCallDataTransaction(
|
||||
String to,
|
||||
String dataHex,
|
||||
BigInt valueWei,
|
||||
EVMChainTransactionPriority priority,
|
||||
) async {
|
||||
|
||||
String to,
|
||||
String dataHex,
|
||||
BigInt valueWei,
|
||||
EVMChainTransactionPriority? priority,
|
||||
) async {
|
||||
// Estimate gas with the SAME call (sender, to, value, data)
|
||||
final gas = await calculateActualEstimatedFeeForCreateTransaction(
|
||||
amount: valueWei, // native value (usually 0 for ERC20 transfer)
|
||||
@@ -632,7 +663,8 @@ abstract class EVMChainWalletBase
|
||||
final nativeCurrency = switch (_client.chainId) {
|
||||
137 => CryptoCurrency.maticpoly,
|
||||
8453 => CryptoCurrency.baseEth,
|
||||
_ => CryptoCurrency.eth,
|
||||
42161 => CryptoCurrency.arbEth,
|
||||
_ => CryptoCurrency.eth,
|
||||
};
|
||||
|
||||
// Fallback for nodes that fail estimate (non-zero)
|
||||
@@ -656,9 +688,13 @@ abstract class EVMChainWalletBase
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Future<PendingTransaction> createApprovalTransaction(BigInt amount, String spender,
|
||||
CryptoCurrency token, EVMChainTransactionPriority priority, String feeCurrency) async {
|
||||
Future<PendingTransaction> createApprovalTransaction(
|
||||
BigInt amount,
|
||||
String spender,
|
||||
CryptoCurrency token,
|
||||
EVMChainTransactionPriority? priority,
|
||||
String feeCurrency,
|
||||
) async {
|
||||
final CryptoCurrency transactionCurrency =
|
||||
balance.keys.firstWhere((element) => element.title == token.title);
|
||||
assert(transactionCurrency is Erc20Token);
|
||||
@@ -840,10 +876,7 @@ abstract class EVMChainWalletBase
|
||||
}
|
||||
|
||||
Future<bool> isApprovalRequired(
|
||||
String tokenContract,
|
||||
String spender,
|
||||
BigInt requiredAmount) async {
|
||||
|
||||
String tokenContract, String spender, BigInt requiredAmount) async {
|
||||
const zero = '0x0000000000000000000000000000000000000000';
|
||||
const evmNative = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
|
||||
|
||||
@@ -859,9 +892,7 @@ abstract class EVMChainWalletBase
|
||||
chainId: _client.chainId,
|
||||
);
|
||||
|
||||
final allowance = await erc20.allowance(
|
||||
owner,
|
||||
EthereumAddress.fromHex(spender));
|
||||
final allowance = await erc20.allowance(owner, EthereumAddress.fromHex(spender));
|
||||
|
||||
return allowance < requiredAmount;
|
||||
} catch (e) {
|
||||
@@ -898,7 +929,6 @@ abstract class EVMChainWalletBase
|
||||
@override
|
||||
Future<void> updateTransactionsHistory() async => await _updateTransactions();
|
||||
|
||||
|
||||
List<Erc20Token> get erc20Currencies => evmChainErc20TokensBox.values.toList();
|
||||
|
||||
Future<void> addErc20Token(Erc20Token token) async {
|
||||
@@ -998,6 +1028,8 @@ abstract class EVMChainWalletBase
|
||||
/// PolygonScan for Polygon.
|
||||
///
|
||||
/// BaseScan for Base.
|
||||
///
|
||||
/// ArbiScan for Arbitrum.
|
||||
void updateScanProviderUsageState(bool isEnabled) {
|
||||
if (isEnabled) {
|
||||
_updateTransactions();
|
||||
|
||||
@@ -356,6 +356,8 @@ class CommonTestFlows {
|
||||
return secrets.solanaTestWalletSeeds;
|
||||
case WalletType.base:
|
||||
return secrets.baseTestWalletSeeds;
|
||||
case WalletType.arbitrum:
|
||||
return secrets.arbitrumTestWalletSeeds;
|
||||
case WalletType.tron:
|
||||
return secrets.tronTestWalletSeeds;
|
||||
case WalletType.nano:
|
||||
@@ -392,6 +394,8 @@ class CommonTestFlows {
|
||||
return secrets.polygonTestWalletReceiveAddress;
|
||||
case WalletType.base:
|
||||
return secrets.baseTestWalletReceiveAddress;
|
||||
case WalletType.arbitrum:
|
||||
return secrets.arbitrumTestWalletReceiveAddress;
|
||||
case WalletType.solana:
|
||||
return secrets.solanaTestWalletReceiveAddress;
|
||||
case WalletType.tron:
|
||||
|
||||
@@ -322,6 +322,26 @@
|
||||
<string>base-wallet</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>arbitrum</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>arbitrum</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>arbitrum-wallet</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>arbitrum-wallet</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
part of 'arbitrum.dart';
|
||||
|
||||
class CWArbitrum extends Arbitrum {
|
||||
@override
|
||||
List<String> getArbitrumWordList(String language) => EVMChainMnemonics.englishWordlist;
|
||||
|
||||
WalletService createArbitrumWalletService(bool isDirect) =>
|
||||
ArbitrumWalletService(isDirect, client: ArbitrumClient());
|
||||
|
||||
@override
|
||||
WalletCredentials createArbitrumNewWalletCredentials({
|
||||
required String name,
|
||||
String? mnemonic,
|
||||
WalletInfo? walletInfo,
|
||||
String? password,
|
||||
String? passphrase,
|
||||
}) =>
|
||||
EVMChainNewWalletCredentials(
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
mnemonic: mnemonic,
|
||||
passphrase: passphrase,
|
||||
);
|
||||
|
||||
@override
|
||||
WalletCredentials createArbitrumRestoreWalletFromSeedCredentials({
|
||||
required String name,
|
||||
required String mnemonic,
|
||||
required String password,
|
||||
String? passphrase,
|
||||
}) =>
|
||||
EVMChainRestoreWalletFromSeedCredentials(
|
||||
name: name,
|
||||
password: password,
|
||||
mnemonic: mnemonic,
|
||||
passphrase: passphrase,
|
||||
);
|
||||
|
||||
@override
|
||||
WalletCredentials createArbitrumRestoreWalletFromPrivateKey({
|
||||
required String name,
|
||||
required String privateKey,
|
||||
required String password,
|
||||
}) =>
|
||||
EVMChainRestoreWalletFromPrivateKey(name: name, password: password, privateKey: privateKey);
|
||||
|
||||
@override
|
||||
WalletCredentials createArbitrumHardwareWalletCredentials({
|
||||
required String name,
|
||||
required HardwareAccountData hwAccountData,
|
||||
WalletInfo? walletInfo,
|
||||
}) =>
|
||||
EVMChainRestoreWalletFromHardware(
|
||||
name: name,
|
||||
hwAccountData: hwAccountData,
|
||||
walletInfo: walletInfo,
|
||||
);
|
||||
|
||||
@override
|
||||
String getAddress(WalletBase wallet) => (wallet as ArbitrumWallet).walletAddresses.address;
|
||||
|
||||
@override
|
||||
String getPrivateKey(WalletBase wallet) {
|
||||
final privateKeyHolder = (wallet as ArbitrumWallet).evmChainPrivateKey;
|
||||
if (privateKeyHolder is EthPrivateKey) return bytesToHex(privateKeyHolder.privateKey);
|
||||
return "";
|
||||
}
|
||||
|
||||
@override
|
||||
String getPublicKey(WalletBase wallet) {
|
||||
final privateKeyInUnitInt = (wallet as ArbitrumWallet).evmChainPrivateKey;
|
||||
return privateKeyInUnitInt.address.hex;
|
||||
}
|
||||
|
||||
Object createArbitrumTransactionCredentials(
|
||||
List<Output> outputs, {
|
||||
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: null,
|
||||
currency: currency,
|
||||
feeRate: feeRate,
|
||||
);
|
||||
|
||||
Object createArbitrumTransactionCredentialsRaw(
|
||||
List<OutputInfo> outputs, {
|
||||
required CryptoCurrency currency,
|
||||
required int feeRate,
|
||||
}) =>
|
||||
EVMChainTransactionCredentials(
|
||||
outputs,
|
||||
priority: null,
|
||||
currency: currency,
|
||||
feeRate: feeRate,
|
||||
);
|
||||
|
||||
@override
|
||||
int formatterArbitrumParseAmount(String amount) => EVMChainFormatter.parseEVMChainAmount(amount);
|
||||
|
||||
@override
|
||||
double formatterArbitrumAmountToDouble({
|
||||
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 ArbitrumWallet).erc20Currencies;
|
||||
|
||||
@override
|
||||
Future<void> addErc20Token(WalletBase wallet, CryptoCurrency token) =>
|
||||
(wallet as ArbitrumWallet).addErc20Token(token as Erc20Token);
|
||||
|
||||
@override
|
||||
Future<void> deleteErc20Token(WalletBase wallet, CryptoCurrency token) =>
|
||||
(wallet as ArbitrumWallet).deleteErc20Token(token as Erc20Token);
|
||||
|
||||
@override
|
||||
Future<void> removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token) =>
|
||||
(wallet as ArbitrumWallet).removeTokenTransactionsInHistory(token as Erc20Token);
|
||||
|
||||
@override
|
||||
Future<Erc20Token?> getErc20Token(WalletBase wallet, String contractAddress) =>
|
||||
(wallet as ArbitrumWallet).getErc20Token(contractAddress, 'arbitrum');
|
||||
|
||||
@override
|
||||
CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction) {
|
||||
transaction as EVMChainTransactionInfo;
|
||||
if (transaction.tokenSymbol == CryptoCurrency.arbEth.title ||
|
||||
transaction.tokenSymbol == "ARB") {
|
||||
return CryptoCurrency.arbEth;
|
||||
}
|
||||
|
||||
wallet as ArbitrumWallet;
|
||||
|
||||
return wallet.erc20Currencies.firstWhere(
|
||||
(element) =>
|
||||
transaction.contractAddress?.toLowerCase() == element.contractAddress?.toLowerCase(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void updateArbitrumScanUsageState(WalletBase wallet, bool isEnabled) =>
|
||||
(wallet as ArbitrumWallet).updateScanProviderUsageState(isEnabled);
|
||||
|
||||
@override
|
||||
Web3Client? getWeb3Client(WalletBase wallet) => (wallet as ArbitrumWallet).getWeb3Client();
|
||||
|
||||
@override
|
||||
String getTokenAddress(CryptoCurrency asset) => (asset as Erc20Token).contractAddress;
|
||||
|
||||
@override
|
||||
Future<PendingTransaction> createTokenApproval(
|
||||
WalletBase wallet,
|
||||
BigInt amount,
|
||||
String spender,
|
||||
CryptoCurrency token,
|
||||
) =>
|
||||
(wallet as EVMChainWallet).createApprovalTransaction(
|
||||
amount,
|
||||
spender,
|
||||
token,
|
||||
null,
|
||||
"ARB",
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> setHardwareWalletService(WalletBase wallet, HardwareWalletService service) async {
|
||||
if (service is EVMChainLedgerService) {
|
||||
((wallet as EVMChainWallet).evmChainPrivateKey as EvmLedgerCredentials).setLedgerConnection(
|
||||
service.ledgerConnection, (await wallet.walletInfo.getDerivationInfo()).derivationPath);
|
||||
} else if (service is EVMChainBitboxService) {
|
||||
((wallet as EVMChainWallet).evmChainPrivateKey as EvmBitboxCredentials)
|
||||
.setBitbox(service.manager, (await wallet.walletInfo.getDerivationInfo()).derivationPath);
|
||||
}
|
||||
return Future.value();
|
||||
}
|
||||
|
||||
@override
|
||||
HardwareWalletService getLedgerHardwareWalletService(ledger.LedgerConnection connection) =>
|
||||
EVMChainLedgerService(connection);
|
||||
|
||||
@override
|
||||
HardwareWalletService getBitboxHardwareWalletService(bitbox.BitboxManager manager) =>
|
||||
EVMChainBitboxService(manager, chainId: 42161);
|
||||
|
||||
@override
|
||||
List<String> getDefaultTokenContractAddresses() => DefaultArbitrumErc20Tokens()
|
||||
.initialArbitrumErc20Tokens
|
||||
.map((e) => e.contractAddress)
|
||||
.toList();
|
||||
|
||||
@override
|
||||
bool isTokenAlreadyAdded(WalletBase wallet, String contractAddress) {
|
||||
final arbitrumWallet = wallet as ArbitrumWallet;
|
||||
return arbitrumWallet.erc20Currencies.any(
|
||||
(element) => element.contractAddress.toLowerCase() == contractAddress.toLowerCase(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> isApprovalRequired(
|
||||
WalletBase wallet, String tokenContract, String spender, BigInt requiredAmount) =>
|
||||
(wallet as EVMChainWallet).isApprovalRequired(tokenContract, spender, requiredAmount);
|
||||
|
||||
@override
|
||||
Future<PendingTransaction> createRawCallDataTransaction(
|
||||
WalletBase wallet, String to, String dataHex, BigInt valueWei) =>
|
||||
(wallet as EVMChainWallet).createCallDataTransaction(to, dataHex, valueWei, null);
|
||||
|
||||
@override
|
||||
String? getArbitrumNativeEstimatedFee(WalletBase wallet) =>
|
||||
(wallet as EVMChainWallet).nativeTxEstimatedFee;
|
||||
|
||||
@override
|
||||
String? getArbitrumERC20EstimatedFee(WalletBase wallet) =>
|
||||
(wallet as EVMChainWallet).erc20TxEstimatedFee;
|
||||
}
|
||||
+14
-5
@@ -190,7 +190,8 @@ class CWBase extends Base {
|
||||
String getTokenAddress(CryptoCurrency asset) => (asset as Erc20Token).contractAddress;
|
||||
|
||||
@override
|
||||
Future<bool> isApprovalRequired(WalletBase wallet, String tokenContract,String spender, BigInt requiredAmount) =>
|
||||
Future<bool> isApprovalRequired(
|
||||
WalletBase wallet, String tokenContract, String spender, BigInt requiredAmount) =>
|
||||
(wallet as EVMChainWallet).isApprovalRequired(tokenContract, spender, requiredAmount);
|
||||
|
||||
@override
|
||||
@@ -210,10 +211,10 @@ class CWBase extends Base {
|
||||
);
|
||||
|
||||
@override
|
||||
Future<PendingTransaction> createRawCallDataTransaction(WalletBase wallet, String to, String dataHex, BigInt valueWei,
|
||||
TransactionPriority priority) =>
|
||||
(wallet as EVMChainWallet).createCallDataTransaction(to, dataHex, valueWei, priority as EVMChainTransactionPriority);
|
||||
|
||||
Future<PendingTransaction> createRawCallDataTransaction(WalletBase wallet, String to,
|
||||
String dataHex, BigInt valueWei, TransactionPriority priority) =>
|
||||
(wallet as EVMChainWallet).createCallDataTransaction(
|
||||
to, dataHex, valueWei, priority as EVMChainTransactionPriority);
|
||||
|
||||
@override
|
||||
Future<void> setHardwareWalletService(WalletBase wallet, HardwareWalletService service) async {
|
||||
@@ -246,4 +247,12 @@ class CWBase extends Base {
|
||||
(element) => element.contractAddress.toLowerCase() == contractAddress.toLowerCase(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String? getBaseNativeEstimatedFee(WalletBase wallet) =>
|
||||
(wallet as EVMChainWallet).nativeTxEstimatedFee;
|
||||
|
||||
@override
|
||||
String? getBaseERC20EstimatedFee(WalletBase wallet) =>
|
||||
(wallet as EVMChainWallet).erc20TxEstimatedFee;
|
||||
}
|
||||
|
||||
@@ -136,6 +136,7 @@ class DFXBuyProvider extends BuyProvider {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.arbitrum:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
return wallet.signMessage(message);
|
||||
|
||||
@@ -219,6 +219,7 @@ class KryptonimBuyProvider extends BuyProvider {
|
||||
String? blockchain = switch (cur.tag) {
|
||||
'ETH' => 'Ethereum',
|
||||
'BASE' => 'Base',
|
||||
'ARB' => 'Arbitrum',
|
||||
'POL' => 'Polygon',
|
||||
'AVAXC' => 'Avalanche',
|
||||
'SOL' => 'Solana',
|
||||
@@ -231,6 +232,7 @@ class KryptonimBuyProvider extends BuyProvider {
|
||||
CryptoCurrency.ltc => 'Litecoin',
|
||||
CryptoCurrency.eth => 'Ethereum',
|
||||
CryptoCurrency.baseEth => 'Base',
|
||||
CryptoCurrency.arbEth => 'Arbitrum',
|
||||
CryptoCurrency.maticpoly => 'Matic',
|
||||
_ => null,
|
||||
};
|
||||
|
||||
@@ -324,6 +324,8 @@ class OnRamperBuyProvider extends BuyProvider {
|
||||
switch (tag) {
|
||||
case 'POL':
|
||||
return 'POLYGON';
|
||||
case 'ARB':
|
||||
return 'ARBITRUM';
|
||||
case 'ETH':
|
||||
return 'ETHEREUM';
|
||||
case 'TRX':
|
||||
|
||||
@@ -106,6 +106,7 @@ class RobinhoodBuyProvider extends BuyProvider {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.arbitrum:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
case WalletType.dogecoin:
|
||||
|
||||
@@ -162,6 +162,8 @@ class OpenCryptoPayService {
|
||||
return "Ethereum";
|
||||
case "POL":
|
||||
return "Polygon";
|
||||
case "ARB":
|
||||
return "Arbitrum";
|
||||
case "TRX":
|
||||
return "Tron";
|
||||
case "SOL":
|
||||
|
||||
@@ -101,6 +101,21 @@ class BaseURI extends PaymentURI {
|
||||
}
|
||||
}
|
||||
|
||||
class ArbitrumURI extends PaymentURI {
|
||||
ArbitrumURI({required super.amount, required super.address});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
var base = 'arbitrum:$address';
|
||||
|
||||
if (amount.isNotEmpty) {
|
||||
base += '?amount=${amount.replaceAll(',', '.')}';
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
}
|
||||
|
||||
class BitcoinCashURI extends PaymentURI {
|
||||
BitcoinCashURI({required super.amount, required super.address});
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/arbitrum/arbitrum.dart';
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/core/validator.dart';
|
||||
@@ -55,6 +56,8 @@ class SeedValidator extends Validator<MnemonicItem> {
|
||||
return decred!.getDecredWordList();
|
||||
case WalletType.base:
|
||||
return base!.getBaseWordList(language);
|
||||
case WalletType.arbitrum:
|
||||
return arbitrum!.getArbitrumWordList(language);
|
||||
case WalletType.none:
|
||||
case WalletType.haven:
|
||||
return [];
|
||||
|
||||
@@ -79,6 +79,7 @@ class WalletCreationService {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.arbitrum:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
case WalletType.dogecoin:
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
import 'package:cake_wallet/anonpay/anonpay_api.dart';
|
||||
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
|
||||
import 'package:cake_wallet/anypay/anypay_api.dart';
|
||||
import 'package:cake_wallet/arbitrum/arbitrum.dart';
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
||||
@@ -1226,6 +1227,8 @@ Future<void> setup({
|
||||
return decred!.createDecredWalletService(_unspentCoinsInfoSource);
|
||||
case WalletType.base:
|
||||
return base!.createBaseWalletService(SettingsStoreBase.walletPasswordDirectInput);
|
||||
case WalletType.arbitrum:
|
||||
return arbitrum!.createArbitrumWalletService(SettingsStoreBase.walletPasswordDirectInput);
|
||||
case WalletType.haven:
|
||||
return HavenWalletService();
|
||||
case WalletType.none:
|
||||
|
||||
@@ -50,6 +50,7 @@ const moneroWorldNodeUri = '.moneroworld.com';
|
||||
const decredDefaultUri = "default-spv-nodes";
|
||||
const dogecoinDefaultNodeUri = 'dogecoin.stackwallet.com:50022';
|
||||
const baseDefaultNodeUri = 'base.nownodes.io';
|
||||
const arbitrumDefaultNodeUri = 'arbitrum.nownodes.io';
|
||||
|
||||
Future<void> defaultSettingsMigration(
|
||||
{required int version,
|
||||
@@ -545,6 +546,16 @@ Future<void> defaultSettingsMigration(
|
||||
currentNodePreferenceKey: PreferencesKey.currentBaseNodeIdKey,
|
||||
);
|
||||
break;
|
||||
case 53:
|
||||
await addWalletNodeList(nodes: nodes, type: WalletType.arbitrum);
|
||||
await _changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.arbitrum,
|
||||
currentNodePreferenceKey: PreferencesKey.currentArbitrumNodeIdKey,
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -655,6 +666,8 @@ String _getDefaultNodeUri(WalletType type) {
|
||||
return dogecoinDefaultNodeUri;
|
||||
case WalletType.base:
|
||||
return baseDefaultNodeUri;
|
||||
case WalletType.arbitrum:
|
||||
return arbitrumDefaultNodeUri;
|
||||
case WalletType.banano:
|
||||
case WalletType.none:
|
||||
return '';
|
||||
@@ -1057,6 +1070,7 @@ Future<void> checkCurrentNodes(
|
||||
final currentEthereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
|
||||
final currentPolygonNodeId = sharedPreferences.getInt(PreferencesKey.currentPolygonNodeIdKey);
|
||||
final currentBaseNodeId = sharedPreferences.getInt(PreferencesKey.currentBaseNodeIdKey);
|
||||
final currentArbitrumNodeId = sharedPreferences.getInt(PreferencesKey.currentArbitrumNodeIdKey);
|
||||
final currentNanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
|
||||
final currentNanoPowNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoPowNodeIdKey);
|
||||
final currentDecredNodeId = sharedPreferences.getInt(PreferencesKey.currentDecredNodeIdKey);
|
||||
@@ -1082,6 +1096,8 @@ Future<void> checkCurrentNodes(
|
||||
nodeSource.values.firstWhereOrNull((node) => node.key == currentPolygonNodeId);
|
||||
final currentBaseNodeServer =
|
||||
nodeSource.values.firstWhereOrNull((node) => node.key == currentBaseNodeId);
|
||||
final currentArbitrumNodeServer =
|
||||
nodeSource.values.firstWhereOrNull((node) => node.key == currentArbitrumNodeId);
|
||||
final currentNanoNodeServer =
|
||||
nodeSource.values.firstWhereOrNull((node) => node.key == currentNanoNodeId);
|
||||
final currentDecredNodeServer =
|
||||
@@ -1179,6 +1195,12 @@ Future<void> checkCurrentNodes(
|
||||
await sharedPreferences.setInt(PreferencesKey.currentBaseNodeIdKey, node.key as int);
|
||||
}
|
||||
|
||||
if (currentArbitrumNodeServer == null) {
|
||||
final node = Node(uri: arbitrumDefaultNodeUri, type: WalletType.arbitrum);
|
||||
await nodeSource.add(node);
|
||||
await sharedPreferences.setInt(PreferencesKey.currentArbitrumNodeIdKey, node.key as int);
|
||||
}
|
||||
|
||||
if (currentSolanaNodeServer == null) {
|
||||
final node = Node(uri: solanaDefaultNodeUri, type: WalletType.solana);
|
||||
await nodeSource.add(node);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/arbitrum/arbitrum.dart';
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cake_wallet/polygon/polygon.dart';
|
||||
@@ -26,6 +27,10 @@ class EnsRecord {
|
||||
_client = base!.getWeb3Client(wallet);
|
||||
}
|
||||
|
||||
if (wallet != null && wallet.type == WalletType.arbitrum) {
|
||||
_client = arbitrum!.getWeb3Client(wallet);
|
||||
}
|
||||
|
||||
if (_client == null) {
|
||||
late final client = ProxyWrapper().getHttpIOClient();
|
||||
|
||||
@@ -48,6 +53,7 @@ class EnsRecord {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.arbitrum:
|
||||
default:
|
||||
return (await ens.withName(name).getAddress()).hex;
|
||||
}
|
||||
|
||||
@@ -52,6 +52,9 @@ Future<List<Node>> loadDefaultNodes(WalletType type) async {
|
||||
case WalletType.base:
|
||||
path = 'assets/base_node_list.yml';
|
||||
break;
|
||||
case WalletType.arbitrum:
|
||||
path = 'assets/arbitrum_node_list.yml';
|
||||
break;
|
||||
case WalletType.banano:
|
||||
case WalletType.none:
|
||||
path = '';
|
||||
@@ -104,6 +107,7 @@ Future<void> resetToDefault(Box<Node> nodeSource) async {
|
||||
final zanoNodes = await loadDefaultNodes(WalletType.zano);
|
||||
final dogecoinElectrumServerList = await loadDefaultNodes(WalletType.dogecoin);
|
||||
final baseNodes = await loadDefaultNodes(WalletType.base);
|
||||
final arbitrumNodes = await loadDefaultNodes(WalletType.arbitrum);
|
||||
|
||||
final nodes = moneroNodes +
|
||||
bitcoinElectrumServerList +
|
||||
@@ -118,7 +122,8 @@ Future<void> resetToDefault(Box<Node> nodeSource) async {
|
||||
zanoNodes +
|
||||
decredNodes +
|
||||
dogecoinElectrumServerList +
|
||||
baseNodes;
|
||||
baseNodes +
|
||||
arbitrumNodes;
|
||||
|
||||
await nodeSource.clear();
|
||||
await nodeSource.addAll(nodes);
|
||||
|
||||
@@ -115,6 +115,7 @@ class AddressResolver {
|
||||
"pog",
|
||||
"polygon",
|
||||
"base",
|
||||
"arbitrum"
|
||||
"press",
|
||||
"privacy",
|
||||
"pro",
|
||||
|
||||
@@ -9,6 +9,7 @@ class PreferencesKey {
|
||||
static const currentEthereumNodeIdKey = 'current_node_id_eth';
|
||||
static const currentPolygonNodeIdKey = 'current_node_id_matic';
|
||||
static const currentBaseNodeIdKey = 'current_node_id_base';
|
||||
static const currentArbitrumNodeIdKey = 'current_node_id_arbitrum';
|
||||
static const currentNanoNodeIdKey = 'current_node_id_nano';
|
||||
static const currentNanoPowNodeIdKey = 'current_node_id_nano_pow';
|
||||
static const currentDecredNodeIdKey = 'current_node_id_decred';
|
||||
@@ -80,6 +81,7 @@ class PreferencesKey {
|
||||
static const useEtherscan = 'use_etherscan';
|
||||
static const usePolygonScan = 'use_polygonscan';
|
||||
static const useBaseScan = 'use_base_scan';
|
||||
static const useArbiScan = 'use_arbitrum_scan';
|
||||
static const useTronGrid = 'use_trongrid';
|
||||
static const useMempoolFeeAPI = 'use_mempool_fee_api';
|
||||
static const defaultNanoRep = 'default_nano_representative';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/arbitrum/arbitrum.dart';
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
||||
@@ -31,7 +32,7 @@ List<TransactionPriority> priorityForWalletType(WalletType type) {
|
||||
return polygon!.getTransactionPriorities();
|
||||
case WalletType.base:
|
||||
return base!.getTransactionPriorities();
|
||||
// no such thing for nano/banano/solana/tron:
|
||||
case WalletType.arbitrum:
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
case WalletType.solana:
|
||||
|
||||
@@ -189,8 +189,8 @@ class CWEthereum extends Ethereum {
|
||||
((wallet as EVMChainWallet).evmChainPrivateKey as EvmBitboxCredentials)
|
||||
.setBitbox(service.manager, (await wallet.walletInfo.getDerivationInfo()).derivationPath);
|
||||
} else if (service is EVMChainTrezorService) {
|
||||
((wallet as EVMChainWallet).evmChainPrivateKey as EvmTrezorCredentials)
|
||||
.setTrezorConnect(service.connect, (await wallet.walletInfo.getDerivationInfo()).derivationPath);
|
||||
((wallet as EVMChainWallet).evmChainPrivateKey as EvmTrezorCredentials).setTrezorConnect(
|
||||
service.connect, (await wallet.walletInfo.getDerivationInfo()).derivationPath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,15 +211,16 @@ class CWEthereum extends Ethereum {
|
||||
return DefaultEthereumErc20Tokens().initialErc20Tokens.map((e) => e.contractAddress).toList();
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
bool isTokenAlreadyAdded(WalletBase wallet, String contractAddress) {
|
||||
final ethereumWallet = wallet as EthereumWallet;
|
||||
return ethereumWallet.erc20Currencies.any((element) => element.contractAddress.toLowerCase() == contractAddress.toLowerCase());
|
||||
return ethereumWallet.erc20Currencies
|
||||
.any((element) => element.contractAddress.toLowerCase() == contractAddress.toLowerCase());
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> isApprovalRequired(WalletBase wallet, String tokenContract,String spender, BigInt requiredAmount) =>
|
||||
Future<bool> isApprovalRequired(
|
||||
WalletBase wallet, String tokenContract, String spender, BigInt requiredAmount) =>
|
||||
(wallet as EVMChainWallet).isApprovalRequired(tokenContract, spender, requiredAmount);
|
||||
|
||||
@override
|
||||
@@ -229,9 +230,10 @@ class CWEthereum extends Ethereum {
|
||||
amount, spender, token, priority as EVMChainTransactionPriority, "ETH");
|
||||
|
||||
@override
|
||||
Future<PendingTransaction> createRawCallDataTransaction(WalletBase wallet, String to, String dataHex, BigInt valueWei,
|
||||
TransactionPriority priority) =>
|
||||
(wallet as EVMChainWallet).createCallDataTransaction(to, dataHex, valueWei, priority as EVMChainTransactionPriority);
|
||||
Future<PendingTransaction> createRawCallDataTransaction(WalletBase wallet, String to,
|
||||
String dataHex, BigInt valueWei, TransactionPriority priority) =>
|
||||
(wallet as EVMChainWallet).createCallDataTransaction(
|
||||
to, dataHex, valueWei, priority as EVMChainTransactionPriority);
|
||||
|
||||
// Integrations
|
||||
@override
|
||||
@@ -270,4 +272,12 @@ class CWEthereum extends Ethereum {
|
||||
@override
|
||||
Future<PendingTransaction> enableDEuroSaving(WalletBase wallet, TransactionPriority priority) =>
|
||||
DEuro(wallet as EthereumWallet).enableSavings(priority as EVMChainTransactionPriority);
|
||||
|
||||
@override
|
||||
String? getEthereumNativeEstimatedFee(WalletBase wallet) =>
|
||||
(wallet as EVMChainWallet).nativeTxEstimatedFee;
|
||||
|
||||
@override
|
||||
String? getEthereumERC20EstimatedFee(WalletBase wallet) =>
|
||||
(wallet as EVMChainWallet).erc20TxEstimatedFee;
|
||||
}
|
||||
|
||||
@@ -466,6 +466,8 @@ class SideShiftExchangeProvider extends ExchangeProvider {
|
||||
return 'lightning';
|
||||
case 'POL':
|
||||
return 'polygon';
|
||||
case 'ARB':
|
||||
return 'arbitrum';
|
||||
case 'ZEC':
|
||||
return 'zcash';
|
||||
case 'AVAXC':
|
||||
@@ -481,6 +483,7 @@ class SideShiftExchangeProvider extends ExchangeProvider {
|
||||
'tron' => 'TRX',
|
||||
'lightning' => 'LN',
|
||||
'polygon' => 'POL',
|
||||
'arbitrum' => 'ARB',
|
||||
'zcash' => 'ZEC',
|
||||
'avax' => 'AVAXC',
|
||||
_ => network,
|
||||
|
||||
+1
-1
@@ -260,7 +260,7 @@ Future<void> initializeAppConfigs({bool loadWallet = true}) async {
|
||||
payjoinSessionSource: payjoinSessionSource,
|
||||
anonpayInvoiceInfo: anonpayInvoiceInfo,
|
||||
havenSeedStore: havenSeedStore,
|
||||
initialMigrationVersion: 52,
|
||||
initialMigrationVersion: 53,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+76
-58
@@ -14,13 +14,14 @@ class CWPolygon extends Polygon {
|
||||
WalletInfo? walletInfo,
|
||||
String? password,
|
||||
String? passphrase,
|
||||
}) => EVMChainNewWalletCredentials(
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
mnemonic: mnemonic,
|
||||
passphrase: passphrase,
|
||||
);
|
||||
}) =>
|
||||
EVMChainNewWalletCredentials(
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
mnemonic: mnemonic,
|
||||
passphrase: passphrase,
|
||||
);
|
||||
|
||||
@override
|
||||
WalletCredentials createPolygonRestoreWalletFromSeedCredentials({
|
||||
@@ -28,30 +29,33 @@ class CWPolygon extends Polygon {
|
||||
required String mnemonic,
|
||||
required String password,
|
||||
String? passphrase,
|
||||
}) => EVMChainRestoreWalletFromSeedCredentials(
|
||||
name: name,
|
||||
password: password,
|
||||
mnemonic: mnemonic,
|
||||
passphrase: passphrase,
|
||||
);
|
||||
}) =>
|
||||
EVMChainRestoreWalletFromSeedCredentials(
|
||||
name: name,
|
||||
password: password,
|
||||
mnemonic: mnemonic,
|
||||
passphrase: passphrase,
|
||||
);
|
||||
|
||||
@override
|
||||
WalletCredentials createPolygonRestoreWalletFromPrivateKey({
|
||||
required String name,
|
||||
required String privateKey,
|
||||
required String password,
|
||||
}) => EVMChainRestoreWalletFromPrivateKey(name: name, password: password, privateKey: privateKey);
|
||||
}) =>
|
||||
EVMChainRestoreWalletFromPrivateKey(name: name, password: password, privateKey: privateKey);
|
||||
|
||||
@override
|
||||
WalletCredentials createPolygonHardwareWalletCredentials({
|
||||
required String name,
|
||||
required HardwareAccountData hwAccountData,
|
||||
WalletInfo? walletInfo,
|
||||
}) => EVMChainRestoreWalletFromHardware(
|
||||
name: name,
|
||||
hwAccountData: hwAccountData,
|
||||
walletInfo: walletInfo,
|
||||
);
|
||||
}) =>
|
||||
EVMChainRestoreWalletFromHardware(
|
||||
name: name,
|
||||
hwAccountData: hwAccountData,
|
||||
walletInfo: walletInfo,
|
||||
);
|
||||
|
||||
@override
|
||||
String getAddress(WalletBase wallet) => (wallet as PolygonWallet).walletAddresses.address;
|
||||
@@ -87,37 +91,39 @@ class CWPolygon extends Polygon {
|
||||
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,
|
||||
);
|
||||
}) =>
|
||||
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 createPolygonTransactionCredentialsRaw(
|
||||
List<OutputInfo> outputs, {
|
||||
TransactionPriority? priority,
|
||||
required CryptoCurrency currency,
|
||||
required int feeRate,
|
||||
}) => EVMChainTransactionCredentials(
|
||||
outputs,
|
||||
priority: priority as EVMChainTransactionPriority?,
|
||||
currency: currency,
|
||||
feeRate: feeRate,
|
||||
);
|
||||
}) =>
|
||||
EVMChainTransactionCredentials(
|
||||
outputs,
|
||||
priority: priority as EVMChainTransactionPriority?,
|
||||
currency: currency,
|
||||
feeRate: feeRate,
|
||||
);
|
||||
|
||||
@override
|
||||
int formatterPolygonParseAmount(String amount) => EVMChainFormatter.parseEVMChainAmount(amount);
|
||||
@@ -169,7 +175,8 @@ class CWPolygon extends Polygon {
|
||||
wallet as PolygonWallet;
|
||||
|
||||
return wallet.erc20Currencies.firstWhere(
|
||||
(element) => transaction.contractAddress?.toLowerCase() == element.contractAddress?.toLowerCase(),
|
||||
(element) =>
|
||||
transaction.contractAddress?.toLowerCase() == element.contractAddress?.toLowerCase(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -183,12 +190,14 @@ class CWPolygon extends Polygon {
|
||||
@override
|
||||
String getTokenAddress(CryptoCurrency asset) => (asset as Erc20Token).contractAddress;
|
||||
|
||||
Future<bool> isApprovalRequired(WalletBase wallet, String tokenContract,String spender, BigInt requiredAmount) =>
|
||||
Future<bool> isApprovalRequired(
|
||||
WalletBase wallet, String tokenContract, String spender, BigInt requiredAmount) =>
|
||||
(wallet as PolygonWallet).isApprovalRequired(tokenContract, spender, requiredAmount);
|
||||
|
||||
Future<PendingTransaction> createRawCallDataTransaction(WalletBase wallet, String to, String dataHex, BigInt valueWei,
|
||||
TransactionPriority priority) =>
|
||||
(wallet as EVMChainWallet).createCallDataTransaction(to, dataHex, valueWei, priority as EVMChainTransactionPriority);
|
||||
Future<PendingTransaction> createRawCallDataTransaction(WalletBase wallet, String to,
|
||||
String dataHex, BigInt valueWei, TransactionPriority priority) =>
|
||||
(wallet as EVMChainWallet).createCallDataTransaction(
|
||||
to, dataHex, valueWei, priority as EVMChainTransactionPriority);
|
||||
|
||||
@override
|
||||
Future<PendingTransaction> createTokenApproval(
|
||||
@@ -197,13 +206,14 @@ class CWPolygon extends Polygon {
|
||||
String spender,
|
||||
CryptoCurrency token,
|
||||
TransactionPriority priority,
|
||||
) => (wallet as EVMChainWallet).createApprovalTransaction(
|
||||
amount,
|
||||
spender,
|
||||
token,
|
||||
priority as EVMChainTransactionPriority,
|
||||
"POL",
|
||||
);
|
||||
) =>
|
||||
(wallet as EVMChainWallet).createApprovalTransaction(
|
||||
amount,
|
||||
spender,
|
||||
token,
|
||||
priority as EVMChainTransactionPriority,
|
||||
"POL",
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> setHardwareWalletService(WalletBase wallet, HardwareWalletService service) async {
|
||||
@@ -214,8 +224,8 @@ class CWPolygon extends Polygon {
|
||||
((wallet as EVMChainWallet).evmChainPrivateKey as EvmBitboxCredentials)
|
||||
.setBitbox(service.manager, (await wallet.walletInfo.getDerivationInfo()).derivationPath);
|
||||
} else if (service is EVMChainTrezorService) {
|
||||
((wallet as EVMChainWallet).evmChainPrivateKey as EvmTrezorCredentials)
|
||||
.setTrezorConnect(service.connect, (await wallet.walletInfo.getDerivationInfo()).derivationPath);
|
||||
((wallet as EVMChainWallet).evmChainPrivateKey as EvmTrezorCredentials).setTrezorConnect(
|
||||
service.connect, (await wallet.walletInfo.getDerivationInfo()).derivationPath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,4 +252,12 @@ class CWPolygon extends Polygon {
|
||||
(element) => element.contractAddress.toLowerCase() == contractAddress.toLowerCase(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String? getPolygonNativeEstimatedFee(WalletBase wallet) =>
|
||||
(wallet as EVMChainWallet).nativeTxEstimatedFee;
|
||||
|
||||
@override
|
||||
String? getPolygonERC20EstimatedFee(WalletBase wallet) =>
|
||||
(wallet as EVMChainWallet).erc20TxEstimatedFee;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'package:cake_wallet/arbitrum/arbitrum.dart';
|
||||
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';
|
||||
@@ -49,6 +50,10 @@ Future<void> startFiatRateUpdate(
|
||||
currencies =
|
||||
base!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
|
||||
}
|
||||
if (appStore.wallet!.type == WalletType.arbitrum) {
|
||||
currencies =
|
||||
arbitrum!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
|
||||
}
|
||||
|
||||
if (appStore.wallet!.type == WalletType.solana) {
|
||||
currencies =
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/arbitrum/arbitrum.dart';
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
@@ -133,6 +134,10 @@ void startCurrentWalletChangeReaction(
|
||||
currencies =
|
||||
base!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
|
||||
}
|
||||
if (wallet.type == WalletType.arbitrum) {
|
||||
currencies =
|
||||
arbitrum!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
|
||||
}
|
||||
if (wallet.type == WalletType.solana) {
|
||||
currencies =
|
||||
solana!.getSPLTokenCurrencies(appStore.wallet!).where((element) => element.enabled);
|
||||
|
||||
@@ -9,6 +9,7 @@ bool isEVMCompatibleChain(WalletType walletType) {
|
||||
case WalletType.polygon:
|
||||
case WalletType.ethereum:
|
||||
case WalletType.base:
|
||||
case WalletType.arbitrum:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -21,6 +22,7 @@ bool isNFTACtivatedChain(WalletType walletType) {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.base:
|
||||
case WalletType.solana:
|
||||
case WalletType.arbitrum:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -33,6 +35,7 @@ bool isWalletConnectCompatibleChain(WalletType walletType) {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.base:
|
||||
case WalletType.solana:
|
||||
case WalletType.arbitrum:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -47,6 +50,8 @@ String getChainNameSpaceAndIdBasedOnWalletType(WalletType walletType) {
|
||||
return EVMChainId.polygon.chain();
|
||||
case WalletType.base:
|
||||
return EVMChainId.base.chain();
|
||||
case WalletType.arbitrum:
|
||||
return EVMChainId.arbitrum.chain();
|
||||
case WalletType.solana:
|
||||
return SolanaChainId.mainnet.chain();
|
||||
default:
|
||||
@@ -59,6 +64,7 @@ List<String> getChainSupportedMethodsOnWalletType(WalletType walletType) {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.arbitrum:
|
||||
return EVMSupportedMethods.values.map((e) => e.name).toList();
|
||||
case WalletType.solana:
|
||||
return SolanaSupportedMethods.values.map((e) => e.name).toList();
|
||||
@@ -73,6 +79,8 @@ int getChainIdBasedOnWalletType(WalletType walletType) {
|
||||
return 137;
|
||||
case WalletType.base:
|
||||
return 8453;
|
||||
case WalletType.arbitrum:
|
||||
return 42161;
|
||||
// For now, we return eth chain Id as the default, we'll modify as we add more wallets
|
||||
case WalletType.ethereum:
|
||||
default:
|
||||
@@ -88,6 +96,8 @@ String getChainNameBasedOnWalletType(WalletType walletType) {
|
||||
return 'polygon';
|
||||
case WalletType.base:
|
||||
return 'base';
|
||||
case WalletType.arbitrum:
|
||||
return 'arbitrum';
|
||||
case WalletType.solana:
|
||||
return 'mainnet';
|
||||
default:
|
||||
@@ -103,6 +113,8 @@ String getTokenNameBasedOnWalletType(WalletType walletType) {
|
||||
return 'MATIC';
|
||||
case WalletType.base:
|
||||
return 'BASE';
|
||||
case WalletType.arbitrum:
|
||||
return 'ARB';
|
||||
case WalletType.solana:
|
||||
return 'SOL';
|
||||
default:
|
||||
|
||||
@@ -5,6 +5,7 @@ bool isBIP39Wallet(WalletType walletType) {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.arbitrum:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
case WalletType.bitcoin:
|
||||
@@ -43,6 +44,7 @@ bool hasTokens(WalletType walletType) {
|
||||
case WalletType.tron:
|
||||
case WalletType.zano:
|
||||
case WalletType.base:
|
||||
case WalletType.arbitrum:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
||||
@@ -39,6 +39,7 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
|
||||
final polygonIcon = Image.asset('assets/images/crypto/polygon.webp', height: 24, width: 24);
|
||||
final bitcoinCashIcon = Image.asset('assets/images/crypto/bitcoin-cash.webp', height: 24, width: 24);
|
||||
final baseIcon = Image.asset('assets/images/crypto/base_icon.webp', height: 24, width: 24);
|
||||
final arbitrumIcon = Image.asset('assets/images/crypto/arbitrum.webp', height: 24, width: 24);
|
||||
final nanoIcon = Image.asset('assets/images/crypto/nano.webp', height: 24, width: 24);
|
||||
final bananoIcon = Image.asset('assets/images/crypto/nano.webp', height: 24, width: 24);
|
||||
final solanaIcon = Image.asset('assets/images/crypto/solana.webp', height: 24, width: 24);
|
||||
@@ -187,6 +188,8 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
|
||||
return dogeIcon;
|
||||
case WalletType.base:
|
||||
return baseIcon;
|
||||
case WalletType.arbitrum:
|
||||
return arbitrumIcon;
|
||||
case WalletType.none:
|
||||
return nonWalletTypeIcon;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ class MenuWidgetState extends State<MenuWidget> {
|
||||
this.bitcoinCashIcon = Image.asset('assets/images/crypto/bitcoin-cash.webp'),
|
||||
this.polygonIcon = Image.asset('assets/images/crypto/polygon.webp'),
|
||||
this.baseIcon = Image.asset('assets/images/crypto/base_icon.webp'),
|
||||
this.arbitrumIcon = Image.asset('assets/images/crypto/arbitrum.webp'),
|
||||
this.solanaIcon = Image.asset('assets/images/crypto/solana.webp'),
|
||||
this.tronIcon = Image.asset('assets/images/crypto/tron.webp'),
|
||||
this.wowneroIcon = Image.asset('assets/images/crypto/wownero.webp'),
|
||||
@@ -62,6 +63,7 @@ class MenuWidgetState extends State<MenuWidget> {
|
||||
Image bananoIcon;
|
||||
Image polygonIcon;
|
||||
Image baseIcon;
|
||||
Image arbitrumIcon;
|
||||
Image solanaIcon;
|
||||
Image tronIcon;
|
||||
Image wowneroIcon;
|
||||
@@ -251,6 +253,8 @@ class MenuWidgetState extends State<MenuWidget> {
|
||||
return solanaIcon;
|
||||
case WalletType.base:
|
||||
return baseIcon;
|
||||
case WalletType.arbitrum:
|
||||
return arbitrumIcon;
|
||||
case WalletType.tron:
|
||||
return tronIcon;
|
||||
case WalletType.wownero:
|
||||
|
||||
@@ -60,7 +60,7 @@ class ExchangeTradeCardItemWidget extends StatelessWidget {
|
||||
FeeSelectionWidget(
|
||||
feesViewModel: feesViewModel,
|
||||
output: output,
|
||||
onTap: () => pickTransactionPriority(context),
|
||||
onTap: () => pickTransactionPriority(context, output),
|
||||
),
|
||||
if (exchangeTradeViewModel.sendViewModel.hasCoinControl)
|
||||
CoinControlWidget(
|
||||
@@ -75,7 +75,7 @@ class ExchangeTradeCardItemWidget extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> pickTransactionPriority(BuildContext context) async {
|
||||
Future<void> pickTransactionPriority(BuildContext context, Output output) async {
|
||||
final items = priorityForWalletType(feesViewModel.walletType);
|
||||
final selectedItem = items.indexOf(feesViewModel.transactionPriority);
|
||||
final customItemIndex = feesViewModel.getCustomPriorityIndex(items);
|
||||
@@ -102,10 +102,10 @@ class ExchangeTradeCardItemWidget extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
sliderValue: customFeeRate,
|
||||
onSliderChanged: (double newValue) => setState(() => customFeeRate = newValue),
|
||||
onItemSelected: (TransactionPriority priority) {
|
||||
onItemSelected: (TransactionPriority priority) async {
|
||||
feesViewModel.setTransactionPriority(priority);
|
||||
setState(() => selectedIdx = items.indexOf(priority));
|
||||
|
||||
await output.calculateEstimatedFee();
|
||||
if (feesViewModel.isLowFee) {
|
||||
_showFeeAlert(context);
|
||||
}
|
||||
|
||||
@@ -542,7 +542,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||
builder: (_) => GestureDetector(
|
||||
key: ValueKey('send_page_select_fee_priority_button_key'),
|
||||
onTap: sendViewModel.feesViewModel.hasFeesPriority
|
||||
? () => pickTransactionPriority(context)
|
||||
? () => pickTransactionPriority(context, output)
|
||||
: () {},
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(top: 24),
|
||||
@@ -799,9 +799,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||
}
|
||||
}
|
||||
|
||||
final parsedAddress = output.isParsedAddress
|
||||
? output.extractedAddress
|
||||
: output.address;
|
||||
final parsedAddress = output.isParsedAddress ? output.extractedAddress : output.address;
|
||||
|
||||
_lastHandledAddress = current;
|
||||
await _handlePaymentFlow(
|
||||
@@ -837,7 +835,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||
_effectsInstalled = true;
|
||||
}
|
||||
|
||||
Future<void> pickTransactionPriority(BuildContext context) async {
|
||||
Future<void> pickTransactionPriority(BuildContext context, Output output) async {
|
||||
final items = priorityForWalletType(sendViewModel.walletType);
|
||||
final selectedItem = items.indexOf(sendViewModel.feesViewModel.transactionPriority);
|
||||
final customItemIndex = sendViewModel.feesViewModel.getCustomPriorityIndex(items);
|
||||
@@ -867,9 +865,10 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
sliderValue: customFeeRate,
|
||||
onSliderChanged: (double newValue) => setState(() => customFeeRate = newValue),
|
||||
onItemSelected: (TransactionPriority priority) {
|
||||
onItemSelected: (TransactionPriority priority) async {
|
||||
sendViewModel.feesViewModel.setTransactionPriority(priority);
|
||||
setState(() => selectedIdx = items.indexOf(priority));
|
||||
await output.calculateEstimatedFee();
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
@@ -126,6 +126,14 @@ class PrivacyPage extends BasePage {
|
||||
_privacySettingsViewModel.setUseBaseScan(value);
|
||||
},
|
||||
),
|
||||
if (_privacySettingsViewModel.canUseArbiScan)
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.arbiscan_history,
|
||||
value: _privacySettingsViewModel.useArbiScan,
|
||||
onValueChange: (BuildContext _, bool value) {
|
||||
_privacySettingsViewModel.setUseArbiScan(value);
|
||||
},
|
||||
),
|
||||
if (_privacySettingsViewModel.canUseTronGrid)
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.trongrid_history,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/arbitrum/arbitrum.dart';
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cake_wallet/polygon/polygon.dart';
|
||||
@@ -26,6 +27,8 @@ class KeyServiceImpl implements WalletConnectKeyService {
|
||||
return polygon!.getPrivateKey(wallet);
|
||||
case WalletType.base:
|
||||
return base!.getPrivateKey(wallet);
|
||||
case WalletType.arbitrum:
|
||||
return arbitrum!.getPrivateKey(wallet);
|
||||
case WalletType.solana:
|
||||
return solana!.getPrivateKey(wallet);
|
||||
default:
|
||||
@@ -41,6 +44,8 @@ class KeyServiceImpl implements WalletConnectKeyService {
|
||||
return polygon!.getPublicKey(wallet);
|
||||
case WalletType.base:
|
||||
return base!.getPublicKey(wallet);
|
||||
case WalletType.arbitrum:
|
||||
return arbitrum!.getPublicKey(wallet);
|
||||
case WalletType.solana:
|
||||
return solana!.getPublicKey(wallet);
|
||||
default:
|
||||
|
||||
@@ -110,6 +110,7 @@ abstract class SettingsStoreBase with Store {
|
||||
required this.pinNativeTokenAtTop,
|
||||
required this.useEtherscan,
|
||||
required this.useBaseScan,
|
||||
required this.useArbiScan,
|
||||
required this.usePolygonScan,
|
||||
required this.useTronGrid,
|
||||
required this.useMempoolFeeAPI,
|
||||
@@ -467,6 +468,11 @@ abstract class SettingsStoreBase with Store {
|
||||
(_) => useBaseScan,
|
||||
(bool useBaseScan) =>
|
||||
_sharedPreferences.setBool(PreferencesKey.useBaseScan, useBaseScan));
|
||||
|
||||
reaction(
|
||||
(_) => useArbiScan,
|
||||
(bool useArbiScan) =>
|
||||
_sharedPreferences.setBool(PreferencesKey.useArbiScan, useArbiScan));
|
||||
|
||||
reaction((_) => useTronGrid,
|
||||
(bool useTronGrid) => _sharedPreferences.setBool(PreferencesKey.useTronGrid, useTronGrid));
|
||||
@@ -826,6 +832,9 @@ abstract class SettingsStoreBase with Store {
|
||||
@observable
|
||||
bool useBaseScan;
|
||||
|
||||
@observable
|
||||
bool useArbiScan;
|
||||
|
||||
@observable
|
||||
bool useTronGrid;
|
||||
|
||||
@@ -1067,6 +1076,7 @@ abstract class SettingsStoreBase with Store {
|
||||
final useEtherscan = sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true;
|
||||
final usePolygonScan = sharedPreferences.getBool(PreferencesKey.usePolygonScan) ?? true;
|
||||
final useBaseScan = sharedPreferences.getBool(PreferencesKey.useBaseScan) ?? true;
|
||||
final useArbiScan = sharedPreferences.getBool(PreferencesKey.useArbiScan) ?? true;
|
||||
final useTronGrid = sharedPreferences.getBool(PreferencesKey.useTronGrid) ?? true;
|
||||
final useMempoolFeeAPI = sharedPreferences.getBool(PreferencesKey.useMempoolFeeAPI) ?? true;
|
||||
final defaultNanoRep = sharedPreferences.getString(PreferencesKey.defaultNanoRep) ?? "";
|
||||
@@ -1113,6 +1123,7 @@ abstract class SettingsStoreBase with Store {
|
||||
final ethereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
|
||||
final polygonNodeId = sharedPreferences.getInt(PreferencesKey.currentPolygonNodeIdKey);
|
||||
final baseNodeId = sharedPreferences.getInt(PreferencesKey.currentBaseNodeIdKey);
|
||||
final arbitrumNodeId = sharedPreferences.getInt(PreferencesKey.currentArbitrumNodeIdKey);
|
||||
final nanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
|
||||
final nanoPowNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoPowNodeIdKey);
|
||||
final solanaNodeId = sharedPreferences.getInt(PreferencesKey.currentSolanaNodeIdKey);
|
||||
@@ -1135,6 +1146,8 @@ abstract class SettingsStoreBase with Store {
|
||||
nodeSource.values.firstWhereOrNull((e) => e.uriRaw == polygonDefaultNodeUri);
|
||||
final baseNode = nodeSource.get(baseNodeId) ??
|
||||
nodeSource.values.firstWhereOrNull((e) => e.uriRaw == baseDefaultNodeUri);
|
||||
final arbitrumNode = nodeSource.get(arbitrumNodeId) ??
|
||||
nodeSource.values.firstWhereOrNull((e) => e.uriRaw == arbitrumDefaultNodeUri);
|
||||
final bitcoinCashElectrumServer = nodeSource.get(bitcoinCashElectrumServerId) ??
|
||||
nodeSource.values.firstWhereOrNull((e) => e.uriRaw == cakeWalletBitcoinCashDefaultNodeUri);
|
||||
final nanoNode = nodeSource.get(nanoNodeId) ??
|
||||
@@ -1212,6 +1225,10 @@ abstract class SettingsStoreBase with Store {
|
||||
nodes[WalletType.base] = baseNode;
|
||||
}
|
||||
|
||||
if (arbitrumNode != null) {
|
||||
nodes[WalletType.arbitrum] = arbitrumNode;
|
||||
}
|
||||
|
||||
if (bitcoinCashElectrumServer != null) {
|
||||
nodes[WalletType.bitcoinCash] = bitcoinCashElectrumServer;
|
||||
}
|
||||
@@ -1391,6 +1408,7 @@ abstract class SettingsStoreBase with Store {
|
||||
useEtherscan: useEtherscan,
|
||||
usePolygonScan: usePolygonScan,
|
||||
useBaseScan: useBaseScan,
|
||||
useArbiScan: useArbiScan,
|
||||
useTronGrid: useTronGrid,
|
||||
useMempoolFeeAPI: useMempoolFeeAPI,
|
||||
defaultNanoRep: defaultNanoRep,
|
||||
@@ -1588,6 +1606,7 @@ abstract class SettingsStoreBase with Store {
|
||||
useEtherscan = sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true;
|
||||
usePolygonScan = sharedPreferences.getBool(PreferencesKey.usePolygonScan) ?? true;
|
||||
useBaseScan = sharedPreferences.getBool(PreferencesKey.useBaseScan) ?? true;
|
||||
useArbiScan = sharedPreferences.getBool(PreferencesKey.useArbiScan) ?? true;
|
||||
useTronGrid = sharedPreferences.getBool(PreferencesKey.useTronGrid) ?? true;
|
||||
useMempoolFeeAPI = sharedPreferences.getBool(PreferencesKey.useMempoolFeeAPI) ?? true;
|
||||
defaultNanoRep = sharedPreferences.getString(PreferencesKey.defaultNanoRep) ?? "";
|
||||
@@ -1619,6 +1638,7 @@ abstract class SettingsStoreBase with Store {
|
||||
final ethereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
|
||||
final polygonNodeId = sharedPreferences.getInt(PreferencesKey.currentPolygonNodeIdKey);
|
||||
final baseNodeId = sharedPreferences.getInt(PreferencesKey.currentBaseNodeIdKey);
|
||||
final arbitrumNodeId = sharedPreferences.getInt(PreferencesKey.currentArbitrumNodeIdKey);
|
||||
final nanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
|
||||
final solanaNodeId = sharedPreferences.getInt(PreferencesKey.currentSolanaNodeIdKey);
|
||||
final tronNodeId = sharedPreferences.getInt(PreferencesKey.currentTronNodeIdKey);
|
||||
@@ -1633,6 +1653,7 @@ abstract class SettingsStoreBase with Store {
|
||||
final ethereumNode = nodeSource.get(ethereumNodeId);
|
||||
final polygonNode = nodeSource.get(polygonNodeId);
|
||||
final baseNode = nodeSource.get(baseNodeId);
|
||||
final arbitrumNode = nodeSource.get(arbitrumNodeId);
|
||||
final bitcoinCashNode = nodeSource.get(bitcoinCashElectrumServerId);
|
||||
final nanoNode = nodeSource.get(nanoNodeId);
|
||||
final solanaNode = nodeSource.get(solanaNodeId);
|
||||
@@ -1670,6 +1691,10 @@ abstract class SettingsStoreBase with Store {
|
||||
nodes[WalletType.base] = baseNode;
|
||||
}
|
||||
|
||||
if (arbitrumNode != null) {
|
||||
nodes[WalletType.arbitrum] = arbitrumNode;
|
||||
}
|
||||
|
||||
if (bitcoinCashNode != null) {
|
||||
nodes[WalletType.bitcoinCash] = bitcoinCashNode;
|
||||
}
|
||||
@@ -1833,6 +1858,9 @@ abstract class SettingsStoreBase with Store {
|
||||
case WalletType.base:
|
||||
await _sharedPreferences.setInt(PreferencesKey.currentBaseNodeIdKey, node.key as int);
|
||||
break;
|
||||
case WalletType.arbitrum:
|
||||
await _sharedPreferences.setInt(PreferencesKey.currentArbitrumNodeIdKey, node.key as int);
|
||||
break;
|
||||
case WalletType.solana:
|
||||
await _sharedPreferences.setInt(PreferencesKey.currentSolanaNodeIdKey, node.key as int);
|
||||
break;
|
||||
|
||||
@@ -30,6 +30,8 @@ String getQrImage(WalletType type) {
|
||||
return 'assets/images/doge_chain_qr.svg';
|
||||
case WalletType.base:
|
||||
return 'assets/images/base_chain_QR.svg';
|
||||
case WalletType.arbitrum:
|
||||
return 'assets/images/arbitrum_chain_QR.svg';
|
||||
case WalletType.banano:
|
||||
case WalletType.haven:
|
||||
case WalletType.none:
|
||||
|
||||
@@ -116,6 +116,7 @@ class TokenUtilities {
|
||||
WalletType.ethereum => '${walletKey}_${Erc20Token.ethereumBoxName}',
|
||||
WalletType.polygon => '${walletKey}_${Erc20Token.polygonBoxName}',
|
||||
WalletType.base => '${walletKey}_${Erc20Token.baseBoxName}',
|
||||
WalletType.arbitrum => '${walletKey}_${Erc20Token.arbitrumBoxName}',
|
||||
_ => '${walletKey}_${Erc20Token.ethereumBoxName}',
|
||||
};
|
||||
|
||||
@@ -193,7 +194,7 @@ class TokenUtilities {
|
||||
}
|
||||
|
||||
// Arbitrum One
|
||||
if (title == 'arbitrum' || title == 'arb' || tag == 'arbitrum') {
|
||||
if (title == 'arbitrum' || title == 'arb' || tag == 'arb') {
|
||||
return 42161;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
|
||||
case WalletType.dogecoin:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.arbitrum:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
return true;
|
||||
@@ -81,6 +82,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
|
||||
WalletType.ethereum,
|
||||
WalletType.polygon,
|
||||
WalletType.base,
|
||||
WalletType.arbitrum,
|
||||
WalletType.tron,
|
||||
WalletType.solana,
|
||||
WalletType.monero,
|
||||
|
||||
@@ -167,6 +167,7 @@ abstract class BalanceViewModelBase with Store {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.arbitrum:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
return S.current.xmr_full_balance;
|
||||
|
||||
@@ -879,6 +879,7 @@ abstract class DashboardViewModelBase with Store {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.arbitrum:
|
||||
case WalletType.solana:
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:cake_wallet/arbitrum/arbitrum.dart';
|
||||
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';
|
||||
@@ -111,6 +112,17 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
);
|
||||
await base!.addErc20Token(_balanceViewModel.wallet, baseToken);
|
||||
}
|
||||
if (_balanceViewModel.wallet.type == WalletType.arbitrum) {
|
||||
final arbitrumToken = Erc20Token(
|
||||
name: token.name,
|
||||
symbol: token.title,
|
||||
decimal: token.decimals,
|
||||
contractAddress: contractAddress.toLowerCase(),
|
||||
iconPath: token.iconPath,
|
||||
isPotentialScam: token.isPotentialScam,
|
||||
);
|
||||
await arbitrum!.addErc20Token(_balanceViewModel.wallet, arbitrumToken);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.solana) {
|
||||
final splToken = token.copyWith(enabled: true);
|
||||
@@ -153,6 +165,10 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
return base!.isTokenAlreadyAdded(_balanceViewModel.wallet, contractAddress);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.arbitrum) {
|
||||
return arbitrum!.isTokenAlreadyAdded(_balanceViewModel.wallet, contractAddress);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.solana) {
|
||||
return solana!.isTokenAlreadyAdded(_balanceViewModel.wallet, contractAddress);
|
||||
}
|
||||
@@ -184,6 +200,10 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
await base!.deleteErc20Token(_balanceViewModel.wallet, token as Erc20Token);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.arbitrum) {
|
||||
await arbitrum!.deleteErc20Token(_balanceViewModel.wallet, token as Erc20Token);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.solana) {
|
||||
await solana!.deleteSPLToken(_balanceViewModel.wallet, token);
|
||||
}
|
||||
@@ -240,6 +260,8 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
break;
|
||||
case WalletType.base:
|
||||
defaultTokenAddresses = base!.getDefaultTokenContractAddresses();
|
||||
case WalletType.arbitrum:
|
||||
defaultTokenAddresses = arbitrum!.getDefaultTokenContractAddresses();
|
||||
break;
|
||||
case WalletType.solana:
|
||||
defaultTokenAddresses = solana!.getDefaultTokenContractAddresses();
|
||||
@@ -380,6 +402,10 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
return await base!.getErc20Token(_balanceViewModel.wallet, contractAddress);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.arbitrum) {
|
||||
return await arbitrum!.getErc20Token(_balanceViewModel.wallet, contractAddress);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.solana) {
|
||||
return await solana!.getSPLToken(_balanceViewModel.wallet, contractAddress);
|
||||
}
|
||||
@@ -426,6 +452,11 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
if (!value) base!.removeTokenTransactionsInHistory(_balanceViewModel.wallet, token);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.arbitrum) {
|
||||
arbitrum!.addErc20Token(_balanceViewModel.wallet, token as Erc20Token);
|
||||
if (!value) arbitrum!.removeTokenTransactionsInHistory(_balanceViewModel.wallet, token);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.solana) {
|
||||
final address = solana!.getTokenAddress(token);
|
||||
solana!.addSPLToken(_balanceViewModel.wallet, token, address);
|
||||
@@ -487,6 +518,14 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
..sort(_sortFunc));
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.arbitrum) {
|
||||
tokens.addAll(arbitrum!
|
||||
.getERC20Currencies(_balanceViewModel.wallet)
|
||||
.where((element) => _matchesSearchText(element))
|
||||
.toList()
|
||||
..sort(_sortFunc));
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.solana) {
|
||||
tokens.addAll(solana!
|
||||
.getSPLTokenCurrencies(_balanceViewModel.wallet)
|
||||
@@ -528,7 +567,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, Base or Solana Wallets.
|
||||
// The homes settings would only be displayed for either of Tron, EVM or Solana Wallets.
|
||||
if (address == null) return false;
|
||||
|
||||
return searchText.isEmpty ||
|
||||
@@ -558,11 +597,15 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
return base!.getTokenAddress(asset);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.arbitrum) {
|
||||
return arbitrum!.getTokenAddress(asset);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.zano) {
|
||||
return zano!.getZanoAssetAddress(asset);
|
||||
}
|
||||
|
||||
// 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).
|
||||
// We return null if it's neither Tron, EVM or Solana wallet (which is actually impossible because we only display home settings for either of these four wallets).
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/arbitrum/arbitrum.dart';
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
import 'package:cake_wallet/entities/balance_display_mode.dart';
|
||||
@@ -139,6 +140,10 @@ class TransactionListItem extends ActionListItem with Keyable {
|
||||
final asset = base!.assetOfTransaction(balanceViewModel.wallet, transaction);
|
||||
return asset;
|
||||
}
|
||||
if (balanceViewModel.wallet.type == WalletType.arbitrum) {
|
||||
final asset = arbitrum!.assetOfTransaction(balanceViewModel.wallet, transaction);
|
||||
return asset;
|
||||
}
|
||||
|
||||
if (balanceViewModel.wallet.type == WalletType.solana) {
|
||||
final asset = solana!.assetOfTransaction(balanceViewModel.wallet, transaction);
|
||||
@@ -199,6 +204,13 @@ class TransactionListItem extends ActionListItem with Keyable {
|
||||
cryptoAmount: base!.formatterBaseAmountToDouble(transaction: transaction),
|
||||
price: price);
|
||||
break;
|
||||
case WalletType.arbitrum:
|
||||
final asset = arbitrum!.assetOfTransaction(balanceViewModel.wallet, transaction);
|
||||
final price = balanceViewModel.fiatConvertationStore.prices[asset];
|
||||
amount = calculateFiatAmountRaw(
|
||||
cryptoAmount: arbitrum!.formatterArbitrumAmountToDouble(transaction: transaction),
|
||||
price: price);
|
||||
break;
|
||||
case WalletType.nano:
|
||||
amount = calculateFiatAmountRaw(
|
||||
cryptoAmount: double.parse(nanoUtil!.getRawAsUsableString(
|
||||
|
||||
@@ -18,6 +18,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/arbitrum/arbitrum.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';
|
||||
@@ -109,14 +110,10 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
@observable
|
||||
bool isSendable;
|
||||
|
||||
|
||||
bool get isSwapsXyzSendingEVMTokenSwap => (_provider is SwapsXyzExchangeProvider) &&
|
||||
(wallet.type == WalletType.ethereum &&
|
||||
wallet.currency != trade.from ||
|
||||
(wallet.type == WalletType.polygon &&
|
||||
wallet.currency != trade.from) ||
|
||||
(wallet.type == WalletType.base &&
|
||||
wallet.currency != trade.from));
|
||||
bool get isSwapsXyzSendingEVMTokenSwap =>
|
||||
(_provider is SwapsXyzExchangeProvider) &&
|
||||
isEVMCompatibleChain(wallet.type) &&
|
||||
wallet.currency != trade.from;
|
||||
|
||||
String get extraInfo => trade.extraId != null && trade.extraId!.isNotEmpty
|
||||
? '\n\n' + S.current.exchange_extra_info
|
||||
@@ -203,7 +200,8 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
|
||||
sendViewModel.selectedCryptoCurrency = selected;
|
||||
|
||||
final pendingTransaction = await sendViewModel.createTransaction(provider: _provider, trade: trade);
|
||||
final pendingTransaction =
|
||||
await sendViewModel.createTransaction(provider: _provider, trade: trade);
|
||||
|
||||
if (_provider is SwapsXyzExchangeProvider) {
|
||||
final hash = pendingTransaction?.evmTxHashFromRawHex ?? pendingTransaction?.id ?? '';
|
||||
@@ -336,6 +334,9 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
bool _isBaseToken() =>
|
||||
wallet.currency == CryptoCurrency.baseEth && tradeFrom?.tag == CryptoCurrency.baseEth.tag;
|
||||
|
||||
bool _isArbitrumToken() =>
|
||||
wallet.currency == CryptoCurrency.arbEth && tradeFrom?.tag == CryptoCurrency.arbEth.tag;
|
||||
|
||||
bool _isTronToken() =>
|
||||
wallet.currency == CryptoCurrency.trx && tradeFrom?.tag == CryptoCurrency.trx.title;
|
||||
|
||||
@@ -348,9 +349,10 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
_isPolygonToken() ||
|
||||
_isSplToken() ||
|
||||
_isTronToken() ||
|
||||
_isBaseToken();
|
||||
_isBaseToken() ||
|
||||
_isArbitrumToken();
|
||||
}
|
||||
|
||||
|
||||
Future<void> registerSwapsXyzTransaction() async {
|
||||
try {
|
||||
if (!(_provider is SwapsXyzExchangeProvider)) return;
|
||||
@@ -366,7 +368,9 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
return;
|
||||
}
|
||||
|
||||
final txHash = sendViewModel.pendingTransaction?.evmTxHashFromRawHex ?? sendViewModel.pendingTransaction?.id ?? '';
|
||||
final txHash = sendViewModel.pendingTransaction?.evmTxHashFromRawHex ??
|
||||
sendViewModel.pendingTransaction?.id ??
|
||||
'';
|
||||
|
||||
if (txHash.isEmpty) {
|
||||
printV('SwapsXyz: transaction register: skipped (txHash empty)');
|
||||
@@ -379,7 +383,8 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
return;
|
||||
}
|
||||
|
||||
printV('SwapsXyz: attempting to register transaction: tradeId = ${trade.id}, txHash = $txHash, chainId = $chainId, vmId = $vmId');
|
||||
printV(
|
||||
'SwapsXyz: attempting to register transaction: tradeId = ${trade.id}, txHash = $txHash, chainId = $chainId, vmId = $vmId');
|
||||
|
||||
final registered = await swaps.registerAltVmTx(
|
||||
txId: trade.id,
|
||||
@@ -423,6 +428,8 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
return PolygonURI(amount: amount, address: inputAddress);
|
||||
case WalletType.base:
|
||||
return BaseURI(amount: amount, address: inputAddress);
|
||||
case WalletType.arbitrum:
|
||||
return ArbitrumURI(amount: amount, address: inputAddress);
|
||||
case WalletType.solana:
|
||||
return SolanaURI(amount: amount, address: inputAddress);
|
||||
case WalletType.tron:
|
||||
|
||||
@@ -878,6 +878,10 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
depositCurrency = CryptoCurrency.baseEth;
|
||||
receiveCurrency = CryptoCurrency.xmr;
|
||||
break;
|
||||
case WalletType.arbitrum:
|
||||
depositCurrency = CryptoCurrency.arbEth;
|
||||
receiveCurrency = CryptoCurrency.xmr;
|
||||
break;
|
||||
case WalletType.solana:
|
||||
depositCurrency = CryptoCurrency.sol;
|
||||
receiveCurrency = CryptoCurrency.xmr;
|
||||
|
||||
@@ -81,6 +81,7 @@ abstract class NodeCreateOrEditViewModelBase with Store {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.arbitrum:
|
||||
case WalletType.solana:
|
||||
case WalletType.banano:
|
||||
case WalletType.nano:
|
||||
|
||||
@@ -5,7 +5,6 @@ import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'payment_view_model.g.dart';
|
||||
|
||||
@@ -32,6 +32,7 @@ class WalletRestoreFromQRCode {
|
||||
'ethereum-wallet': WalletType.ethereum,
|
||||
'polygon-wallet': WalletType.polygon,
|
||||
'base-wallet': WalletType.base,
|
||||
'arbitrum-wallet': WalletType.arbitrum,
|
||||
'nano-wallet': WalletType.nano,
|
||||
'nano_wallet': WalletType.nano,
|
||||
'bitcoincash': WalletType.bitcoinCash,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/arbitrum/arbitrum.dart';
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
@@ -102,6 +103,7 @@ abstract class FeesViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
case WalletType.banano:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
case WalletType.arbitrum:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -119,7 +121,8 @@ abstract class FeesViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
wallet.type != WalletType.nano &&
|
||||
wallet.type != WalletType.banano &&
|
||||
wallet.type != WalletType.solana &&
|
||||
wallet.type != WalletType.tron;
|
||||
wallet.type != WalletType.tron &&
|
||||
wallet.type != WalletType.arbitrum;
|
||||
|
||||
@computed
|
||||
bool get isElectrumWallet =>
|
||||
|
||||
@@ -28,6 +28,7 @@ import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/arbitrum/arbitrum.dart';
|
||||
|
||||
import 'package:cake_wallet/entities/contact_base.dart';
|
||||
|
||||
@@ -49,8 +50,15 @@ abstract class OutputBase with Store {
|
||||
address = '',
|
||||
note = '',
|
||||
extractedAddress = '',
|
||||
estimatedFee = 0.0,
|
||||
parsedAddress = ParsedAddress(addresses: []) {
|
||||
_setCryptoNumMaximumFractionDigits();
|
||||
autorun((_) {
|
||||
final status = _wallet.syncStatus;
|
||||
printV("Sync status changed to $status. Recalculating fees");
|
||||
|
||||
calculateEstimatedFee();
|
||||
});
|
||||
}
|
||||
|
||||
Key key;
|
||||
@@ -118,6 +126,9 @@ abstract class OutputBase with Store {
|
||||
case WalletType.base:
|
||||
_amount = base!.formatterBaseParseAmount(_cryptoAmount);
|
||||
break;
|
||||
case WalletType.arbitrum:
|
||||
_amount = arbitrum!.formatterArbitrumParseAmount(_cryptoAmount);
|
||||
break;
|
||||
case WalletType.wownero:
|
||||
_amount = wownero!.formatterWowneroParseAmount(amount: _cryptoAmount);
|
||||
break;
|
||||
@@ -145,28 +156,74 @@ abstract class OutputBase with Store {
|
||||
return amount;
|
||||
}
|
||||
|
||||
@computed
|
||||
double get estimatedFee {
|
||||
try {
|
||||
// forces mobx to rebuild the computed value
|
||||
final _ = _wallet.syncStatus;
|
||||
@observable
|
||||
double estimatedFee;
|
||||
|
||||
@action
|
||||
Future<void> calculateEstimatedFee() async {
|
||||
try {
|
||||
if (_wallet.type == WalletType.tron) {
|
||||
if (cryptoCurrencyHandler() == CryptoCurrency.trx) {
|
||||
final nativeEstimatedFee = tron!.getTronNativeEstimatedFee(_wallet) ?? 0;
|
||||
return double.parse(nativeEstimatedFee.toString());
|
||||
final nativeEstimatedFee = tron!.getTronNativeEstimatedFee(_wallet) ?? '0';
|
||||
estimatedFee = double.parse(nativeEstimatedFee);
|
||||
} else {
|
||||
final trc20EstimatedFee = tron!.getTronTRC20EstimatedFee(_wallet) ?? 0;
|
||||
return double.parse(trc20EstimatedFee.toString());
|
||||
final trc20EstimatedFee = tron!.getTronTRC20EstimatedFee(_wallet) ?? '0';
|
||||
estimatedFee = double.parse(trc20EstimatedFee);
|
||||
}
|
||||
}
|
||||
|
||||
if (isEVMCompatibleChain(_wallet.type)) {
|
||||
if (_wallet.type == WalletType.arbitrum) {
|
||||
String? fee = cryptoCurrencyHandler() == CryptoCurrency.arbEth
|
||||
? arbitrum!.getArbitrumNativeEstimatedFee(_wallet)
|
||||
: arbitrum!.getArbitrumERC20EstimatedFee(_wallet);
|
||||
|
||||
estimatedFee = arbitrum!
|
||||
.formatterArbitrumAmountToDouble(amount: BigInt.from(double.parse(fee ?? '0.0')));
|
||||
return;
|
||||
}
|
||||
|
||||
await _wallet.updateEstimatedFeesParams(_settingsStore.priority[_wallet.type]!);
|
||||
|
||||
double calculatedFee = 0.0;
|
||||
|
||||
if (_wallet.type == WalletType.ethereum) {
|
||||
String? fee = cryptoCurrencyHandler() == CryptoCurrency.eth
|
||||
? ethereum!.getEthereumNativeEstimatedFee(_wallet)
|
||||
: ethereum!.getEthereumERC20EstimatedFee(_wallet);
|
||||
|
||||
calculatedFee = ethereum!
|
||||
.formatterEthereumAmountToDouble(amount: BigInt.from(double.parse(fee ?? '0.0')));
|
||||
}
|
||||
|
||||
if (_wallet.type == WalletType.polygon) {
|
||||
String? fee = cryptoCurrencyHandler() == CryptoCurrency.maticpoly
|
||||
? polygon!.getPolygonNativeEstimatedFee(_wallet)
|
||||
: polygon!.getPolygonERC20EstimatedFee(_wallet);
|
||||
|
||||
calculatedFee = polygon!
|
||||
.formatterPolygonAmountToDouble(amount: BigInt.from(double.parse(fee ?? '0.0')));
|
||||
}
|
||||
|
||||
if (_wallet.type == WalletType.base) {
|
||||
String? fee = cryptoCurrencyHandler() == CryptoCurrency.baseEth
|
||||
? base!.getBaseNativeEstimatedFee(_wallet)
|
||||
: base!.getBaseERC20EstimatedFee(_wallet);
|
||||
calculatedFee =
|
||||
base!.formatterBaseAmountToDouble(amount: BigInt.from(double.parse(fee ?? '0.0')));
|
||||
}
|
||||
|
||||
estimatedFee = calculatedFee;
|
||||
}
|
||||
|
||||
if (_wallet.type == WalletType.solana) {
|
||||
return solana!.getEstimateFees(_wallet) ?? 0.0;
|
||||
estimatedFee = solana!.getEstimateFees(_wallet) ?? 0.0;
|
||||
}
|
||||
|
||||
int? fee = _wallet.calculateEstimatedFee(
|
||||
_settingsStore.priority[_wallet.type]!, formattedCryptoAmount);
|
||||
_settingsStore.priority[_wallet.type]!,
|
||||
formattedCryptoAmount,
|
||||
);
|
||||
|
||||
if (_wallet.type == WalletType.bitcoin) {
|
||||
if (_settingsStore.priority[_wallet.type] ==
|
||||
@@ -175,48 +232,34 @@ abstract class OutputBase with Store {
|
||||
_wallet, _settingsStore.customBitcoinFeeRate, formattedCryptoAmount);
|
||||
}
|
||||
|
||||
return bitcoin!.formatterBitcoinAmountToDouble(amount: fee);
|
||||
estimatedFee = bitcoin!.formatterBitcoinAmountToDouble(amount: fee);
|
||||
}
|
||||
|
||||
if (_wallet.type == WalletType.litecoin ||
|
||||
_wallet.type == WalletType.bitcoinCash ||
|
||||
_wallet.type == WalletType.dogecoin) {
|
||||
return bitcoin!.formatterBitcoinAmountToDouble(amount: fee);
|
||||
estimatedFee = bitcoin!.formatterBitcoinAmountToDouble(amount: fee);
|
||||
}
|
||||
|
||||
if (_wallet.type == WalletType.monero) {
|
||||
return monero!.formatterMoneroAmountToDouble(amount: fee);
|
||||
estimatedFee = monero!.formatterMoneroAmountToDouble(amount: fee);
|
||||
}
|
||||
|
||||
if (_wallet.type == WalletType.wownero) {
|
||||
return wownero!.formatterWowneroAmountToDouble(amount: fee);
|
||||
}
|
||||
|
||||
if (_wallet.type == WalletType.ethereum) {
|
||||
return ethereum!.formatterEthereumAmountToDouble(amount: BigInt.from(fee));
|
||||
}
|
||||
|
||||
if (_wallet.type == WalletType.polygon) {
|
||||
return polygon!.formatterPolygonAmountToDouble(amount: BigInt.from(fee));
|
||||
}
|
||||
|
||||
if (_wallet.type == WalletType.base) {
|
||||
return base!.formatterBaseAmountToDouble(amount: BigInt.from(fee));
|
||||
estimatedFee = wownero!.formatterWowneroAmountToDouble(amount: fee);
|
||||
}
|
||||
|
||||
if (_wallet.type == WalletType.zano) {
|
||||
return zano!.formatterIntAmountToDouble(
|
||||
estimatedFee = zano!.formatterIntAmountToDouble(
|
||||
amount: fee, currency: cryptoCurrencyHandler(), forFee: true);
|
||||
}
|
||||
|
||||
if (_wallet.type == WalletType.decred) {
|
||||
return decred!.formatterDecredAmountToDouble(amount: fee);
|
||||
estimatedFee = decred!.formatterDecredAmountToDouble(amount: fee);
|
||||
}
|
||||
} catch (e) {
|
||||
printV(e.toString());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@computed
|
||||
@@ -337,6 +380,7 @@ abstract class OutputBase with Store {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.arbitrum:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
case WalletType.haven:
|
||||
|
||||
@@ -57,6 +57,7 @@ abstract class SendTemplateViewModelBase with Store {
|
||||
_wallet.type != WalletType.ethereum &&
|
||||
_wallet.type != WalletType.polygon &&
|
||||
_wallet.type != WalletType.base &&
|
||||
_wallet.type != WalletType.arbitrum &&
|
||||
_wallet.type != WalletType.solana &&
|
||||
_wallet.type != WalletType.tron;
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/nano/nano.dart';
|
||||
import 'package:cake_wallet/polygon/polygon.dart';
|
||||
import 'package:cake_wallet/arbitrum/arbitrum.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
@@ -137,7 +138,6 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
bool get isMwebEnabled => balanceViewModel.mwebEnabled;
|
||||
|
||||
bool get isEVMWallet => isEVMCompatibleChain(walletType);
|
||||
|
||||
@action
|
||||
void setShowAddressBookPopup(bool value) {
|
||||
_settingsStore.showAddressBookPopupEnabled = value;
|
||||
@@ -223,6 +223,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.arbitrum:
|
||||
case WalletType.tron:
|
||||
case WalletType.solana:
|
||||
return wallet.currency;
|
||||
@@ -603,6 +604,26 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
priority,
|
||||
);
|
||||
|
||||
_isSwapsXYZCallDataTx = true;
|
||||
state = ExecutedSuccessfullyState();
|
||||
return pendingTransaction; // do NOT fall back to regular flow
|
||||
}
|
||||
if (walletType == WalletType.arbitrum) {
|
||||
_pendingApprovalTx = await buildApprovalIfNeeded(
|
||||
spender: routerTo!,
|
||||
tokenContract: tokenContract,
|
||||
requiredAmount: requiredAmount,
|
||||
sourceTokenDecimals: trade.sourceTokenDecimals,
|
||||
);
|
||||
|
||||
// Build the callData tx
|
||||
pendingTransaction = await arbitrum!.createRawCallDataTransaction(
|
||||
wallet,
|
||||
routerTo,
|
||||
routerData,
|
||||
routerValueWei,
|
||||
);
|
||||
|
||||
_isSwapsXYZCallDataTx = true;
|
||||
state = ExecutedSuccessfullyState();
|
||||
return pendingTransaction; // do NOT fall back to regular flow
|
||||
@@ -637,7 +658,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
return pendingTransaction;
|
||||
}
|
||||
if (walletType == WalletType.base) {
|
||||
final priority = _settingsStore.priority[WalletType.polygon]!;
|
||||
final priority = _settingsStore.priority[WalletType.base]!;
|
||||
pendingTransaction = await base!.createRawCallDataTransaction(
|
||||
wallet,
|
||||
routerTo!,
|
||||
@@ -649,6 +670,17 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
state = ExecutedSuccessfullyState();
|
||||
return pendingTransaction;
|
||||
}
|
||||
if (walletType == WalletType.arbitrum) {
|
||||
pendingTransaction = await arbitrum!.createRawCallDataTransaction(
|
||||
wallet,
|
||||
routerTo!,
|
||||
routerData,
|
||||
routerValueWei,
|
||||
);
|
||||
_isSwapsXYZCallDataTx = true;
|
||||
state = ExecutedSuccessfullyState();
|
||||
return pendingTransaction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -847,7 +879,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
wallet.type != WalletType.nano &&
|
||||
wallet.type != WalletType.banano &&
|
||||
wallet.type != WalletType.solana &&
|
||||
wallet.type != WalletType.tron) {
|
||||
wallet.type != WalletType.tron &&
|
||||
wallet.type != WalletType.arbitrum) {
|
||||
throw Exception('Priority is null for wallet type: ${wallet.type}');
|
||||
}
|
||||
|
||||
@@ -890,6 +923,9 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
case WalletType.base:
|
||||
return base!.createBaseTransactionCredentials(outputs,
|
||||
priority: priority!, currency: selectedCryptoCurrency);
|
||||
case WalletType.arbitrum:
|
||||
return arbitrum!
|
||||
.createArbitrumTransactionCredentials(outputs, currency: selectedCryptoCurrency);
|
||||
case WalletType.solana:
|
||||
return solana!
|
||||
.createSolanaTransactionCredentials(outputs, currency: selectedCryptoCurrency);
|
||||
@@ -1016,6 +1052,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
if (walletType == WalletType.ethereum ||
|
||||
walletType == WalletType.polygon ||
|
||||
walletType == WalletType.base ||
|
||||
walletType == WalletType.arbitrum ||
|
||||
walletType == WalletType.haven) {
|
||||
if (errorMessage.contains('gas required exceeds allowance')) {
|
||||
return S.current.gas_exceeds_allowance;
|
||||
@@ -1154,6 +1191,13 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
spender,
|
||||
requiredAmount,
|
||||
);
|
||||
} else if (walletType == WalletType.arbitrum) {
|
||||
needsApproval = await arbitrum!.isApprovalRequired(
|
||||
wallet,
|
||||
tokenContract,
|
||||
spender,
|
||||
requiredAmount,
|
||||
);
|
||||
}
|
||||
|
||||
if (!needsApproval) return null;
|
||||
@@ -1196,6 +1240,13 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
erc20Token,
|
||||
priority,
|
||||
);
|
||||
} else if (walletType == WalletType.arbitrum) {
|
||||
return await arbitrum!.createTokenApproval(
|
||||
wallet,
|
||||
requiredAmount,
|
||||
spender,
|
||||
erc20Token,
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/arbitrum/arbitrum.dart';
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
|
||||
@@ -79,6 +80,9 @@ abstract class PrivacySettingsViewModelBase with Store {
|
||||
@computed
|
||||
bool get useBaseScan => _settingsStore.useBaseScan;
|
||||
|
||||
@computed
|
||||
bool get useArbiScan => _settingsStore.useArbiScan;
|
||||
|
||||
@computed
|
||||
bool get useTronGrid => _settingsStore.useTronGrid;
|
||||
|
||||
@@ -118,6 +122,8 @@ abstract class PrivacySettingsViewModelBase with Store {
|
||||
|
||||
bool get canUseBaseScan => _wallet.type == WalletType.base;
|
||||
|
||||
bool get canUseArbiScan => _wallet.type == WalletType.arbitrum;
|
||||
|
||||
bool get canUseTronGrid => _wallet.type == WalletType.tron;
|
||||
|
||||
bool get canUseMempoolFeeAPI => _wallet.type == WalletType.bitcoin;
|
||||
@@ -195,6 +201,12 @@ abstract class PrivacySettingsViewModelBase with Store {
|
||||
tron!.updateTronGridUsageState(_wallet, value);
|
||||
}
|
||||
|
||||
@action
|
||||
void setUseArbiScan(bool value) {
|
||||
_settingsStore.useArbiScan = value;
|
||||
arbitrum!.updateArbitrumScanUsageState(_wallet, value);
|
||||
}
|
||||
|
||||
@action
|
||||
void setUseMempoolFeeAPI(bool value) => _settingsStore.useMempoolFeeAPI = value;
|
||||
|
||||
|
||||
@@ -77,6 +77,9 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
case WalletType.base:
|
||||
_addBaseListItems(tx, dateFormat);
|
||||
break;
|
||||
case WalletType.arbitrum:
|
||||
_addArbitrumListItems(tx, dateFormat);
|
||||
break;
|
||||
case WalletType.solana:
|
||||
_addSolanaListItems(tx, dateFormat);
|
||||
break;
|
||||
@@ -204,6 +207,8 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
return 'https://blockchair.com/dogecoin/transaction/${txId}';
|
||||
case WalletType.base:
|
||||
return 'https://basescan.org/tx/${txId}';
|
||||
case WalletType.arbitrum:
|
||||
return 'https://arbiscan.io/tx/${txId}';
|
||||
case WalletType.none:
|
||||
return '';
|
||||
}
|
||||
@@ -241,6 +246,8 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
return S.current.view_transaction_on + 'dcrdata.decred.org';
|
||||
case WalletType.base:
|
||||
return S.current.view_transaction_on + 'basescan.org';
|
||||
case WalletType.arbitrum:
|
||||
return S.current.view_transaction_on + 'arbiscan.io';
|
||||
case WalletType.none:
|
||||
return '';
|
||||
}
|
||||
@@ -606,6 +613,56 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
items.addAll(_items);
|
||||
}
|
||||
|
||||
void _addArbitrumListItems(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(
|
||||
|
||||
@@ -13,6 +13,7 @@ import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/polygon/polygon.dart';
|
||||
import 'package:cake_wallet/arbitrum/arbitrum.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_utils.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
@@ -146,6 +147,8 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
return DogeURI(amount: amount, address: address.address);
|
||||
case WalletType.base:
|
||||
return BaseURI(amount: amount, address: address.address);
|
||||
case WalletType.arbitrum:
|
||||
return ArbitrumURI(amount: amount, address: address.address);
|
||||
case WalletType.none:
|
||||
throw Exception('Unexpected type: ${type.toString()}');
|
||||
}
|
||||
@@ -272,6 +275,12 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.arbitrum) {
|
||||
final primaryAddress = arbitrum!.getAddress(wallet);
|
||||
|
||||
addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.solana) {
|
||||
final primaryAddress = solana!.getAddress(wallet);
|
||||
|
||||
@@ -450,6 +459,12 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
'assets/images/usdc_icon.svg',
|
||||
'assets/images/more_tokens.svg',
|
||||
];
|
||||
case WalletType.arbitrum:
|
||||
return [
|
||||
'assets/images/crypto/arbitrum.webp',
|
||||
'assets/images/usdc_icon.svg',
|
||||
'assets/images/more_tokens.svg',
|
||||
];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
@@ -473,6 +488,8 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
return 'assets/images/zano_chain_mono.svg';
|
||||
case WalletType.base:
|
||||
return 'assets/images/base_chain_mono.svg';
|
||||
case WalletType.arbitrum:
|
||||
return 'assets/images/arbitrum_chain_mono.svg';
|
||||
default:
|
||||
return 'assets/images/eth_chain_mono.svg';
|
||||
}
|
||||
|
||||
@@ -127,6 +127,7 @@ abstract class WalletKeysViewModelBase with Store {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.arbitrum:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
items.addAll([
|
||||
@@ -255,6 +256,8 @@ abstract class WalletKeysViewModelBase with Store {
|
||||
return 'polygon-wallet';
|
||||
case WalletType.base:
|
||||
return 'base-wallet';
|
||||
case WalletType.arbitrum:
|
||||
return 'arbitrum-wallet';
|
||||
case WalletType.solana:
|
||||
return 'solana-wallet';
|
||||
case WalletType.tron:
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/arbitrum/arbitrum.dart';
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/core/new_wallet_arguments.dart';
|
||||
import 'package:cake_wallet/dogecoin/dogecoin.dart';
|
||||
@@ -97,6 +98,13 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
|
||||
mnemonic: newWalletArguments!.mnemonic,
|
||||
passphrase: passphrase,
|
||||
);
|
||||
case WalletType.arbitrum:
|
||||
return arbitrum!.createArbitrumNewWalletCredentials(
|
||||
name: name,
|
||||
password: walletPassword,
|
||||
mnemonic: newWalletArguments!.mnemonic,
|
||||
passphrase: passphrase,
|
||||
);
|
||||
case WalletType.bitcoinCash:
|
||||
return bitcoinCash!.createBitcoinCashNewWalletCredentials(
|
||||
name: name,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/arbitrum/arbitrum.dart';
|
||||
import 'package:cake_wallet/base/base.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
||||
@@ -52,6 +53,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.arbitrum:
|
||||
case WalletType.decred:
|
||||
case WalletType.bitcoin:
|
||||
availableModes = [WalletRestoreMode.seed, WalletRestoreMode.keys];
|
||||
@@ -92,6 +94,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||
WalletType.ethereum,
|
||||
WalletType.polygon,
|
||||
WalletType.base,
|
||||
WalletType.arbitrum,
|
||||
WalletType.nano,
|
||||
WalletType.banano,
|
||||
WalletType.solana,
|
||||
@@ -183,6 +186,13 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||
password: password,
|
||||
passphrase: passphrase,
|
||||
);
|
||||
case WalletType.arbitrum:
|
||||
return arbitrum!.createArbitrumRestoreWalletFromSeedCredentials(
|
||||
name: name,
|
||||
mnemonic: seed,
|
||||
password: password,
|
||||
passphrase: passphrase,
|
||||
);
|
||||
case WalletType.solana:
|
||||
return solana!.createSolanaRestoreWalletFromSeedCredentials(
|
||||
name: name,
|
||||
@@ -276,6 +286,12 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||
password: password,
|
||||
privateKey: options['private_key'] as String,
|
||||
);
|
||||
case WalletType.arbitrum:
|
||||
return arbitrum!.createArbitrumRestoreWalletFromPrivateKey(
|
||||
name: name,
|
||||
password: password,
|
||||
privateKey: options['private_key'] as String,
|
||||
);
|
||||
case WalletType.solana:
|
||||
return solana!.createSolanaRestoreWalletFromPrivateKey(
|
||||
name: name,
|
||||
|
||||
@@ -237,6 +237,7 @@ flutter:
|
||||
- assets/decred_node_list.yml
|
||||
- assets/dogecoin_electrum_server_list.yml
|
||||
- assets/base_node_list.yml
|
||||
- assets/arbitrum_node_list.yml
|
||||
- assets/text/
|
||||
- assets/faq/
|
||||
- assets/animation/
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
"approve": "ﺪﻤﺘﻌﻳ",
|
||||
"approve_request": "الموافقة على الطلب",
|
||||
"approve_tokens": "الموافقة على الرموز",
|
||||
"arbiscan_history": "تاريخ أربيسكان",
|
||||
"arrive_in_this_address": "سيصل ${currency} ${tag}إلى هذا العنوان",
|
||||
"ascending": "تصاعدي",
|
||||
"ask_each_time": "اسأل في كل مرة",
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
"approve": "Одобряване",
|
||||
"approve_request": "Одобрете искане",
|
||||
"approve_tokens": "Одобрете жетоните",
|
||||
"arbiscan_history": "Арбисканска история",
|
||||
"arrive_in_this_address": "${currency} ${tag}ще отидат на този адрес",
|
||||
"ascending": "Възходящ",
|
||||
"ask_each_time": "Питайте всеки път",
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
"approve": "Schvalovat",
|
||||
"approve_request": "Schválit žádost",
|
||||
"approve_tokens": "Schválit tokeny",
|
||||
"arbiscan_history": "Arbiscan History",
|
||||
"arrive_in_this_address": "${currency} ${tag}přijde na tuto adresu",
|
||||
"ascending": "Vzestupné",
|
||||
"ask_each_time": "Zeptejte se pokaždé",
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
"approve": "Genehmigen",
|
||||
"approve_request": "Anfrage genehmigen",
|
||||
"approve_tokens": "Token genehmigen",
|
||||
"arbiscan_history": "ArbiScan-Geschichte",
|
||||
"arrive_in_this_address": "${currency} ${tag} wird an dieser Adresse ankommen",
|
||||
"ascending": "Aufsteigend",
|
||||
"ask_each_time": "Jedes Mal fragen",
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
"approve": "Approve",
|
||||
"approve_request": "Approve Request",
|
||||
"approve_tokens": "Approve tokens",
|
||||
"arbiscan_history": "ArbiScan History",
|
||||
"arrive_in_this_address": "${currency} ${tag}will arrive in this address",
|
||||
"ascending": "Ascending",
|
||||
"ask_each_time": "Ask each time",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user