CW-1250: Refactor L2 Chains Code (#2677)
What changed, and why it matters
This is a large code-refactoring commit that consolidates Ethereum and L2 chain support (Ethereum, Polygon, Base, Arbitrum) into a single unified EVM package. It also adds a new optional privacy feature called Blink that routes EVM transaction broadcasts through a third-party service. The commit is primarily an architectural refactor with many file moves and deletions, plus new UI for switching EVM chains. There is no explicit vendor statement that this fixes a security vulnerability, and the diff alone does not show a clear exploitable bug, but the scope and new RPC routing warrant careful review.
Treat this as a high-risk refactor rather than a confirmed vulnerability. Review the Blink RPC proxy implementation for privacy and integrity guarantees, verify that chain switching cannot leave stale balances or transaction histories accessible across chains, confirm Erc20Token equality changes do not break scam/spam filtering, and ensure the new blinkApiKey is not logged or exposed in build artifacts. Full security validation requires reviewing the new cw_evm source files, which are only partially shown in the diff.
Security signals we found
New third-party RPC routing (Blink) for EVM broadcasts added behind a privacy toggle
New blinkApiKey secret added to CI build workflows
Large refactor of EVM wallet/client architecture with deletion of old per-chain packages
ChainId-based node and token selection introduced
Erc20Token equality operator narrowed to contractAddress only
Transaction history file naming now depends on current chainId
No explicit security bug fix or CVE reference in commit message
Evidence from the diff
The commit refactors per-chain EVM packages (cw_ethereum, cw_polygon, cw_base, cw_arbitrum) into a single cw_evm package with a chain registry, unified wallet/client classes, and chainId-based dispatch. It introduces WalletType.evm while keeping legacy types functional, persists selected chainId, and adds a dashboard chain switcher. It also adds Blink integration: a privacy toggle that routes EVM broadcast RPC calls through Blink when enabled, plus a new blinkApiKey secret injected in CI workflows. The diff is mostly deletions of old packages and additions of consolidated code; security-relevant areas include the new Blink RPC proxy, chainId-based node/token selection, transaction history file naming, and Erc20Token equality changes.
Changed components
cw_evm package (new unified EVM chain registry, clients, wallet, transaction history, tokens)lib/evm/cw_evm.dart (new unified proxy)Dashboard and balance UI (EVM chain switcher)Privacy settings / advanced settings (Blink toggle)CI workflows (blinkApiKey secret injection)cw_core (WalletType.evm, chainId helpers, Erc20Token equality)Removed: cw_ethereum, cw_polygon, cw_base, cw_arbitrum packagesInspect captured patch +8427 / −8882
diff --git a/.github/workflows/automated_integration_test.yml b/.github/workflows/automated_integration_test.yml
index ac0fa6f..96d6ef1 100644
--- a/.github/workflows/automated_integration_test.yml
+++ b/.github/workflows/automated_integration_test.yml
@@ -66,7 +66,6 @@ jobs:
echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const nowNodesApiKey = '${{ secrets.EVM_NOWNODES_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
-
echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart
echo "const chainStackApiKey = '${{ secrets.CHAIN_STACK_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart
echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart
diff --git a/.github/workflows/pr_test_build_linux.yml b/.github/workflows/pr_test_build_linux.yml
index 7444be1..c3e7ecd 100644
--- a/.github/workflows/pr_test_build_linux.yml
+++ b/.github/workflows/pr_test_build_linux.yml
@@ -53,8 +53,8 @@ jobs:
echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
+ echo "const blinkApiKey = '${{ secrets.BLINK_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const nowNodesApiKey = '${{ secrets.EVM_NOWNODES_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
-
echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart
echo "const chainStackApiKey = '${{ secrets.CHAIN_STACK_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart
echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart
diff --git a/.github/workflows/reusable-build.yml b/.github/workflows/reusable-build.yml
index b184952..08bf24d 100644
--- a/.github/workflows/reusable-build.yml
+++ b/.github/workflows/reusable-build.yml
@@ -68,7 +68,7 @@ jobs:
echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const nowNodesApiKey = '${{ secrets.EVM_NOWNODES_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
-
+ echo "const blinkApiKey = '${{ secrets.BLINK_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart
echo "const chainStackApiKey = '${{ secrets.CHAIN_STACK_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart
echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart
diff --git a/.gitignore b/.gitignore
index cbe6227..5b17c01 100644
--- a/.gitignore
+++ b/.gitignore
@@ -143,6 +143,7 @@ lib/decred/decred.dart
lib/dogecoin/dogecoin.dart
lib/base/base.dart
lib/arbitrum/arbitrum.dart
+lib/evm/evm.dart
ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
ios/Runner/Assets.xcassets/AppIcon.appiconset/*.png
diff --git a/assets/images/evm_switcher.svg b/assets/images/evm_switcher.svg
new file mode 100644
index 0000000..18ed0cf
--- /dev/null
+++ b/assets/images/evm_switcher.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <path d="M15 0C23.2843 0 30 6.71573 30 15C30 23.2843 23.2843 30 15 30C6.71586 29.9999 6.59743e-07 23.2842 0 15C0 6.71582 6.71586 0.000149433 15 0ZM21.2227 16.3311L14.9941 20.0166L8.76758 16.3428C8.74823 16.3314 8.72327 16.3351 8.70801 16.3516C8.69296 16.3681 8.69129 16.3928 8.7041 16.4111L14.9561 25.2158C14.9619 25.224 14.9706 25.2302 14.9805 25.2334C14.9897 25.2362 15.0001 25.2364 15.0098 25.2334C15.0196 25.2302 15.0284 25.2239 15.0342 25.2158L21.2861 16.3994C21.2989 16.3811 21.2973 16.3563 21.2822 16.3398C21.267 16.3234 21.242 16.3198 21.2227 16.3311ZM15.0078 4.74902C14.9901 4.74424 14.9719 4.75125 14.96 4.76465C14.959 4.76576 14.957 4.76644 14.9561 4.76758C14.9554 4.76847 14.9547 4.76957 14.9541 4.77051L8.70215 15.1523C8.69578 15.1631 8.69428 15.1763 8.69727 15.1885C8.70041 15.2007 8.70794 15.2113 8.71875 15.2178L14.9678 18.9043C14.9692 18.9054 14.9701 18.9073 14.9717 18.9082C14.9864 18.9163 15.005 18.9157 15.0195 18.9072L21.2715 15.2188C21.2822 15.2123 21.2908 15.2015 21.2939 15.1895C21.297 15.1773 21.2944 15.1641 21.2881 15.1533L15.0361 4.77148L15.0342 4.76953C15.0296 4.76259 15.0232 4.75772 15.0156 4.75391C15.0143 4.75324 15.0131 4.75159 15.0117 4.75098C15.0104 4.75049 15.0091 4.74946 15.0078 4.74902Z" fill="#91B0FF"/>
+ <path d="M15 0C23.2843 0 30 6.71573 30 15C30 23.2843 23.2843 30 15 30C6.71586 29.9999 6.59743e-07 23.2842 0 15C0 6.71582 6.71586 0.000149433 15 0ZM21.2227 16.3311L14.9941 20.0166L8.76758 16.3428C8.74823 16.3314 8.72327 16.3351 8.70801 16.3516C8.69296 16.3681 8.69129 16.3928 8.7041 16.4111L14.9561 25.2158C14.9619 25.224 14.9706 25.2302 14.9805 25.2334C14.9897 25.2362 15.0001 25.2364 15.0098 25.2334C15.0196 25.2302 15.0284 25.2239 15.0342 25.2158L21.2861 16.3994C21.2989 16.3811 21.2973 16.3563 21.2822 16.3398C21.267 16.3234 21.242 16.3198 21.2227 16.3311ZM15.0078 4.74902C14.9901 4.74424 14.9719 4.75125 14.96 4.76465C14.959 4.76576 14.957 4.76644 14.9561 4.76758C14.9554 4.76847 14.9547 4.76957 14.9541 4.77051L8.70215 15.1523C8.69578 15.1631 8.69428 15.1763 8.69727 15.1885C8.70041 15.2007 8.70794 15.2113 8.71875 15.2178L14.9678 18.9043C14.9692 18.9054 14.9701 18.9073 14.9717 18.9082C14.9864 18.9163 15.005 18.9157 15.0195 18.9072L21.2715 15.2188C21.2822 15.2123 21.2908 15.2015 21.2939 15.1895C21.297 15.1773 21.2944 15.1641 21.2881 15.1533L15.0361 4.77148L15.0342 4.76953C15.0296 4.76259 15.0232 4.75772 15.0156 4.75391C15.0143 4.75324 15.0131 4.75159 15.0117 4.75098C15.0104 4.75049 15.0091 4.74946 15.0078 4.74902Z" fill="#91B0FF"/>
+</svg>
diff --git a/assets/images/evm_switcher_arrow_left.svg b/assets/images/evm_switcher_arrow_left.svg
new file mode 100644
index 0000000..dd12ab3
--- /dev/null
+++ b/assets/images/evm_switcher_arrow_left.svg
@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <path d="M10.65 14.6666L11.8334 13.4833L6.35004 7.99992L11.8334 2.51659L10.65 1.33325L3.98337 7.99992L10.65 14.6666Z" fill="#91B0FF"/>
+</svg>
diff --git a/assets/images/evm_switcher_arrow_right.svg b/assets/images/evm_switcher_arrow_right.svg
new file mode 100644
index 0000000..e73ba2a
--- /dev/null
+++ b/assets/images/evm_switcher_arrow_right.svg
@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <path d="M5.34996 14.6666L4.16663 13.4833L9.64996 7.99992L4.16663 2.51659L5.34996 1.33325L12.0166 7.99992L5.34996 14.6666Z" fill="#91B0FF"/>
+</svg>
diff --git a/assets/images/evm_switcher_checkmark.svg b/assets/images/evm_switcher_checkmark.svg
new file mode 100644
index 0000000..a634d87
--- /dev/null
+++ b/assets/images/evm_switcher_checkmark.svg
@@ -0,0 +1,10 @@
+<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <g clip-path="url(#clip0_10958_56952)">
+ <path d="M7.74 13.14L14.085 6.795L12.825 5.535L7.74 10.62L5.175 8.055L3.915 9.315L7.74 13.14ZM9 18C7.755 18 6.585 17.7638 5.49 17.2913C4.395 16.8188 3.4425 16.1775 2.6325 15.3675C1.8225 14.5575 1.18125 13.605 0.70875 12.51C0.23625 11.415 0 10.245 0 9C0 7.755 0.23625 6.585 0.70875 5.49C1.18125 4.395 1.8225 3.4425 2.6325 2.6325C3.4425 1.8225 4.395 1.18125 5.49 0.70875C6.585 0.23625 7.755 0 9 0C10.245 0 11.415 0.23625 12.51 0.70875C13.605 1.18125 14.5575 1.8225 15.3675 2.6325C16.1775 3.4425 16.8188 4.395 17.2913 5.49C17.7638 6.585 18 7.755 18 9C18 10.245 17.7638 11.415 17.2913 12.51C16.8188 13.605 16.1775 14.5575 15.3675 15.3675C14.5575 16.1775 13.605 16.8188 12.51 17.2913C11.415 17.7638 10.245 18 9 18Z" fill="#91B0FF"/>
+ </g>
+ <defs>
+ <clipPath id="clip0_10958_56952">
+ <rect width="18" height="18" fill="white"/>
+ </clipPath>
+ </defs>
+</svg>
diff --git a/assets/images/evm_switcher_icons/arbitrum.svg b/assets/images/evm_switcher_icons/arbitrum.svg
new file mode 100644
index 0000000..c072025
--- /dev/null
+++ b/assets/images/evm_switcher_icons/arbitrum.svg
@@ -0,0 +1,20 @@
+<svg width="15" height="16" viewBox="0 0 15 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <g clip-path="url(#clip0_10958_57031)">
+ <mask id="mask0_10958_57031" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-1" y="0" width="16" height="16">
+ <path d="M14.4978 0H-0.00219727V16H14.4978V0Z" fill="white"/>
+ </mask>
+ <g mask="url(#mask0_10958_57031)">
+ <path d="M8.46295 9.21579L7.66965 11.3397C7.64625 11.3986 7.64625 11.4641 7.66965 11.523L9.03195 15.1786L10.6085 14.2884L8.71735 9.21579C8.6738 9.09799 8.50645 9.09799 8.46295 9.21579Z" fill="#D7E2F7"/>
+ <path d="M10.0492 5.64206C10.0057 5.52426 9.8384 5.52426 9.7949 5.64206L9.0016 7.76601C8.97815 7.82496 8.97815 7.89041 9.0016 7.94931L11.2341 13.935L12.8107 13.0448L10.0492 5.64206Z" fill="#D7E2F7"/>
+ <path d="M7.2478 0.991615C7.28795 0.991615 7.3248 1.00143 7.3616 1.02107L13.3732 4.41481C13.4435 4.45408 13.487 4.52608 13.487 4.60463V11.3921C13.487 11.4707 13.4435 11.5426 13.3732 11.5819L7.3616 14.9789C7.32815 14.9985 7.28795 15.0084 7.2478 15.0084C7.20765 15.0084 7.1708 14.9985 7.134 14.9789L1.1258 11.5852C1.05551 11.5459 1.012 11.4739 1.012 11.3954V4.60463C1.012 4.52608 1.05551 4.45408 1.1258 4.41481L7.13735 1.02107C7.1708 1.00143 7.211 0.991615 7.2478 0.991615ZM7.2478 0C7.0336 0 6.81935 0.055635 6.62855 0.163632L0.617033 3.55737C0.235453 3.77337 -0.00219727 4.17264 -0.00219727 4.60463V11.3921C-0.00219727 11.8241 0.235453 12.2267 0.617033 12.4426L6.62855 15.8364C6.81935 15.9444 7.0336 16 7.2478 16C7.462 16 7.67625 15.9444 7.86705 15.8364L13.8786 12.4426C14.2635 12.2267 14.4978 11.8274 14.4978 11.3921V4.60463C14.4978 4.17264 14.2602 3.7701 13.8786 3.5541L7.8704 0.163632C7.67625 0.055635 7.462 0 7.2478 0Z" fill="#D7E2F7"/>
+ <path d="M3.27527 13.9416L3.82755 12.4624L4.93882 13.3657L3.90119 14.2951L3.27527 13.9416Z" fill="#D7E2F7"/>
+ <path d="M6.74233 4.12036H5.21938C5.10558 4.12036 5.00178 4.18909 4.96497 4.29381L1.69812 13.0514L3.27465 13.9416L6.87288 4.29709C6.90298 4.212 6.83938 4.12036 6.74233 4.12036Z" fill="#D7E2F7"/>
+ <path d="M9.40972 4.12036H7.88677C7.77297 4.12036 7.66917 4.18909 7.63237 4.29381L3.90027 14.295L5.47677 15.1852L9.53692 4.30036C9.57037 4.212 9.50347 4.12036 9.40972 4.12036Z" fill="#D7E2F7"/>
+ </g>
+ </g>
+ <defs>
+ <clipPath id="clip0_10958_57031">
+ <rect width="14.5" height="16" fill="white"/>
+ </clipPath>
+ </defs>
+</svg>
diff --git a/assets/images/evm_switcher_icons/base.svg b/assets/images/evm_switcher_icons/base.svg
new file mode 100644
index 0000000..6d51c92
--- /dev/null
+++ b/assets/images/evm_switcher_icons/base.svg
@@ -0,0 +1,10 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <g clip-path="url(#clip0_10958_57048)">
+ <path d="M7.986 15.9951C12.412 15.9951 16 12.4144 16 7.99754C16 3.58061 12.412 0 7.986 0C3.78691 0 0.342123 3.22294 0 7.32525H10.5926V8.66975H0C0.342123 12.7721 3.78691 15.9951 7.986 15.9951Z" fill="#D7E2F7"/>
+ </g>
+ <defs>
+ <clipPath id="clip0_10958_57048">
+ <rect width="16" height="16" fill="white"/>
+ </clipPath>
+ </defs>
+</svg>
diff --git a/assets/images/evm_switcher_icons/ethereum.svg b/assets/images/evm_switcher_icons/ethereum.svg
new file mode 100644
index 0000000..b011052
--- /dev/null
+++ b/assets/images/evm_switcher_icons/ethereum.svg
@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <path d="M12.8702 9.05127C12.8821 9.06418 12.8832 9.08379 12.8732 9.09814L7.99036 15.9819C7.9859 15.9888 7.9801 15.9948 7.9718 15.9976C7.95636 16.0024 7.93919 15.9971 7.92981 15.9839L3.047 9.10791C3.03691 9.09358 3.03808 9.07396 3.04993 9.06104C3.06172 9.04833 3.08079 9.04563 3.09583 9.0542L7.95911 11.9224L12.8243 9.04443C12.8394 9.03582 12.8584 9.03861 12.8702 9.05127ZM7.97375 0.00244141C7.98122 0.00535592 7.98798 0.0100481 7.99231 0.0170898L12.8751 8.12549C12.8802 8.13389 12.8813 8.14429 12.879 8.15381C12.8766 8.16331 12.8708 8.17216 12.8624 8.17725L7.97864 11.0581C7.96719 11.0646 7.953 11.0647 7.94153 11.0581L3.05872 8.17725C3.05028 8.17216 3.04355 8.16336 3.04114 8.15381C3.03885 8.14433 3.04004 8.13387 3.04504 8.12549L7.92786 0.0170898C7.92847 0.0160823 7.92911 0.0150907 7.92981 0.0141602C7.93158 0.011717 7.93532 0.0112365 7.93762 0.00927734C7.94096 0.00664239 7.94338 0.00287135 7.94739 0.00146484C7.94835 0.00110723 7.94932 0.000765826 7.95032 0.000488281H7.96985L7.97375 0.00244141Z" fill="#D7E2F7"/>
+</svg>
diff --git a/assets/images/evm_switcher_icons/gnosis.svg b/assets/images/evm_switcher_icons/gnosis.svg
new file mode 100644
index 0000000..d9347d6
--- /dev/null
+++ b/assets/images/evm_switcher_icons/gnosis.svg
@@ -0,0 +1,18 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <g clip-path="url(#clip0_10958_57056)">
+ <mask id="mask0_10958_57056" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="16" height="16">
+ <path d="M15.9701 0.0297852H0V15.9999H15.9701V0.0297852Z" fill="white"/>
+ </mask>
+ <g mask="url(#mask0_10958_57056)">
+ <path d="M2.62806 7.0571C2.62078 6.59436 2.77125 6.14292 3.05473 5.7771L5.96459 8.68697C5.59783 8.96865 5.14704 9.1189 4.68459 9.11363C4.13987 9.11139 3.6181 8.89401 3.2329 8.50881C2.84771 8.12362 2.63032 7.60185 2.62806 7.0571Z" fill="#D7E2F7"/>
+ <path d="M11.2851 9.11148C11.5576 9.11261 11.8276 9.05981 12.0795 8.95613C12.3315 8.85245 12.5604 8.69996 12.7531 8.5074C12.9459 8.31485 13.0986 8.08607 13.2026 7.83423C13.3065 7.58237 13.3596 7.31246 13.3587 7.04001C13.3638 6.57761 13.2136 6.12688 12.9321 5.76001L10.0137 8.67841C10.3764 8.96219 10.8246 9.11483 11.2851 9.11148Z" fill="#D7E2F7"/>
+ <path d="M13.5359 5.1692C13.9845 5.69601 14.231 6.36528 14.2313 7.0572C14.2308 7.83538 13.9213 8.58147 13.3708 9.13153C12.8204 9.68156 12.0741 9.99053 11.2959 9.99053C10.6076 9.99132 9.94111 9.74876 9.41428 9.30573L8.00202 10.718L6.58975 9.30573C6.06256 9.74955 5.39515 9.99218 4.70602 9.99053C4.31982 9.99194 3.93712 9.91713 3.57989 9.77037C3.22265 9.6236 2.89789 9.40779 2.6242 9.1353C2.35052 8.86279 2.13328 8.53897 1.98496 8.18239C1.83665 7.82578 1.76016 7.44342 1.75988 7.0572C1.76044 6.36845 2.0028 5.70179 2.44468 5.17347L1.78548 4.51427L1.15616 3.87427C0.39805 5.12071 -0.00201957 6.55192 -0.000115304 8.0108C-0.000395751 9.0594 0.205975 10.0978 0.607189 11.0666C1.0084 12.0354 1.5966 12.9156 2.33817 13.657C3.07973 14.3983 3.96014 14.9863 4.92904 15.3873C5.89795 15.7882 6.93635 15.9943 7.98495 15.9937C10.1011 15.9932 12.1305 15.1529 13.6276 13.6574C15.1247 12.1618 15.9672 10.1333 15.97 8.0172C15.9832 6.55856 15.587 5.12547 14.8265 3.88067L13.5359 5.1692Z" fill="#D7E2F7"/>
+ <path d="M13.7684 2.50445C13.0238 1.72155 12.1276 1.0984 11.1344 0.672961C10.1412 0.247521 9.07185 0.0286866 7.99138 0.0297893C6.91067 0.0292664 5.84115 0.248353 4.8477 0.67375C3.85425 1.09915 2.95759 1.72199 2.21218 2.50445C2.01805 2.71778 1.82819 2.93112 1.65112 3.15938L7.98498 9.49112L14.3188 3.15298C14.1516 2.92354 13.9677 2.70677 13.7684 2.50445ZM7.99138 8.01272L3.08471 3.10605C3.72623 2.45771 4.4906 1.94377 5.3331 1.59432C6.17559 1.24486 7.07929 1.06691 7.99138 1.07086C8.90363 1.06583 9.80766 1.2433 10.6503 1.59281C11.4929 1.94232 12.2572 2.45683 12.898 3.10605L7.99138 8.01272Z" fill="#D7E2F7"/>
+ </g>
+ </g>
+ <defs>
+ <clipPath id="clip0_10958_57056">
+ <rect width="16" height="16" fill="white"/>
+ </clipPath>
+ </defs>
+</svg>
diff --git a/assets/images/evm_switcher_icons/polygon.svg b/assets/images/evm_switcher_icons/polygon.svg
new file mode 100644
index 0000000..65335cf
--- /dev/null
+++ b/assets/images/evm_switcher_icons/polygon.svg
@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <path d="M11.2511 1.05078L7.04255 3.46781V11.0115L4.72052 12.3575L2.38428 11.0104V8.31716L4.72052 6.98309L6.22272 7.85424V5.67523L4.70742 4.81497L0.5 7.25927V12.0944L4.72161 14.5257L8.92904 12.0944V4.55187L11.2652 3.20471L13.6004 4.55187V7.23311L11.2652 8.59227L9.74999 7.71343V9.88154L11.2511 10.7473L15.5 8.33024V3.46781L11.2511 1.05078Z" fill="#D7E2F7"/>
+</svg>
diff --git a/cakewallet.bat b/cakewallet.bat
index 5870452..d601741 100644
--- a/cakewallet.bat
+++ b/cakewallet.bat
@@ -1,5 +1,5 @@
@echo off
-set cw_win_app_config=--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --dogecoin --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%
diff --git a/cw_arbitrum/.gitignore b/cw_arbitrum/.gitignore
deleted file mode 100644
index eb6c05c..0000000
--- a/cw_arbitrum/.gitignore
+++ /dev/null
@@ -1,31 +0,0 @@
-# Miscellaneous
-*.class
-*.log
-*.pyc
-*.swp
-.DS_Store
-.atom/
-.buildlog/
-.history
-.svn/
-migrate_working_dir/
-
-# IntelliJ related
-*.iml
-*.ipr
-*.iws
-.idea/
-
-# The .vscode folder contains launch configuration and tasks you configure in
-# VS Code which you may wish to be included in version control, so this line
-# is commented out by default.
-#.vscode/
-
-# Flutter/Dart/Pub related
-# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
-/pubspec.lock
-**/doc/api/
-.dart_tool/
-.flutter-plugins
-.flutter-plugins-dependencies
-build/
diff --git a/cw_arbitrum/.metadata b/cw_arbitrum/.metadata
deleted file mode 100644
index 6f8d1aa..0000000
--- a/cw_arbitrum/.metadata
+++ /dev/null
@@ -1,10 +0,0 @@
-# This file tracks properties of this Flutter project.
-# Used by Flutter tool to assess capabilities and perform upgrades etc.
-#
-# This file should be version controlled and should not be manually edited.
-
-version:
- revision: "fcf2c11572af6f390246c056bc905eca609533a0"
- channel: "[user-branch]"
-
-project_type: package
diff --git a/cw_arbitrum/CHANGELOG.md b/cw_arbitrum/CHANGELOG.md
deleted file mode 100644
index 41cc7d8..0000000
--- a/cw_arbitrum/CHANGELOG.md
+++ /dev/null
@@ -1,3 +0,0 @@
-## 0.0.1
-
-* TODO: Describe initial release.
diff --git a/cw_arbitrum/LICENSE b/cw_arbitrum/LICENSE
deleted file mode 100644
index ba75c69..0000000
--- a/cw_arbitrum/LICENSE
+++ /dev/null
@@ -1 +0,0 @@
-TODO: Add your license here.
diff --git a/cw_arbitrum/README.md b/cw_arbitrum/README.md
deleted file mode 100644
index 4a260d8..0000000
--- a/cw_arbitrum/README.md
+++ /dev/null
@@ -1,39 +0,0 @@
-<!--
-This README describes the package. If you publish this package to pub.dev,
-this README's contents appear on the landing page for your package.
-
-For information about how to write a good package README, see the guide for
-[writing package pages](https://dart.dev/tools/pub/writing-package-pages).
-
-For general information about developing packages, see the Dart guide for
-[creating packages](https://dart.dev/guides/libraries/create-packages)
-and the Flutter guide for
-[developing packages and plugins](https://flutter.dev/to/develop-packages).
--->
-
-TODO: Put a short description of the package here that helps potential users
-know whether this package might be useful for them.
-
-## Features
-
-TODO: List what your package can do. Maybe include images, gifs, or videos.
-
-## Getting started
-
-TODO: List prerequisites and provide or point to information on how to
-start using the package.
-
-## Usage
-
-TODO: Include short and useful examples for package users. Add longer examples
-to `/example` folder.
-
-```dart
-const like = 'sample';
-```
-
-## Additional information
-
-TODO: Tell users more about the package: where to find more information, how to
-contribute to the package, how to file issues, what response they can expect
-from the package authors, and more.
diff --git a/cw_arbitrum/analysis_options.yaml b/cw_arbitrum/analysis_options.yaml
deleted file mode 100644
index a5744c1..0000000
--- a/cw_arbitrum/analysis_options.yaml
+++ /dev/null
@@ -1,4 +0,0 @@
-include: package:flutter_lints/flutter.yaml
-
-# Additional information about this file can be found at
-# https://dart.dev/guides/language/analysis-options
diff --git a/cw_arbitrum/lib/arbirtrum_mnemonics_exception.dart b/cw_arbitrum/lib/arbirtrum_mnemonics_exception.dart
deleted file mode 100644
index 8af5612..0000000
--- a/cw_arbitrum/lib/arbirtrum_mnemonics_exception.dart
+++ /dev/null
@@ -1,5 +0,0 @@
-class ArbitrumMnemonicIsIncorrectException implements Exception {
- @override
- String toString() =>
- 'Arbitrum mnemonic has incorrect format. Mnemonic should contain 12 or 24 words separated by space.';
-}
diff --git a/cw_arbitrum/lib/arbitrum_client.dart b/cw_arbitrum/lib/arbitrum_client.dart
deleted file mode 100644
index f9fb36d..0000000
--- a/cw_arbitrum/lib/arbitrum_client.dart
+++ /dev/null
@@ -1,107 +0,0 @@
-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 [];
- }
- }
-}
diff --git a/cw_arbitrum/lib/arbitrum_transaction_history.dart b/cw_arbitrum/lib/arbitrum_transaction_history.dart
deleted file mode 100644
index 1319c7a..0000000
--- a/cw_arbitrum/lib/arbitrum_transaction_history.dart
+++ /dev/null
@@ -1,20 +0,0 @@
-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);
-}
diff --git a/cw_arbitrum/lib/arbitrum_transaction_info.dart b/cw_arbitrum/lib/arbitrum_transaction_info.dart
deleted file mode 100644
index d124999..0000000
--- a/cw_arbitrum/lib/arbitrum_transaction_info.dart
+++ /dev/null
@@ -1,41 +0,0 @@
-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';
-}
diff --git a/cw_arbitrum/lib/arbitrum_wallet.dart b/cw_arbitrum/lib/arbitrum_wallet.dart
deleted file mode 100644
index 3968056..0000000
--- a/cw_arbitrum/lib/arbitrum_wallet.dart
+++ /dev/null
@@ -1,183 +0,0 @@
-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_transaction_priority.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
- int getTotalPriorityFee(EVMChainTransactionPriority priority) => 0;
-
- @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,
- );
- }
-}
diff --git a/cw_arbitrum/lib/arbitrum_wallet_service.dart b/cw_arbitrum/lib/arbitrum_wallet_service.dart
deleted file mode 100644
index 91e0b86..0000000
--- a/cw_arbitrum/lib/arbitrum_wallet_service.dart
+++ /dev/null
@@ -1,174 +0,0 @@
-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();
- }
-}
diff --git a/cw_arbitrum/lib/cw_arbitrum.dart b/cw_arbitrum/lib/cw_arbitrum.dart
deleted file mode 100644
index 298576d..0000000
--- a/cw_arbitrum/lib/cw_arbitrum.dart
+++ /dev/null
@@ -1,5 +0,0 @@
-/// A Calculator.
-class Calculator {
- /// Returns [value] plus 1.
- int addOne(int value) => value + 1;
-}
diff --git a/cw_arbitrum/lib/default_arbitrum_erc20_tokens.dart b/cw_arbitrum/lib/default_arbitrum_erc20_tokens.dart
deleted file mode 100644
index 25cc573..0000000
--- a/cw_arbitrum/lib/default_arbitrum_erc20_tokens.dart
+++ /dev/null
@@ -1,71 +0,0 @@
-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();
-}
diff --git a/cw_arbitrum/pubspec.yaml b/cw_arbitrum/pubspec.yaml
deleted file mode 100644
index f6cb365..0000000
--- a/cw_arbitrum/pubspec.yaml
+++ /dev/null
@@ -1,73 +0,0 @@
-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
diff --git a/cw_arbitrum/test/cw_arbitrum_test.dart b/cw_arbitrum/test/cw_arbitrum_test.dart
deleted file mode 100644
index 5cf3562..0000000
--- a/cw_arbitrum/test/cw_arbitrum_test.dart
+++ /dev/null
@@ -1,12 +0,0 @@
-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);
- });
-}
diff --git a/cw_base/.gitignore b/cw_base/.gitignore
deleted file mode 100644
index eb6c05c..0000000
--- a/cw_base/.gitignore
+++ /dev/null
@@ -1,31 +0,0 @@
-# Miscellaneous
-*.class
-*.log
-*.pyc
-*.swp
-.DS_Store
-.atom/
-.buildlog/
-.history
-.svn/
-migrate_working_dir/
-
-# IntelliJ related
-*.iml
-*.ipr
-*.iws
-.idea/
-
-# The .vscode folder contains launch configuration and tasks you configure in
-# VS Code which you may wish to be included in version control, so this line
-# is commented out by default.
-#.vscode/
-
-# Flutter/Dart/Pub related
-# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
-/pubspec.lock
-**/doc/api/
-.dart_tool/
-.flutter-plugins
-.flutter-plugins-dependencies
-build/
diff --git a/cw_base/.metadata b/cw_base/.metadata
deleted file mode 100644
index 6f8d1aa..0000000
--- a/cw_base/.metadata
+++ /dev/null
@@ -1,10 +0,0 @@
-# This file tracks properties of this Flutter project.
-# Used by Flutter tool to assess capabilities and perform upgrades etc.
-#
-# This file should be version controlled and should not be manually edited.
-
-version:
- revision: "fcf2c11572af6f390246c056bc905eca609533a0"
- channel: "[user-branch]"
-
-project_type: package
diff --git a/cw_base/CHANGELOG.md b/cw_base/CHANGELOG.md
deleted file mode 100644
index 41cc7d8..0000000
--- a/cw_base/CHANGELOG.md
+++ /dev/null
@@ -1,3 +0,0 @@
-## 0.0.1
-
-* TODO: Describe initial release.
diff --git a/cw_base/LICENSE b/cw_base/LICENSE
deleted file mode 100644
index ba75c69..0000000
--- a/cw_base/LICENSE
+++ /dev/null
@@ -1 +0,0 @@
-TODO: Add your license here.
diff --git a/cw_base/README.md b/cw_base/README.md
deleted file mode 100644
index 4a260d8..0000000
--- a/cw_base/README.md
+++ /dev/null
@@ -1,39 +0,0 @@
-<!--
-This README describes the package. If you publish this package to pub.dev,
-this README's contents appear on the landing page for your package.
-
-For information about how to write a good package README, see the guide for
-[writing package pages](https://dart.dev/tools/pub/writing-package-pages).
-
-For general information about developing packages, see the Dart guide for
-[creating packages](https://dart.dev/guides/libraries/create-packages)
-and the Flutter guide for
-[developing packages and plugins](https://flutter.dev/to/develop-packages).
--->
-
-TODO: Put a short description of the package here that helps potential users
-know whether this package might be useful for them.
-
-## Features
-
-TODO: List what your package can do. Maybe include images, gifs, or videos.
-
-## Getting started
-
-TODO: List prerequisites and provide or point to information on how to
-start using the package.
-
-## Usage
-
-TODO: Include short and useful examples for package users. Add longer examples
-to `/example` folder.
-
-```dart
-const like = 'sample';
-```
-
-## Additional information
-
-TODO: Tell users more about the package: where to find more information, how to
-contribute to the package, how to file issues, what response they can expect
-from the package authors, and more.
diff --git a/cw_base/analysis_options.yaml b/cw_base/analysis_options.yaml
deleted file mode 100644
index a5744c1..0000000
--- a/cw_base/analysis_options.yaml
+++ /dev/null
@@ -1,4 +0,0 @@
-include: package:flutter_lints/flutter.yaml
-
-# Additional information about this file can be found at
-# https://dart.dev/guides/language/analysis-options
diff --git a/cw_base/devtools_options.yaml b/cw_base/devtools_options.yaml
deleted file mode 100644
index fa0b357..0000000
--- a/cw_base/devtools_options.yaml
+++ /dev/null
@@ -1,3 +0,0 @@
-description: This file stores settings for Dart & Flutter DevTools.
-documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
-extensions:
diff --git a/cw_base/lib/base_client.dart b/cw_base/lib/base_client.dart
deleted file mode 100644
index 4cf7bc9..0000000
--- a/cw_base/lib/base_client.dart
+++ /dev/null
@@ -1,103 +0,0 @@
-import 'dart:convert';
-
-import 'package:cw_evm/evm_chain_client.dart';
-import 'package:cw_evm/.secrets.g.dart' as secrets;
-import 'package:cw_evm/evm_chain_transaction_model.dart';
-import 'package:flutter/foundation.dart';
-import 'package:web3dart/web3dart.dart';
-
-class BaseClient extends EVMChainClient {
- @override
- Transaction createTransaction({
- required EthereumAddress from,
- required EthereumAddress to,
- required EtherAmount amount,
- EtherAmount? maxPriorityFeePerGas,
- Uint8List? data,
- int? maxGas,
- EtherAmount? gasPrice,
- EtherAmount? maxFeePerGas,
- }) {
- EtherAmount? finalGasPrice = gasPrice;
-
- if (gasPrice == null && maxFeePerGas != null) {
- // If we have EIP-1559 parameters but no legacy gasPrice, then use maxFeePerGas as gasPrice
- finalGasPrice = maxFeePerGas;
- }
-
- return Transaction(
- from: from,
- to: to,
- value: amount,
- data: data,
- maxGas: maxGas,
- gasPrice: finalGasPrice,
- // maxFeePerGas: maxFeePerGas,
- // maxPriorityFeePerGas: maxPriorityFeePerGas,
- );
- }
-
- @override
- Uint8List prepareSignedTransactionForSending(Uint8List signedTransaction) => signedTransaction;
-
- @override
- int get chainId => 8453;
-
- @override
- Future<List<EVMChainTransactionModel>> fetchTransactions(String address,
- {String? contractAddress}) async {
- try {
- final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
- "chainid": "$chainId",
- "module": "account",
- "action": contractAddress != null ? "tokentx" : "txlist",
- if (contractAddress != null) "contractaddress": contractAddress,
- "address": address,
- "apikey": secrets.etherScanApiKey,
- }));
-
- final jsonResponse = json.decode(response.body) as Map<String, dynamic>;
-
- if (response.statusCode >= 200 && response.statusCode < 300 && jsonResponse['status'] != 0) {
- final res = (jsonResponse['result'] as List);
-
- res.removeWhere((e) => e['value'] == '0');
-
- return res
- .map(
- (e) => EVMChainTransactionModel.fromJson(e as Map<String, dynamic>, 'ETH'),
- )
- .toList();
- }
-
- return [];
- } catch (e) {
- return [];
- }
- }
-
- @override
- Future<List<EVMChainTransactionModel>> fetchInternalTransactions(String address) async {
- try {
- final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
- "chainid": "$chainId",
- "module": "account",
- "action": "txlistinternal",
- "address": address,
- "apikey": secrets.etherScanApiKey,
- }));
-
- final jsonResponse = json.decode(response.body) as Map<String, dynamic>;
-
- if (response.statusCode >= 200 && response.statusCode < 300 && jsonResponse['status'] != 0) {
- return (jsonResponse['result'] as List)
- .map((e) => EVMChainTransactionModel.fromJson(e as Map<String, dynamic>, 'ETH'))
- .toList();
- }
-
- return [];
- } catch (_) {
- return [];
- }
- }
-}
diff --git a/cw_base/lib/base_mnemonics_exception.dart b/cw_base/lib/base_mnemonics_exception.dart
deleted file mode 100644
index 83f8403..0000000
--- a/cw_base/lib/base_mnemonics_exception.dart
+++ /dev/null
@@ -1,5 +0,0 @@
-class BaseMnemonicIsIncorrectException implements Exception {
- @override
- String toString() =>
- 'Base mnemonic has incorrect format. Mnemonic should contain 12 or 24 words separated by space.';
-}
diff --git a/cw_base/lib/base_transaction_history.dart b/cw_base/lib/base_transaction_history.dart
deleted file mode 100644
index 42d93a1..0000000
--- a/cw_base/lib/base_transaction_history.dart
+++ /dev/null
@@ -1,20 +0,0 @@
-import 'dart:core';
-
-import 'package:cw_evm/evm_chain_transaction_history.dart';
-import 'package:cw_evm/evm_chain_transaction_info.dart';
-import 'package:cw_base/base_transaction_info.dart';
-
-class BaseTransactionHistory extends EVMChainTransactionHistory {
- BaseTransactionHistory({
- required super.walletInfo,
- required super.password,
- required super.encryptionFileUtils,
- });
-
- @override
- String getTransactionHistoryFileName() => 'base_transactions.json';
-
- @override
- EVMChainTransactionInfo getTransactionInfo(Map<String, dynamic> val) =>
- BaseTransactionInfo.fromJson(val);
-}
diff --git a/cw_base/lib/base_transaction_info.dart b/cw_base/lib/base_transaction_info.dart
deleted file mode 100644
index 858c4fc..0000000
--- a/cw_base/lib/base_transaction_info.dart
+++ /dev/null
@@ -1,41 +0,0 @@
-import 'package:cw_core/transaction_direction.dart';
-import 'package:cw_evm/evm_chain_transaction_info.dart';
-
-class BaseTransactionInfo extends EVMChainTransactionInfo {
- BaseTransactionInfo({
- required super.id,
- required super.height,
- required super.ethAmount,
- required super.ethFee,
- required super.tokenSymbol,
- required super.direction,
- required super.isPending,
- required super.date,
- required super.confirmations,
- required super.to,
- required super.from,
- super.contractAddress,
- super.exponent,
- });
-
- factory BaseTransactionInfo.fromJson(Map<String, dynamic> data) {
- return BaseTransactionInfo(
- id: data['id'] as String,
- height: data['height'] as int,
- ethAmount: BigInt.parse(data['amount']),
- exponent: data['exponent'] as int,
- ethFee: BigInt.parse(data['fee']),
- direction: parseTransactionDirectionFromInt(data['direction'] as int),
- date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
- isPending: data['isPending'] as bool,
- confirmations: data['confirmations'] as int,
- tokenSymbol: data['tokenSymbol'] as String,
- to: data['to'],
- from: data['from'],
- contractAddress: data['contractAddress'],
- );
- }
-
- @override
- String get feeCurrency => 'ETH';
-}
diff --git a/cw_base/lib/base_wallet.dart b/cw_base/lib/base_wallet.dart
deleted file mode 100644
index a54ace6..0000000
--- a/cw_base/lib/base_wallet.dart
+++ /dev/null
@@ -1,180 +0,0 @@
-import 'dart:convert';
-
-import 'package:cw_base/base_client.dart';
-import 'package:cw_base/base_transaction_history.dart';
-import 'package:cw_base/base_transaction_info.dart';
-import 'package:cw_base/default_base_erc20_tokens.dart';
-import 'package:cw_core/cake_hive.dart';
-import 'package:cw_core/crypto_currency.dart';
-import 'package:cw_core/encryption_file_utils.dart';
-import 'package:cw_core/erc20_token.dart';
-import 'package:cw_core/pathForWallet.dart';
-import 'package:cw_core/transaction_direction.dart';
-import 'package:cw_core/wallet_info.dart';
-import 'package:cw_core/wallet_keys_file.dart';
-import 'package:cw_evm/evm_chain_transaction_history.dart';
-import 'package:cw_evm/evm_chain_transaction_info.dart';
-import 'package:cw_evm/evm_chain_transaction_model.dart';
-import 'package:cw_evm/evm_chain_transaction_priority.dart';
-import 'package:cw_evm/evm_chain_wallet.dart';
-import 'package:cw_evm/evm_erc20_balance.dart';
-import 'package:web3dart/web3dart.dart';
-
-class BaseWallet extends EVMChainWallet {
- BaseWallet({
- 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.baseEth);
-
- @override
- int getTotalPriorityFee(EVMChainTransactionPriority priority) {
- return switch (priority) {
- EVMChainTransactionPriority.fast => EtherAmount.fromInt(EtherUnit.mwei, 5).getInWei.toInt(),
- EVMChainTransactionPriority.medium => EtherAmount.fromInt(EtherUnit.mwei, 3).getInWei.toInt(),
- EVMChainTransactionPriority.slow => EtherAmount.fromInt(EtherUnit.mwei, 1).getInWei.toInt(),
- _ => EtherAmount.fromInt(EtherUnit.mwei, 1).getInWei.toInt(),
- };
- }
-
- @override
- Future<void> initErc20TokensBox() async {
- final boxName = "${walletInfo.name.replaceAll(" ", "_")}_${Erc20Token.baseBoxName}";
-
- evmChainErc20TokensBox = await CakeHive.openBox<Erc20Token>(boxName);
- }
-
- @override
- void addInitialTokens() {
- final initialErc20Tokens = DefaultBaseErc20Tokens().initialBaseErc20Tokens;
-
- 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 =>
- DefaultBaseErc20Tokens().initialBaseErc20Tokens.map((e) => e.contractAddress).toList();
-
- @override
- Future<bool> checkIfScanProviderIsEnabled() async {
- return (await sharedPrefs.future).getBool("use_basescan") ?? true;
- }
-
- @override
- String getTransactionHistoryFileName() => 'base_transactions.json';
-
- @override
- Erc20Token createNewErc20TokenObject(Erc20Token token, String? iconPath) {
- return Erc20Token(
- name: token.name,
- symbol: token.symbol,
- contractAddress: token.contractAddress,
- decimal: token.decimal,
- enabled: token.enabled,
- tag: token.tag ?? 'ETH',
- iconPath: iconPath,
- isPotentialScam: token.isPotentialScam,
- );
- }
-
- @override
- EVMChainTransactionInfo getTransactionInfo(
- EVMChainTransactionModel transactionModel, String address) {
- final model = BaseTransactionInfo(
- id: transactionModel.hash,
- height: transactionModel.blockNumber,
- ethAmount: transactionModel.amount,
- direction: transactionModel.from == address
- ? TransactionDirection.outgoing
- : TransactionDirection.incoming,
- isPending: false,
- date: transactionModel.date,
- confirmations: transactionModel.confirmations,
- ethFee: BigInt.from(transactionModel.gasUsed) * transactionModel.gasPrice,
- exponent: transactionModel.tokenDecimal ?? 18,
- tokenSymbol: transactionModel.tokenSymbol ?? "ETH",
- to: transactionModel.to,
- from: transactionModel.from,
- contractAddress: transactionModel.contractAddress,
- );
- return model;
- }
-
- @override
- EVMChainTransactionHistory setUpTransactionHistory(
- WalletInfo walletInfo, String password, EncryptionFileUtils encryptionFileUtils) {
- return BaseTransactionHistory(
- walletInfo: walletInfo,
- password: password,
- encryptionFileUtils: encryptionFileUtils,
- );
- }
-
- static Future<BaseWallet> open({
- required String name,
- required String password,
- required WalletInfo walletInfo,
- required EncryptionFileUtils encryptionFileUtils,
- }) async {
- final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type);
- final path = await pathForWallet(name: name, type: walletInfo.type);
-
- Map<String, dynamic>? data;
- try {
- final jsonSource = await encryptionFileUtils.read(path: path, password: password);
-
- data = json.decode(jsonSource) as Map<String, dynamic>;
- } catch (e) {
- if (!hasKeysFile) rethrow;
- }
-
- final balance = EVMChainERC20Balance.fromJSON(data?['balance'] as String?) ??
- EVMChainERC20Balance(BigInt.zero);
-
- final WalletKeysData keysData;
- // Migrate wallet from the old scheme to then new .keys file scheme
- if (!hasKeysFile) {
- final mnemonic = data!['mnemonic'] as String?;
- final privateKey = data['private_key'] as String?;
- final passphrase = data['passphrase'] as String?;
-
- keysData = WalletKeysData(mnemonic: mnemonic, privateKey: privateKey, passphrase: passphrase);
- } else {
- keysData = await WalletKeysFile.readKeysFile(
- name,
- walletInfo.type,
- password,
- encryptionFileUtils,
- );
- }
-
- return BaseWallet(
- walletInfo: walletInfo,
- derivationInfo: await walletInfo.getDerivationInfo(),
- password: password,
- mnemonic: keysData.mnemonic,
- privateKey: keysData.privateKey,
- passphrase: keysData.passphrase,
- initialBalance: balance,
- client: BaseClient(),
- encryptionFileUtils: encryptionFileUtils,
- );
- }
-}
diff --git a/cw_base/lib/base_wallet_service.dart b/cw_base/lib/base_wallet_service.dart
deleted file mode 100644
index 7457cbd..0000000
--- a/cw_base/lib/base_wallet_service.dart
+++ /dev/null
@@ -1,172 +0,0 @@
-import 'package:bip39/bip39.dart' as bip39;
-import 'package:cw_core/encryption_file_utils.dart';
-import 'package:cw_core/wallet_base.dart';
-import 'package:cw_core/wallet_info.dart';
-import 'package:cw_core/wallet_type.dart';
-import 'package:cw_evm/evm_chain_wallet_creation_credentials.dart';
-import 'package:cw_evm/evm_chain_wallet_service.dart';
-import 'package:cw_base/base_wallet.dart';
-import 'package:cw_base/base_client.dart';
-import 'package:cw_base/base_mnemonics_exception.dart';
-
-class BaseWalletService extends EVMChainWalletService<BaseWallet> {
- BaseWalletService(
- super.isDirect, {
- required this.client,
- });
-
- late BaseClient client;
-
- @override
- WalletType getType() => WalletType.base;
-
- @override
- Future<BaseWallet> create(EVMChainNewWalletCredentials credentials, {bool? isTestnet}) async {
- final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
-
- final mnemonic = credentials.mnemonic ?? bip39.generateMnemonic(strength: strength);
-
- final wallet = BaseWallet(
- walletInfo: credentials.walletInfo!,
- 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<BaseWallet> 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 BaseWallet.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 BaseWallet.open(
- name: name,
- password: password,
- walletInfo: walletInfo,
- encryptionFileUtils: encryptionFileUtilsFor(isDirect),
- );
-
- await wallet.init();
- wallet.addInitialTokens();
- await wallet.save();
- return wallet;
- }
- }
-
- @override
- Future<BaseWallet> restoreFromKeys(EVMChainRestoreWalletFromPrivateKey credentials,
- {bool? isTestnet}) async {
- final wallet = BaseWallet(
- password: credentials.password!,
- privateKey: credentials.privateKey,
- walletInfo: credentials.walletInfo!,
- derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
- client: client,
- encryptionFileUtils: encryptionFileUtilsFor(isDirect),
- );
-
- await wallet.init();
- wallet.addInitialTokens();
- await wallet.save();
- return wallet;
- }
-
- @override
- Future<BaseWallet> 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 = BaseWallet(
- 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<BaseWallet> restoreFromSeed(EVMChainRestoreWalletFromSeedCredentials credentials,
- {bool? isTestnet}) async {
- if (!bip39.validateMnemonic(credentials.mnemonic)) {
- throw BaseMnemonicIsIncorrectException();
- }
-
- final wallet = BaseWallet(
- password: credentials.password!,
- mnemonic: credentials.mnemonic,
- walletInfo: credentials.walletInfo!,
- 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 BaseWallet.open(
- password: password,
- name: currentName,
- walletInfo: currentWalletInfo,
- encryptionFileUtils: encryptionFileUtilsFor(isDirect),
- );
-
- await currentWallet.renameWalletFiles(newName);
- await saveBackup(newName);
-
- final newWalletInfo = currentWalletInfo;
- newWalletInfo.id = WalletBase.idFor(newName, getType());
- newWalletInfo.name = newName;
-
- newWalletInfo.save();
- }
-}
diff --git a/cw_base/lib/cw_base.dart b/cw_base/lib/cw_base.dart
deleted file mode 100644
index 298576d..0000000
--- a/cw_base/lib/cw_base.dart
+++ /dev/null
@@ -1,5 +0,0 @@
-/// A Calculator.
-class Calculator {
- /// Returns [value] plus 1.
- int addOne(int value) => value + 1;
-}
diff --git a/cw_base/lib/default_base_erc20_tokens.dart b/cw_base/lib/default_base_erc20_tokens.dart
deleted file mode 100644
index 9677183..0000000
--- a/cw_base/lib/default_base_erc20_tokens.dart
+++ /dev/null
@@ -1,71 +0,0 @@
-import 'package:cw_core/crypto_currency.dart';
-import 'package:cw_core/erc20_token.dart';
-
-class DefaultBaseErc20Tokens {
- final List<Erc20Token> _defaultTokens = [
- Erc20Token(
- name: "USD Coin",
- symbol: "USDC",
- contractAddress: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
- decimal: 6,
- enabled: true,
- ),
- Erc20Token(
- name: "USDe",
- symbol: "USDe",
- contractAddress: "0x5d3a1Ff2b6BAb83b63cd9AD0787074081a52ef34",
- decimal: 18,
- enabled: true,
- ),
- Erc20Token(
- name: "Dai",
- symbol: "DAI",
- contractAddress: "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
- decimal: 18,
- enabled: true,
- ),
- Erc20Token(
- name: "Bridged Tether USD",
- symbol: "USDT",
- contractAddress: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2",
- decimal: 6,
- enabled: true,
- ),
- Erc20Token(
- name: "Wrapped Ether",
- symbol: "WETH",
- contractAddress: "0x4200000000000000000000000000000000000006",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Wrapped BTC",
- symbol: "WBTC",
- contractAddress: "0x0555E30da8f98308EdB960aa94C0Db47230d2B9c",
- decimal: 8,
- enabled: false,
- ),
- Erc20Token(
- name: "SPX6900",
- symbol: "SPX",
- contractAddress: "0x50dA645f148798F68EF2d7dB7C1CB22A6819bb2C",
- decimal: 8,
- enabled: false,
- ),
- ];
-
- List<Erc20Token> get initialBaseErc20Tokens => _defaultTokens.map((token) {
- String? iconPath;
- 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: 'BASE');
- }).toList();
-}
diff --git a/cw_base/pubspec.yaml b/cw_base/pubspec.yaml
deleted file mode 100644
index f94468b..0000000
--- a/cw_base/pubspec.yaml
+++ /dev/null
@@ -1,73 +0,0 @@
-name: cw_base
-description: "Base package for Cake Wallet"
-version: 0.0.1
-publish_to: none
-homepage: https://cakewallet.com
-
-environment:
- sdk: '>=3.0.6 <4.0.0'
- flutter: ">=1.17.0"
-
-dependencies:
- flutter:
- sdk: flutter
- cw_core:
- path: ../cw_core
- cw_ethereum:
- path: ../cw_ethereum
- cw_evm:
- path: ../cw_evm
- web3dart: ^2.7.1
- hive: ^2.2.3
- bip39: ^1.0.6
- collection: ^1.17.1
-
-dependency_overrides:
- web3dart:
- git:
- url: https://github.com/cake-tech/web3dart.git
- ref: cake
- watcher: ^1.1.0
-
-dev_dependencies:
- flutter_test:
- sdk: flutter
- flutter_lints: ^2.0.0
- build_runner: ^2.4.15
-
-# For information on the generic Dart part of this file, see the
-# following page: https://dart.dev/tools/pub/pubspec
-
-# The following section is specific to Flutter packages.
-flutter:
-
- # To add assets to your package, add an assets section, like this:
- # assets:
- # - images/a_dot_burr.jpeg
- # - images/a_dot_ham.jpeg
- #
- # For details regarding assets in packages, see
- # https://flutter.dev/to/asset-from-package
- #
- # An image asset can refer to one or more resolution-specific "variants", see
- # https://flutter.dev/to/resolution-aware-images
-
- # To add custom fonts to your package, add a fonts section here,
- # in this "flutter" section. Each entry in this list should have a
- # "family" key with the font family name, and a "fonts" key with a
- # list giving the asset and other descriptors for the font. For
- # example:
- # fonts:
- # - family: Schyler
- # fonts:
- # - asset: fonts/Schyler-Regular.ttf
- # - asset: fonts/Schyler-Italic.ttf
- # style: italic
- # - family: Trajan Pro
- # fonts:
- # - asset: fonts/TrajanPro.ttf
- # - asset: fonts/TrajanPro_Bold.ttf
- # weight: 700
- #
- # For details regarding fonts in packages, see
- # https://flutter.dev/to/font-from-package
diff --git a/cw_base/test/cw_base_test.dart b/cw_base/test/cw_base_test.dart
deleted file mode 100644
index 31f5980..0000000
--- a/cw_base/test/cw_base_test.dart
+++ /dev/null
@@ -1,12 +0,0 @@
-import 'package:flutter_test/flutter_test.dart';
-
-import 'package:cw_base/cw_base.dart';
-
-void main() {
- test('adds one to input values', () {
- final calculator = Calculator();
- expect(calculator.addOne(2), 3);
- expect(calculator.addOne(-7), -6);
- expect(calculator.addOne(0), 1);
- });
-}
diff --git a/cw_core/lib/crypto_currency.dart b/cw_core/lib/crypto_currency.dart
index d3f9faf..b581e80 100644
--- a/cw_core/lib/crypto_currency.dart
+++ b/cw_core/lib/crypto_currency.dart
@@ -250,7 +250,8 @@ 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 const arbEth = CryptoCurrency(title: 'ETH', tag: 'ARB', fullName: 'Ethereum (Arbitrum One)', raw: 106, name: 'arbeth', iconPath: 'assets/images/crypto/ethereum.webp', decimals: 18);
+ static const usdcArb = CryptoCurrency(title: 'USDC', tag: 'ARB', fullName: 'USDC Coin', raw: 107, name: 'usdcarb', iconPath: 'assets/images/crypto/usdc.webp', decimals: 6);
static final Map<int, CryptoCurrency> _rawCurrencyMap =
[...all, ...havenCurrencies].fold<Map<int, CryptoCurrency>>(<int, CryptoCurrency>{}, (acc, item) {
@@ -289,6 +290,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
'shibainu': shib,
'zcash': zec,
'base': baseEth,
+ 'arbitrum': arbEth,
};
static CryptoCurrency deserialize({required int raw}) {
diff --git a/cw_core/lib/currency_for_wallet_type.dart b/cw_core/lib/currency_for_wallet_type.dart
index fe6899c..e09abed 100644
--- a/cw_core/lib/currency_for_wallet_type.dart
+++ b/cw_core/lib/currency_for_wallet_type.dart
@@ -1,7 +1,11 @@
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/wallet_type.dart';
-CryptoCurrency walletTypeToCryptoCurrency(WalletType type, {bool isTestnet = false}) {
+CryptoCurrency walletTypeToCryptoCurrency(WalletType type, {bool isTestnet = false, int? chainId}) {
+ if (chainId != null) {
+ return getCryptoCurrencyByChainId(chainId);
+ }
+
switch (type) {
case WalletType.monero:
return CryptoCurrency.xmr;
@@ -45,3 +49,43 @@ CryptoCurrency walletTypeToCryptoCurrency(WalletType type, {bool isTestnet = fal
'Unexpected wallet type: ${type.toString()} for CryptoCurrency walletTypeToCryptoCurrency');
}
}
+
+CryptoCurrency getCryptoCurrencyByChainId(int chainId) {
+ switch (chainId) {
+ case 1:
+ return CryptoCurrency.eth;
+ case 137:
+ return CryptoCurrency.maticpoly;
+ case 8453:
+ return CryptoCurrency.baseEth;
+ case 42161:
+ return CryptoCurrency.arbEth;
+ default:
+ return CryptoCurrency.eth;
+ }
+}
+
+/// Get chainId from CryptoCurrency for EVM chains
+/// Returns null if currency is not an EVM chain
+int? getChainIdByCryptoCurrency(CryptoCurrency currency) {
+ switch (currency) {
+ case CryptoCurrency.eth:
+ return 1;
+ case CryptoCurrency.maticpoly:
+ return 137;
+ case CryptoCurrency.baseEth:
+ return 8453;
+ case CryptoCurrency.arbEth:
+ return 42161;
+ default:
+ return null;
+ }
+}
+
+CryptoCurrency getCryptoCurrencyForWalletListItem(WalletType type, {bool isTestnet = false, int? chainId}) {
+ if (type == WalletType.arbitrum) {
+ return CryptoCurrency.arb;
+ }
+
+ return walletTypeToCryptoCurrency(type, isTestnet: isTestnet, chainId: chainId);
+}
diff --git a/cw_core/lib/erc20_token.dart b/cw_core/lib/erc20_token.dart
index cbc42ad..841be33 100644
--- a/cw_core/lib/erc20_token.dart
+++ b/cw_core/lib/erc20_token.dart
@@ -74,9 +74,7 @@ class Erc20Token extends CryptoCurrency with HiveObjectMixin {
static const arbitrumBoxName = 'ArbitrumErc20Tokens';
@override
- bool operator ==(other) =>
- (other is Erc20Token && other.contractAddress == contractAddress) ||
- (other is CryptoCurrency && other.title == title);
+ bool operator ==(Object other) => other is Erc20Token && other.contractAddress == contractAddress;
@override
int get hashCode => contractAddress.hashCode;
diff --git a/cw_core/lib/hardware/device_connection_type.dart b/cw_core/lib/hardware/device_connection_type.dart
index 76f07ed..74f043a 100644
--- a/cw_core/lib/hardware/device_connection_type.dart
+++ b/cw_core/lib/hardware/device_connection_type.dart
@@ -15,7 +15,7 @@ enum DeviceConnectionType {
WalletType.bitcoin,
// WalletType.litecoin,
WalletType.ethereum,
- WalletType.polygon
+ WalletType.polygon,
].contains(walletType);
break;
case HardwareWalletType.ledger:
@@ -24,7 +24,7 @@ enum DeviceConnectionType {
WalletType.bitcoin,
WalletType.litecoin,
WalletType.ethereum,
- WalletType.polygon
+ WalletType.polygon,
].contains(walletType);
break;
case HardwareWalletType.trezor:
diff --git a/cw_core/lib/wallet_base.dart b/cw_core/lib/wallet_base.dart
index 0a8a00b..e477608 100644
--- a/cw_core/lib/wallet_base.dart
+++ b/cw_core/lib/wallet_base.dart
@@ -25,7 +25,13 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
WalletType get type => walletInfo.type;
- CryptoCurrency get currency => walletTypeToCryptoCurrency(type, isTestnet: isTestnet);
+ int? get chainId => null;
+
+ CryptoCurrency get currency => walletTypeToCryptoCurrency(
+ type,
+ chainId: chainId,
+ isTestnet: isTestnet,
+ );
String get id => walletInfo.id;
diff --git a/cw_core/lib/wallet_type.dart b/cw_core/lib/wallet_type.dart
index 5ef50ee..1f8af52 100644
--- a/cw_core/lib/wallet_type.dart
+++ b/cw_core/lib/wallet_type.dart
@@ -257,14 +257,18 @@ WalletType? cryptoCurrencyToWalletType(CryptoCurrency type) {
return WalletType.haven;
case CryptoCurrency.eth:
return WalletType.ethereum;
+ case CryptoCurrency.maticpoly:
+ return WalletType.polygon;
+ case CryptoCurrency.baseEth:
+ return WalletType.base;
+ case CryptoCurrency.arbEth:
+ return WalletType.arbitrum;
case CryptoCurrency.bch:
return WalletType.bitcoinCash;
case CryptoCurrency.nano:
return WalletType.nano;
case CryptoCurrency.banano:
return WalletType.banano;
- case CryptoCurrency.maticpoly:
- return WalletType.polygon;
case CryptoCurrency.sol:
return WalletType.solana;
case CryptoCurrency.trx:
@@ -277,10 +281,6 @@ WalletType? cryptoCurrencyToWalletType(CryptoCurrency type) {
return WalletType.decred;
case CryptoCurrency.doge:
return WalletType.dogecoin;
- case CryptoCurrency.baseEth:
- return WalletType.base;
- case CryptoCurrency.arbEth:
- return WalletType.arbitrum;
default:
return null;
}
diff --git a/cw_ethereum/.gitignore b/cw_ethereum/.gitignore
deleted file mode 100644
index 96486fd..0000000
--- a/cw_ethereum/.gitignore
+++ /dev/null
@@ -1,30 +0,0 @@
-# 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/
-.packages
-build/
diff --git a/cw_ethereum/.metadata b/cw_ethereum/.metadata
deleted file mode 100644
index 1e05dac..0000000
--- a/cw_ethereum/.metadata
+++ /dev/null
@@ -1,10 +0,0 @@
-# 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: eb6d86ee27deecba4a83536aa20f366a6044895c
- channel: stable
-
-project_type: package
diff --git a/cw_ethereum/CHANGELOG.md b/cw_ethereum/CHANGELOG.md
deleted file mode 100644
index 41cc7d8..0000000
--- a/cw_ethereum/CHANGELOG.md
+++ /dev/null
@@ -1,3 +0,0 @@
-## 0.0.1
-
-* TODO: Describe initial release.
diff --git a/cw_ethereum/LICENSE b/cw_ethereum/LICENSE
deleted file mode 100644
index ba75c69..0000000
--- a/cw_ethereum/LICENSE
+++ /dev/null
@@ -1 +0,0 @@
-TODO: Add your license here.
diff --git a/cw_ethereum/README.md b/cw_ethereum/README.md
deleted file mode 100644
index 70a350b..0000000
--- a/cw_ethereum/README.md
+++ /dev/null
@@ -1,64 +0,0 @@
-## cw_ethereum
-
-Ethereum wallet module built on `cw_evm`. Supports native ETH and ERC‑20 tokens, with history fetched via Etherscan.
-
-### Features
-
-- EVM client specialized for Ethereum mainnet (chainId 1).
-- Default ERC‑20 token list and wallet‑scoped token storage/migration.
-- History via Etherscan v2 API (external, internal, and token transfers).
-- EIP‑1559 fee support; gas estimation per transaction intent.
-- Create/sign native and ERC‑20 transfers; approvals; broadcast.
-- Manage ERC‑20 tokens and balances; metadata lookup when needed.
-- Message signing and verification.
-- Node health checks for native and USDC token balance.
-
-### Getting started
-
-Add shared EVM secrets (see `cw_evm` README):
-
-```dart
-// cw_evm/lib/.secrets.g.dart (DO NOT COMMIT)
-const String etherScanApiKey = 'YOUR_ETHERSCAN_KEY';
-const String nowNodesApiKey = 'YOUR_NOWNODES_KEY'; // if using eth.nownodes.io
-const String moralisApiKey = 'YOUR_MORALIS_KEY'; // optional
-```
-
-Connect and sync:
-
-```dart
-final service = EthereumWalletService(walletInfoBox, true, client: EthereumClient());
-final wallet = await service.create(EVMChainNewWalletCredentials(name: 'My ETH', password: 'secret'));
-await wallet.connectToNode(node: Node(uriRaw: 'eth.llamarpc.com', isSSL: true));
-await wallet.startSync();
-```
-
-### Usage
-
-Send ETH:
-
-```dart
-final pending = await wallet.createTransaction(
- EVMChainTransactionCredentials.single(
- address: '0x...',
- cryptoAmount: '0.05',
- currency: CryptoCurrency.eth,
- priority: EVMChainTransactionPriority.medium,
- ),
-);
-final hash = await pending.commit();
-```
-
-Add an ERC‑20 token and refresh balance:
-
-```dart
-final token = await wallet.getErc20Token('0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', 'eth'); // USDC
-if (token != null) {
- await wallet.addErc20Token(token);
-}
-```
-
-### Additional information
-
-- Toggle Etherscan usage via shared preferences key `use_etherscan`.
-- See `lib/` for APIs: `EthereumClient`, `EthereumWallet`, `EthereumWalletService`.
diff --git a/cw_ethereum/analysis_options.yaml b/cw_ethereum/analysis_options.yaml
deleted file mode 100644
index a5744c1..0000000
--- a/cw_ethereum/analysis_options.yaml
+++ /dev/null
@@ -1,4 +0,0 @@
-include: package:flutter_lints/flutter.yaml
-
-# Additional information about this file can be found at
-# https://dart.dev/guides/language/analysis-options
diff --git a/cw_ethereum/lib/cw_ethereum.dart b/cw_ethereum/lib/cw_ethereum.dart
deleted file mode 100644
index af9ea7e..0000000
--- a/cw_ethereum/lib/cw_ethereum.dart
+++ /dev/null
@@ -1,7 +0,0 @@
-library cw_ethereum;
-
-/// A Calculator.
-class Calculator {
- /// Returns [value] plus 1.
- int addOne(int value) => value + 1;
-}
diff --git a/cw_ethereum/lib/default_ethereum_erc20_tokens.dart b/cw_ethereum/lib/default_ethereum_erc20_tokens.dart
deleted file mode 100644
index 4bb9033..0000000
--- a/cw_ethereum/lib/default_ethereum_erc20_tokens.dart
+++ /dev/null
@@ -1,863 +0,0 @@
-import 'package:cw_core/crypto_currency.dart';
-import 'package:cw_core/erc20_token.dart';
-
-class DefaultEthereumErc20Tokens {
- final List<Erc20Token> _defaultTokens = [
- Erc20Token(
- name: "USD Coin",
- symbol: "USDC",
- contractAddress: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
- decimal: 6,
- enabled: true,
- ),
- Erc20Token(
- name: "USDT Tether",
- symbol: "USDT",
- contractAddress: "0xdac17f958d2ee523a2206206994597c13d831ec7",
- decimal: 6,
- enabled: true,
- ),
- Erc20Token(
- name: "Decentralized Euro",
- symbol: "DEURO",
- contractAddress: "0xbA3f535bbCcCcA2A154b573Ca6c5A49BAAE0a3ea",
- decimal: 18,
- enabled: true,
- ),
- Erc20Token(
- name: "Dai",
- symbol: "DAI",
- contractAddress: "0x6B175474E89094C44Da98b954EedeAC495271d0F",
- decimal: 18,
- enabled: true,
- ),
- Erc20Token(
- name: "Wrapped Ether",
- symbol: "WETH",
- contractAddress: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Pepe",
- symbol: "PEPE",
- contractAddress: "0x6982508145454ce325ddbe47a25d4ec3d2311933",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "SHIBA INU",
- symbol: "SHIB",
- contractAddress: "0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "ApeCoin",
- symbol: "APE",
- contractAddress: "0x4d224452801aced8b2f0aebe155379bb5d594381",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Matic Token",
- symbol: "MATIC",
- contractAddress: "0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Wrapped BTC",
- symbol: "WBTC",
- contractAddress: "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
- decimal: 8,
- enabled: false,
- ),
- Erc20Token(
- name: "Gitcoin",
- symbol: "GTC",
- contractAddress: "0xde30da39c46104798bb5aa3fe8b9e0e1f348163f",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Compound",
- symbol: "COMP",
- contractAddress: "0xc00e94cb662c3520282e6f5717214004a7f26888",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Aave Token",
- symbol: "AAVE",
- contractAddress: "0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Uniswap",
- symbol: "UNI",
- contractAddress: "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Decentraland",
- symbol: "MANA",
- contractAddress: "0x0F5D2fB29fb7d3CFeE444a200298f468908cC942",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Storj",
- symbol: "STORJ",
- contractAddress: "0xb64ef51c888972c908cfacf59b47c1afbc0ab8ac",
- decimal: 8,
- enabled: false,
- ),
- Erc20Token(
- name: "Maker",
- symbol: "MKR",
- contractAddress: "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Orchid",
- symbol: "OXT",
- contractAddress: "0x4575f41308EC1483f3d399aa9a2826d74Da13Deb",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Paxos Gold",
- symbol: "PAXG",
- contractAddress: "0x45804880De22913dAFE09f4980848ECE6EcbAf78",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Binance Coin",
- symbol: "BNB",
- contractAddress: "0xB8c77482e45F1F44dE1745F52C74426C631bDD52",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "stETH",
- symbol: "stETH",
- contractAddress: "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Lido DAO",
- symbol: "LDO",
- contractAddress: "0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Arbitrum",
- symbol: "ARB",
- contractAddress: "0xB50721BCf8d664c30412Cfbc6cf7a15145234ad1",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Graph Token",
- symbol: "GRT",
- contractAddress: "0xc944E90C64B2c07662A292be6244BDf05Cda44a7",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Frax",
- symbol: "FRAX",
- contractAddress: "0x853d955aCEf822Db058eb8505911ED77F175b99e",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Gemini dollar",
- symbol: "GUSD",
- contractAddress: "0x056Fd409E1d7A124BD7017459dFEa2F387b6d5Cd",
- decimal: 2,
- enabled: false,
- ),
- Erc20Token(
- name: "Compound Ether",
- symbol: "cETH",
- contractAddress: "0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5",
- decimal: 8,
- enabled: false,
- ),
- Erc20Token(
- name: "Binance USD",
- symbol: "BUSD",
- contractAddress: "0x4Fabb145d64652a948d72533023f6E7A623C7C53",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "TrueUSD",
- symbol: "TUSD",
- contractAddress: "0x0000000000085d4780B73119b644AE5ecd22b376",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Cronos Coin",
- symbol: "CRO",
- contractAddress: "0xA0b73E1Ff0B80914AB6fe0444E65848C4C34450b",
- decimal: 8,
- enabled: false,
- ),
- Erc20Token(
- name: "Pax Dollar",
- symbol: "USDP",
- contractAddress: "0x8E870D67F660D95d5be530380D0eC0bd388289E1",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Fantom Token",
- symbol: "FTM",
- contractAddress: "0x4E15361FD6b4BB609Fa63C81A2be19d873717870",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "BitTorrent",
- symbol: "BTT",
- contractAddress: "0xC669928185DbCE49d2230CC9B0979BE6DC797957",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Nexo",
- symbol: "NEXO",
- contractAddress: "0xB62132e35a6c13ee1EE0f84dC5d40bad8d815206",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "dYdX",
- symbol: "DYDX",
- contractAddress: "0x92D6C1e31e14520e676a687F0a93788B716BEff5",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "PancakeSwap Token",
- symbol: "Cake",
- contractAddress: "0x152649eA73beAb28c5b49B26eb48f7EAD6d4c898",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "BAT",
- symbol: "BAT",
- contractAddress: "0x0D8775F648430679A709E98d2b0Cb6250d2887EF",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "1INCH Token",
- symbol: "1INCH",
- contractAddress: "0x111111111117dC0aa78b770fA6A738034120C302",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Ethereum Name Service",
- symbol: "ENS",
- contractAddress: "0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "ZRX",
- symbol: "ZRX",
- contractAddress: "0xE41d2489571d322189246DaFA5ebDe1F4699F498",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Verse",
- symbol: "VERSE",
- contractAddress: "0x249cA82617eC3DfB2589c4c17ab7EC9765350a18",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "PayPal USD",
- symbol: "PYUSD",
- contractAddress: "0x6c3ea9036406852006290770bedfcaba0e23a0e8",
- decimal: 6,
- enabled: false,
- ),
- Erc20Token(
- name: "Chainflip",
- symbol: "FLIP",
- contractAddress: "0x826180541412D574cf1336d22c0C0a287822678A",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Native Decentralized Euro Protocol Share",
- symbol: "NDEPS",
- contractAddress: "0xc71104001A3CCDA1BEf1177d765831Bd1bfE8eE6",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Decentralized Euro Protocol Share",
- symbol: "DEPS",
- contractAddress: "0x103747924e74708139a9400e4ab4bea79fffa380",
- decimal: 18,
- enabled: false,
- ),
- Erc20Token(
- name: "Kraken Wrapped Bitcoin",
- symbol: "KBTC",
- contractAddress: "0x73E0C0d45E048D25Fc26Fa3159b0aA04BfA4Db98",
- decimal: 8,
- enabled: false,
- ),
- Erc20Token(
- name: "Coinbase Wrapped BTC",
- symbol: "CBBTC",
- contractAddress: "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf",
- decimal: 8,
- enabled: false,
- ),
- Erc20Token(
- name: 'Abbott xStock',
- symbol: 'ABTx',
- contractAddress: '0x89233399708c18ac6887f90a2b4cd8ba5fedd06e',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/abtx.webp',
- ),
- Erc20Token(
- name: 'AbbVie xStock',
- symbol: 'ABBVx',
- contractAddress: '0xfbf2398df672cee4afcc2a4a733222331c742a6a',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/abbv.webp',
- ),
- Erc20Token(
- name: 'Accenture xStock',
- symbol: 'ACNx',
- contractAddress: '0x03183ce31b1656b72a55fa6056e287f50c35bbeb',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/acnx.webp',
- ),
- Erc20Token(
- name: 'Alphabet xStock',
- symbol: 'GOOGLx',
- contractAddress: '0xe92f673ca36c5e2efd2de7628f815f84807e803f',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/googlx.webp',
- ),
- Erc20Token(
- name: 'Amazon xStock',
- symbol: 'AMZNx',
- contractAddress: '0x3557ba345b01efa20a1bddc61f573bfd87195081',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/amznx.webp',
- ),
- Erc20Token(
- name: 'Amber xStock',
- symbol: 'AMBRx',
- contractAddress: '0x2f9a35ab5ddfbc49927bfdeab98a86c53dc6e763',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/ambrx.webp',
- ),
- Erc20Token(
- name: 'Apple xStock',
- symbol: 'AAPLx',
- contractAddress: '0x9d275685dc284c8eb1c79f6aba7a63dc75ec890a',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/apple.webp',
- ),
- Erc20Token(
- name: 'AppLovin xStock',
- symbol: 'APPx',
- contractAddress: '0x50a1291f69d9d3853def8209cfb1af0b46927be1',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/appx.webp',
- ),
- Erc20Token(
- name: 'AstraZeneca xStock',
- symbol: 'AZNx',
- contractAddress: '0x5d642505fe1a28897eb3baba665f454755d8daa2',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/aznx.webp',
- ),
- Erc20Token(
- name: 'Bank of America xStock',
- symbol: 'BACx',
- contractAddress: '0x314938c596f5ce31c3f75307d2979338c346d7f2',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/bacx.webp',
- ),
- Erc20Token(
- name: 'Berkshire Hathaway xStock',
- symbol: 'BRK.Bx',
- contractAddress: '0x12992613fdd35abe95dec5a4964331b1ee23b50d',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/brkbx.webp',
- ),
- Erc20Token(
- name: 'Broadcom xStock',
- symbol: 'AVGOx',
- contractAddress: '0x38bac69cbbd28156796e4163b2b6dcb81e336565',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/avgox.webp',
- ),
- Erc20Token(
- name: 'Chevron xStock',
- symbol: 'CVXx',
- contractAddress: '0xad5cdc3340904285b8159089974a99a1a09eb4c0',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/cvxx.webp',
- ),
- Erc20Token(
- name: 'Circle xStock',
- symbol: 'CRCLx',
- contractAddress: '0xfebded1b0986a8ee107f5ab1a1c5a813491deceb',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/crclx.webp',
- ),
- Erc20Token(
- name: 'Cisco xStock',
- symbol: 'CSCOx',
- contractAddress: '0x053c784cd87b74f42e0c089f98643e79c1a3ff16',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/cscox.webp',
- ),
- Erc20Token(
- name: 'Coca-Cola xStock',
- symbol: 'KOx',
- contractAddress: '0xdcc1a2699441079da889b1f49e12b69cc791129b',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/kox.webp',
- ),
- Erc20Token(
- name: 'Coinbase xStock',
- symbol: 'COINx',
- contractAddress: '0x364f210f430ec2448fc68a49203040f6124096f0',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/coinx.webp',
- ),
- Erc20Token(
- name: 'Comcast xStock',
- symbol: 'CMCSAx',
- contractAddress: '0xbc7170a1280be28513b4e940c681537eb25e39f4',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/cmcsax.webp',
- ),
- Erc20Token(
- name: 'CrowdStrike xStock',
- symbol: 'CRWDx',
- contractAddress: '0x214151022c2a5e380ab80cdac31f23ae554a7345',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/crwdx.webp',
- ),
- Erc20Token(
- name: 'Danaher xStock',
- symbol: 'DHRx',
- contractAddress: '0xdba228936f4079daf9aa906fd48a87f2300405f4',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/dhrx.webp',
- ),
- Erc20Token(
- name: 'DFDV xStock',
- symbol: 'DFDVx',
- contractAddress: '0x521860bb5df5468358875266b89bfe90d990c6e7',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/dfdvx.webp',
- ),
- Erc20Token(
- name: 'Eli Lilly xStock',
- symbol: 'LLYx',
- contractAddress: '0x19c41ea77b34bbdee61c3a87a75d1abda2ed0be4',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/llyx.webp',
- ),
- Erc20Token(
- name: 'Exxon Mobil xStock',
- symbol: 'XOMx',
- contractAddress: '0xeedb0273c5af792745180e9ff568cd01550ffa13',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/xomx.webp',
- ),
- Erc20Token(
- name: 'Gamestop xStock',
- symbol: 'GMEx',
- contractAddress: '0xe5f6d3b2405abdfe6f660e63202b25d23763160d',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/gmex.webp',
- ),
- Erc20Token(
- name: 'Gold xStock',
- symbol: 'GLDx',
- contractAddress: '0x2380f2673c640fb67e2d6b55b44c62f0e0e69da9',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/gldx.webp',
- ),
- Erc20Token(
- name: 'Goldman Sachs xStock',
- symbol: 'GSx',
- contractAddress: '0x3ee7e9b3a992fd23cd1c363b0e296856b04ab149',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/gsx.webp',
- ),
- Erc20Token(
- name: 'Home Depot xStock',
- symbol: 'HDx',
- contractAddress: '0x766b0cd6ed6d90b5d49d2c36a3761e9728501ba9',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/hdx.webp',
- ),
- Erc20Token(
- name: 'Honeywell xStock',
- symbol: 'HONx',
- contractAddress: '0x62a48560861b0b451654bfffdb5be6e47aa8ff1b',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/honx.webp',
- ),
- Erc20Token(
- name: 'Intel xStock',
- symbol: 'INTCx',
- contractAddress: '0xf8a80d1cb9cfd70d03d655d9df42339846f3b3c8',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/intcx.webp',
- ),
- Erc20Token(
- name: 'International Business Machines xStock',
- symbol: 'IBMx',
- contractAddress: '0xd9913208647671fe0f48f7f260076b2c6f310aac',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/ibmx.webp',
- ),
- Erc20Token(
- name: 'Johnson & Johnson xStock',
- symbol: 'JNJx',
- contractAddress: '0xdb0482cfad4789798623e64b15eeba01b16e917c',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/jnjx.webp',
- ),
- Erc20Token(
- name: 'JPMorgan Chase xStock',
- symbol: 'JPMx',
- contractAddress: '0xd9fc3e075d45254a1d834fea18af8041207dea0a',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/jpmx.webp',
- ),
- Erc20Token(
- name: 'Linde xStock',
- symbol: 'LINx',
- contractAddress: '0x15059c599c16fd8f70b633ade165502d6402cd49',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/linx.webp',
- ),
- Erc20Token(
- name: 'Marvell xStock',
- symbol: 'MRVLx',
- contractAddress: '0xeaad46f4146ded5a47b55aa7f6c48c191deaec88',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/mrvlx.webp',
- ),
- Erc20Token(
- name: 'Mastercard xStock',
- symbol: 'MAx',
- contractAddress: '0xb365cd2588065f522d379ad19e903304f6b622c6',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/mastercard.webp',
- ),
- Erc20Token(
- name: 'McDonald\'s xStock',
- symbol: 'MCDx',
- contractAddress: '0x80a77a372c1e12accda84299492f404902e2da67',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/mcdonalds.webp',
- ),
- Erc20Token(
- name: 'Medtronic xStock',
- symbol: 'MDTx',
- contractAddress: '0x0588e851ec0418d660bee81230d6c678daf21d46',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/mdtx.webp',
- ),
- Erc20Token(
- name: 'Merck xStock',
- symbol: 'MRKx',
- contractAddress: '0x17d8186ed8f68059124190d147174d0f6697dc40',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/mrkx.webp',
- ),
- Erc20Token(
- name: 'Meta xStock',
- symbol: 'METAx',
- contractAddress: '0x96702be57cd9777f835117a809c7124fe4ec989a',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/metax.webp',
- ),
- Erc20Token(
- name: 'Microsoft xStock',
- symbol: 'MSFTx',
- contractAddress: '0x5621737f42dae558b81269fcb9e9e70c19aa6b35',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/msftx.webp',
- ),
- Erc20Token(
- name: 'MicroStrategy xStock',
- symbol: 'MSTRx',
- contractAddress: '0xae2f842ef90c0d5213259ab82639d5bbf649b08e',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/mstrx.webp',
- ),
- Erc20Token(
- name: 'Nasdaq xStock',
- symbol: 'QQQx',
- contractAddress: '0xa753a7395cae905cd615da0b82a53e0560f250af',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/qqqx.webp',
- ),
- Erc20Token(
- name: 'Netflix xStock',
- symbol: 'NFLXx',
- contractAddress: '0xa6a65ac27e76cd53cb790473e4345c46e5ebf961',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/nflxx.webp',
- ),
- Erc20Token(
- name: 'Novo Nordisk xStock',
- symbol: 'NVOx',
- contractAddress: '0xf9523e369c5f55ad72dbaa75b0a9b92b3d8b147e',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/nvox.webp',
- ),
- Erc20Token(
- name: 'NVIDIA xStock',
- symbol: 'NVDAx',
- contractAddress: '0xc845b2894dbddd03858fd2d643b4ef725fe0849d',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/nvdax.webp',
- ),
- Erc20Token(
- name: 'OPEN xStock',
- symbol: 'OPENx',
- contractAddress: '0xbee6b69345f376598fe16abd5592c6f844825e66',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/openx.webp',
- ),
- Erc20Token(
- name: 'Oracle xStock',
- symbol: 'ORCLx',
- contractAddress: '0x548308e91ec9f285c7bff05295badbd56a6e4971',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/orclx.webp',
- ),
- Erc20Token(
- name: 'Palantir xStock',
- symbol: 'PLTRx',
- contractAddress: '0x6d482cec5f9dd1f05ccee9fd3ff79b246170f8e2',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/pltrx.webp',
- ),
- Erc20Token(
- name: 'PepsiCo xStock',
- symbol: 'PEPx',
- contractAddress: '0x36c424a6ec0e264b1616102ad63ed2ad7857413e',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/pepx.webp',
- ),
- Erc20Token(
- name: 'Pfizer xStock',
- symbol: 'PFEx',
- contractAddress: '0x1ac765b5bea23184802c7d2d497f7c33f1444a9e',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/pfex.webp',
- ),
- Erc20Token(
- name: 'Philip Morris xStock',
- symbol: 'PMx',
- contractAddress: '0x02a6c1789c3b4fdb1a7a3dfa39f90e5d3c94f4f9',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/pmx.webp',
- ),
- Erc20Token(
- name: 'Procter & Gamble xStock',
- symbol: 'PGx',
- contractAddress: '0xa90424d5d3e770e8644103ab503ed775dd1318fd',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/pgx.webp',
- ),
- Erc20Token(
- name: 'Robinhood xStock',
- symbol: 'HOODx',
- contractAddress: '0xe1385fdd5ffb10081cd52c56584f25efa9084015',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/hoodx.webp',
- ),
- Erc20Token(
- name: 'Salesforce xStock',
- symbol: 'CRMx',
- contractAddress: '0x4a4073f2eaf299a1be22254dcd2c41727f6f54a2',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/crmx.webp',
- ),
- Erc20Token(
- name: 'SP500 xStock',
- symbol: 'SPYx',
- contractAddress: '0x90a2a4c76b5d8c0bc892a69ea28aa775a8f2dd48',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/spyx.webp',
- ),
- Erc20Token(
- name: 'TBLL xStock',
- symbol: 'TBLLx',
- contractAddress: '0x4cbf89ed7bb30b8a860fa86d3c96e9c72931299b',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/tbllx.webp',
- ),
- Erc20Token(
- name: 'Tesla xStock',
- symbol: 'TSLAx',
- contractAddress: '0x8ad3c73f833d3f9a523ab01476625f269aeb7cf0',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/tslax.webp',
- ),
- Erc20Token(
- name: 'Thermo Fisher xStock',
- symbol: 'TMOx',
- contractAddress: '0xaf072f109a2c173d822a4fe9af311a1b18f83d19',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/tmox.webp',
- ),
- Erc20Token(
- name: 'TON xStock',
- symbol: 'TONXx',
- contractAddress: '0xe95ab205e333443d7970336d5fd827ef9ed97608',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/tonxx.webp',
- ),
- Erc20Token(
- name: 'TQQQ xStock',
- symbol: 'TQQQx',
- contractAddress: '0xfdddb57878ef9d6f681ec4381dcb626b9e69ac86',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/tqqqx.webp',
- ),
- Erc20Token(
- name: 'UnitedHealth xStock',
- symbol: 'UNHx',
- contractAddress: '0x167a6375da1efc4a5be0f470e73ecefd66245048',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/unhx.webp',
- ),
- Erc20Token(
- name: 'Vanguard xStock',
- symbol: 'VTIx',
- contractAddress: '0xbd730e618bcd88c82ddee52e10275cf2f88a4777',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/vtix.webp',
- ),
- Erc20Token(
- name: 'Visa xStock',
- symbol: 'Vx',
- contractAddress: '0x2363fd1235c1b6d3a5088ddf8df3a0b3a30c5293',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/vx.webp',
- ),
- Erc20Token(
- name: 'Walmart xStock',
- symbol: 'WMTx',
- contractAddress: '0x7aefc9965699fbea943e03264d96e50cd4a97b21',
- decimal: 8,
- enabled: false,
- iconPath: 'assets/images/stocks/wmtx.webp',
- ),
- ];
-
- List<Erc20Token> get initialErc20Tokens => _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: 'ETH');
- }).toList();
-}
diff --git a/cw_ethereum/lib/deuro/deuro_savings.dart b/cw_ethereum/lib/deuro/deuro_savings.dart
deleted file mode 100644
index 2fa93b4..0000000
--- a/cw_ethereum/lib/deuro/deuro_savings.dart
+++ /dev/null
@@ -1,202 +0,0 @@
-import 'package:cw_core/crypto_currency.dart';
-import 'package:cw_ethereum/deuro/deuro_savings_contract.dart';
-import 'package:cw_ethereum/ethereum_wallet.dart';
-import 'package:cw_evm/contract/erc20.dart';
-import 'package:cw_evm/evm_chain_exceptions.dart';
-import 'package:cw_evm/evm_chain_transaction_priority.dart';
-import 'package:cw_evm/pending_evm_chain_transaction.dart';
-import 'package:web3dart/crypto.dart';
-import 'package:web3dart/web3dart.dart';
-
-const String savingsGatewayAddress = "0x073493d73258C4BEb6542e8dd3e1b2891C972303";
-
-const String dEuroAddress = "0xbA3f535bbCcCcA2A154b573Ca6c5A49BAAE0a3ea";
-const String frontendCode = "0x00000000000000000000000000000000000000000043616b652057616c6c6574";
-
-class DEuro {
- final SavingsGateway _savingsGateway;
- final ERC20 _dEuro;
- final EthereumWallet _wallet;
-
- DEuro(EthereumWallet wallet)
- : _wallet = wallet,
- _savingsGateway = _getSavingsGateway(wallet.getWeb3Client()!),
- _dEuro = _getDEuroToken(wallet.getWeb3Client()!);
-
- static SavingsGateway _getSavingsGateway(Web3Client client) => SavingsGateway(
- address: EthereumAddress.fromHex(savingsGatewayAddress),
- client: client,
- );
-
- static ERC20 _getDEuroToken(Web3Client client) => ERC20(
- address: EthereumAddress.fromHex(dEuroAddress),
- client: client,
- );
-
- EthereumAddress get _address => EthereumAddress.fromHex(_wallet.walletAddresses.primaryAddress);
-
- Future<BigInt> get savingsBalance async =>
- (await _savingsGateway.savings(accountOwner: _address)).saved;
-
- Future<BigInt> get accruedInterest => _savingsGateway.accruedInterest(accountOwner: _address);
-
- Future<BigInt> get interestRate => _savingsGateway.currentRatePPM();
-
- Future<BigInt> get approvedBalance => _dEuro.allowance(_address, _savingsGateway.self.address);
-
- Future<void> _checkEthBalanceForGasFees(EVMChainTransactionPriority priority) async {
- final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
- final currentBalance = ethBalance.getInWei;
-
- final gasFeesModel = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
- amount: BigInt.zero,
- contractAddress: _savingsGateway.self.address.hexEip55,
- receivingAddressHex: _savingsGateway.self.address.hexEip55,
- priority: priority,
- data: _savingsGateway.self.abi.functions[17]
- .encodeCall([BigInt.zero, hexToBytes(frontendCode)]),
- );
-
- final estimatedGasFee = BigInt.from(gasFeesModel.estimatedGasFee);
- final requiredBalance = estimatedGasFee;
-
- if (currentBalance < requiredBalance) {
- throw InsufficientGasFeeException(
- requiredGasFee: requiredBalance,
- currentBalance: currentBalance,
- );
- }
- }
-
- Future<PendingEVMChainTransaction> depositSavings(
- BigInt amount, EVMChainTransactionPriority priority) async {
- try {
- await _checkEthBalanceForGasFees(priority);
-
- final signedTransaction = await _savingsGateway.save(
- (amount: amount, frontendCode: hexToBytes(frontendCode)),
- credentials: _wallet.evmChainPrivateKey,
- );
-
- final fee = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
- amount: amount,
- contractAddress: _savingsGateway.self.address.hexEip55,
- receivingAddressHex: _savingsGateway.self.address.hexEip55,
- priority: priority,
- data: _savingsGateway.self.abi.functions[17].encodeCall([amount, hexToBytes(frontendCode)]),
- );
-
- final sendTransaction = () => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
-
- return PendingEVMChainTransaction(
- feeCurrency: "ETH",
- sendTransaction: sendTransaction,
- signedTransaction: signedTransaction,
- fee: BigInt.from(fee.estimatedGasFee),
- amount: amount.toString(),
- exponent: 18,
- );
- } catch (e) {
- if (e.toString().contains('insufficient funds for gas')) {
- final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
- throw InsufficientGasFeeException(currentBalance: ethBalance.getInWei);
- }
- rethrow;
- }
- }
-
- Future<PendingEVMChainTransaction> withdrawSavings(
- BigInt amount, EVMChainTransactionPriority priority) async {
- try {
- await _checkEthBalanceForGasFees(priority);
-
- final signedTransaction = await _savingsGateway.withdraw(
- (target: _address, amount: amount, frontendCode: hexToBytes(frontendCode)),
- credentials: _wallet.evmChainPrivateKey,
- );
-
- final fee = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
- amount: amount,
- contractAddress: _savingsGateway.self.address.hexEip55,
- receivingAddressHex: _savingsGateway.self.address.hexEip55,
- priority: priority,
- data: _savingsGateway.self.abi.functions[24].encodeCall([_address, amount, hexToBytes(frontendCode)]),
- );
-
- final sendTransaction = () => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
-
- return PendingEVMChainTransaction(
- sendTransaction: sendTransaction,
- signedTransaction: signedTransaction,
- fee: BigInt.from(fee.estimatedGasFee),
- amount: amount.toString(),
- feeCurrency: "ETH",
- exponent: 18);
- } catch (e) {
- if (e.toString().contains('insufficient funds for gas')) {
- final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
- throw InsufficientGasFeeException(currentBalance: ethBalance.getInWei);
- }
- rethrow;
- }
- }
-
- Future<PendingEVMChainTransaction> reinvestInterest(EVMChainTransactionPriority priority) async {
- try {
- await _checkEthBalanceForGasFees(priority);
-
- final signedTransaction = await _savingsGateway.refreshBalance((owner: _address),
- credentials: _wallet.evmChainPrivateKey,
- );
-
- final fee = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
- amount: BigInt.zero,
- contractAddress: _savingsGateway.self.address.hexEip55,
- receivingAddressHex: _savingsGateway.self.address.hexEip55,
- priority: priority,
- data: _savingsGateway.self.abi.functions[15].encodeCall([_address]),
- );
-
- final sendTransaction = () => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
-
- return PendingEVMChainTransaction(
- feeCurrency: "ETH",
- sendTransaction: sendTransaction,
- signedTransaction: signedTransaction,
- fee: BigInt.from(fee.estimatedGasFee),
- amount: 0.toString(),
- exponent: 18,
- );
- } catch (e) {
- if (e.toString().contains('insufficient funds for gas')) {
- final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
- throw InsufficientGasFeeException(currentBalance: ethBalance.getInWei);
- }
- rethrow;
- }
- }
-
- // Set an infinite approval to save gas in the future
- Future<PendingEVMChainTransaction> enableSavings(EVMChainTransactionPriority priority) async {
- try {
- await _checkEthBalanceForGasFees(priority);
-
- return (await _wallet.createApprovalTransaction(
- BigInt.parse(
- 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
- radix: 16,
- ),
- _savingsGateway.self.address.hexEip55,
- CryptoCurrency.deuro,
- priority,
- "ETH",
- )) as PendingEVMChainTransaction;
- } catch (e) {
- if (e.toString().contains('insufficient funds for gas')) {
- final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
- throw InsufficientGasFeeException(currentBalance: ethBalance.getInWei);
- }
- rethrow;
- }
- }
-}
diff --git a/cw_ethereum/lib/deuro/deuro_savings_contract.dart b/cw_ethereum/lib/deuro/deuro_savings_contract.dart
deleted file mode 100644
index 77316f4..0000000
--- a/cw_ethereum/lib/deuro/deuro_savings_contract.dart
+++ /dev/null
@@ -1,543 +0,0 @@
-// ignore_for_file: type=lint
-// ignore_for_file: unused_local_variable, unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
-// ignore_for_file: no_leading_underscores_for_library_prefixes
-import 'package:web3dart/web3dart.dart' as _i1;
-import 'dart:typed_data' as _i2;
-
-final _contractAbi = _i1.ContractAbi.fromJson(
- '[{"inputs":[{"internalType":"contract IDecentralizedEURO","name":"deuro_","type":"address"},{"internalType":"uint24","name":"initialRatePPM","type":"uint24"},{"internalType":"address","name":"gateway_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ChangeNotReady","type":"error"},{"inputs":[],"name":"ModuleDisabled","type":"error"},{"inputs":[],"name":"NoPendingChange","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"interest","type":"uint256"}],"name":"InterestCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint24","name":"newRate","type":"uint24"}],"name":"RateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"who","type":"address"},{"indexed":false,"internalType":"uint24","name":"nextRate","type":"uint24"},{"indexed":false,"internalType":"uint40","name":"nextChange","type":"uint40"}],"name":"RateProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint192","name":"amount","type":"uint192"}],"name":"Saved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint192","name":"amount","type":"uint192"}],"name":"Withdrawn","type":"event"},{"inputs":[],"name":"GATEWAY","outputs":[{"internalType":"contract IFrontendGateway","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"accountOwner","type":"address"}],"name":"accruedInterest","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"accountOwner","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"accruedInterest","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint192","name":"targetAmount","type":"uint192"},{"internalType":"bytes32","name":"frontendCode","type":"bytes32"}],"name":"adjust","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint192","name":"targetAmount","type":"uint192"}],"name":"adjust","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"applyChange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint192","name":"saved","type":"uint192"},{"internalType":"uint64","name":"ticks","type":"uint64"}],"internalType":"struct Savings.Account","name":"account","type":"tuple"},{"internalType":"uint64","name":"ticks","type":"uint64"}],"name":"calculateInterest","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentRatePPM","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentTicks","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deuro","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"equity","outputs":[{"internalType":"contract IReserve","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextChange","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextRatePPM","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint24","name":"newRatePPM_","type":"uint24"},{"internalType":"address[]","name":"helpers","type":"address[]"}],"name":"proposeChange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"refreshBalance","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"refreshMyBalance","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint192","name":"amount","type":"uint192"},{"internalType":"bytes32","name":"frontendCode","type":"bytes32"}],"name":"save","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint192","name":"amount","type":"uint192"}],"name":"save","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint192","name":"amount","type":"uint192"},{"internalType":"bytes32","name":"frontendCode","type":"bytes32"}],"name":"save","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint192","name":"amount","type":"uint192"}],"name":"save","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"savings","outputs":[{"internalType":"uint192","name":"saved","type":"uint192"},{"internalType":"uint64","name":"ticks","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"ticks","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint192","name":"amount","type":"uint192"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint192","name":"amount","type":"uint192"},{"internalType":"bytes32","name":"frontendCode","type":"bytes32"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]',
- 'SavingsGateway',
-);
-
-class SavingsGateway extends _i1.GeneratedContract {
- SavingsGateway({
- required _i1.EthereumAddress address,
- required _i1.Web3Client client,
- int? chainId,
- }) : super(
- _i1.DeployedContract(
- _contractAbi,
- address,
- ),
- client,
- chainId,
- );
-
- /// The optional [atBlock] parameter can be used to view historical data. When
- /// set, the function will be evaluated in the specified block. By default, the
- /// latest on-chain block will be used.
- Future<_i1.EthereumAddress> GATEWAY({_i1.BlockNum? atBlock}) async {
- final function = self.abi.functions[1];
- assert(checkSignature(function, '338c5371'));
- final params = [];
- final response = await read(
- function,
- params,
- atBlock,
- );
- return (response[0] as _i1.EthereumAddress);
- }
-
- /// The optional [atBlock] parameter can be used to view historical data. When
- /// set, the function will be evaluated in the specified block. By default, the
- /// latest on-chain block will be used.
- Future<BigInt> accruedInterest({
- required _i1.EthereumAddress accountOwner,
- _i1.BlockNum? atBlock,
- }) async {
- final function = self.abi.functions[2];
- assert(checkSignature(function, '77267ec3'));
- final params = [accountOwner];
- final response = await read(
- function,
- params,
- atBlock,
- );
- return (response[0] as BigInt);
- }
-
- /// The optional [atBlock] parameter can be used to view historical data. When
- /// set, the function will be evaluated in the specified block. By default, the
- /// latest on-chain block will be used.
- Future<BigInt> accruedInterest$2(
- ({_i1.EthereumAddress accountOwner, BigInt timestamp}) args, {
- _i1.BlockNum? atBlock,
- }) async {
- final function = self.abi.functions[3];
- assert(checkSignature(function, 'a696399d'));
- final params = [
- args.accountOwner,
- args.timestamp,
- ];
- final response = await read(
- function,
- params,
- atBlock,
- );
- return (response[0] as BigInt);
- }
-
- /// The optional [transaction] parameter can be used to override parameters
- /// like the gas price, nonce and max gas. The `data` and `to` fields will be
- /// set by the contract.
- Future<String> adjust(
- ({BigInt targetAmount, _i2.Uint8List frontendCode}) args, {
- required _i1.Credentials credentials,
- _i1.Transaction? transaction,
- }) async {
- final function = self.abi.functions[4];
- assert(checkSignature(function, '753ef93c'));
- final params = [
- args.targetAmount,
- args.frontendCode,
- ];
- return write(
- credentials,
- transaction,
- function,
- params,
- );
- }
-
- /// The optional [atBlock] parameter can be used to view historical data. When
- /// set, the function will be evaluated in the specified block. By default, the
- /// latest on-chain block will be used.
- Future<BigInt> calculateInterest(
- ({dynamic account, BigInt ticks}) args, {
- _i1.BlockNum? atBlock,
- }) async {
- final function = self.abi.functions[7];
- assert(checkSignature(function, '7915ce20'));
- final params = [
- args.account,
- args.ticks,
- ];
- final response = await read(
- function,
- params,
- atBlock,
- );
- return (response[0] as BigInt);
- }
-
- /// The optional [atBlock] parameter can be used to view historical data. When
- /// set, the function will be evaluated in the specified block. By default, the
- /// latest on-chain block will be used.
- Future<BigInt> currentRatePPM({_i1.BlockNum? atBlock}) async {
- final function = self.abi.functions[8];
- assert(checkSignature(function, '06a7b376'));
- final params = [];
- final response = await read(
- function,
- params,
- atBlock,
- );
- return (response[0] as BigInt);
- }
-
- /// The optional [atBlock] parameter can be used to view historical data. When
- /// set, the function will be evaluated in the specified block. By default, the
- /// latest on-chain block will be used.
- Future<BigInt> currentTicks({_i1.BlockNum? atBlock}) async {
- final function = self.abi.functions[9];
- assert(checkSignature(function, 'b079f163'));
- final params = [];
- final response = await read(
- function,
- params,
- atBlock,
- );
- return (response[0] as BigInt);
- }
-
- /// The optional [atBlock] parameter can be used to view historical data. When
- /// set, the function will be evaluated in the specified block. By default, the
- /// latest on-chain block will be used.
- Future<_i1.EthereumAddress> deuro({_i1.BlockNum? atBlock}) async {
- final function = self.abi.functions[10];
- assert(checkSignature(function, '82b8eaf5'));
- final params = [];
- final response = await read(
- function,
- params,
- atBlock,
- );
- return (response[0] as _i1.EthereumAddress);
- }
-
- /// The optional [atBlock] parameter can be used to view historical data. When
- /// set, the function will be evaluated in the specified block. By default, the
- /// latest on-chain block will be used.
- Future<_i1.EthereumAddress> equity({_i1.BlockNum? atBlock}) async {
- final function = self.abi.functions[11];
- assert(checkSignature(function, '91a0ac6a'));
- final params = [];
- final response = await read(
- function,
- params,
- atBlock,
- );
- return (response[0] as _i1.EthereumAddress);
- }
-
- /// The optional [atBlock] parameter can be used to view historical data. When
- /// set, the function will be evaluated in the specified block. By default, the
- /// latest on-chain block will be used.
- Future<BigInt> nextChange({_i1.BlockNum? atBlock}) async {
- final function = self.abi.functions[12];
- assert(checkSignature(function, 'b6f83c17'));
- final params = [];
- final response = await read(
- function,
- params,
- atBlock,
- );
- return (response[0] as BigInt);
- }
-
- /// The optional [atBlock] parameter can be used to view historical data. When
- /// set, the function will be evaluated in the specified block. By default, the
- /// latest on-chain block will be used.
- Future<BigInt> nextRatePPM({_i1.BlockNum? atBlock}) async {
- final function = self.abi.functions[13];
- assert(checkSignature(function, '2e4b20ab'));
- final params = [];
- final response = await read(
- function,
- params,
- atBlock,
- );
- return (response[0] as BigInt);
- }
-
- /// The optional [transaction] parameter can be used to override parameters
- /// like the gas price, nonce and max gas. The `data` and `to` fields will be
- /// set by the contract.
- Future<_i2.Uint8List> refreshBalance(
- ({_i1.EthereumAddress owner}) args, {
- required _i1.Credentials credentials,
- _i1.Transaction? transaction,
- }) async {
- final function = self.abi.functions[15];
- assert(checkSignature(function, 'b77cd1c7'));
- final params = [args.owner];
- return writeRaw(
- credentials,
- transaction,
- function,
- params,
- );
- }
-
- /// The optional [transaction] parameter can be used to override parameters
- /// like the gas price, nonce and max gas. The `data` and `to` fields will be
- /// set by the contract.
- Future<_i2.Uint8List> refreshMyBalance({
- required _i1.Credentials credentials,
- _i1.Transaction? transaction,
- }) async {
- final function = self.abi.functions[16];
- assert(checkSignature(function, '85bd12d1'));
- final params = [];
- return writeRaw(
- credentials,
- transaction,
- function,
- params,
- );
- }
-
- /// The optional [transaction] parameter can be used to override parameters
- /// like the gas price, nonce and max gas. The `data` and `to` fields will be
- /// set by the contract.
- Future<_i2.Uint8List> save(
- ({BigInt amount, _i2.Uint8List frontendCode}) args, {
- required _i1.Credentials credentials,
- _i1.Transaction? transaction,
- }) async {
- final function = self.abi.functions[17];
- assert(checkSignature(function, '9e2363dc'));
- final params = [
- args.amount,
- args.frontendCode,
- ];
- return writeRaw(
- credentials,
- transaction,
- function,
- params,
- );
- }
-
- /// The optional [transaction] parameter can be used to override parameters
- /// like the gas price, nonce and max gas. The `data` and `to` fields will be
- /// set by the contract.
- Future<_i2.Uint8List> saveTo(
- ({
- _i1.EthereumAddress owner,
- BigInt amount,
- _i2.Uint8List frontendCode
- }) args, {
- required _i1.Credentials credentials,
- _i1.Transaction? transaction,
- }) async {
- final function = self.abi.functions[19];
- assert(checkSignature(function, 'cbcf9676'));
- final params = [
- args.owner,
- args.amount,
- args.frontendCode,
- ];
- return writeRaw(
- credentials,
- transaction,
- function,
- params,
- );
- }
-
- /// The optional [atBlock] parameter can be used to view historical data. When
- /// set, the function will be evaluated in the specified block. By default, the
- /// latest on-chain block will be used.
- Future<Savings> savings({
- required _i1.EthereumAddress accountOwner,
- _i1.BlockNum? atBlock,
- }) async {
- final function = self.abi.functions[21];
- assert(checkSignature(function, '1f7cdd5f'));
- final params = [accountOwner];
- final response = await read(
- function,
- params,
- atBlock,
- );
- return Savings(response);
- }
-
- /// The optional [transaction] parameter can be used to override parameters
- /// like the gas price, nonce and max gas. The `data` and `to` fields will be
- /// set by the contract.
- Future<_i2.Uint8List> withdraw(
- ({
- _i1.EthereumAddress target,
- BigInt amount,
- _i2.Uint8List frontendCode
- }) args, {
- required _i1.Credentials credentials,
- _i1.Transaction? transaction,
- }) async {
- final function = self.abi.functions[24];
- assert(checkSignature(function, '829a0476'));
- final params = [
- args.target,
- args.amount,
- args.frontendCode,
- ];
- return writeRaw(
- credentials,
- transaction,
- function,
- params,
- );
- }
-
- /// Returns a live stream of all InterestCollected events emitted by this contract.
- Stream<InterestCollected> interestCollectedEvents({
- _i1.BlockNum? fromBlock,
- _i1.BlockNum? toBlock,
- }) {
- final event = self.event('InterestCollected');
- final filter = _i1.FilterOptions.events(
- contract: self,
- event: event,
- fromBlock: fromBlock,
- toBlock: toBlock,
- );
- return client.events(filter).map((_i1.FilterEvent result) {
- final decoded = event.decodeResults(
- result.topics!,
- result.data!,
- );
- return InterestCollected(
- decoded,
- result,
- );
- });
- }
-
- /// Returns a live stream of all RateChanged events emitted by this contract.
- Stream<RateChanged> rateChangedEvents({
- _i1.BlockNum? fromBlock,
- _i1.BlockNum? toBlock,
- }) {
- final event = self.event('RateChanged');
- final filter = _i1.FilterOptions.events(
- contract: self,
- event: event,
- fromBlock: fromBlock,
- toBlock: toBlock,
- );
- return client.events(filter).map((_i1.FilterEvent result) {
- final decoded = event.decodeResults(
- result.topics!,
- result.data!,
- );
- return RateChanged(
- decoded,
- result,
- );
- });
- }
-
- /// Returns a live stream of all RateProposed events emitted by this contract.
- Stream<RateProposed> rateProposedEvents({
- _i1.BlockNum? fromBlock,
- _i1.BlockNum? toBlock,
- }) {
- final event = self.event('RateProposed');
- final filter = _i1.FilterOptions.events(
- contract: self,
- event: event,
- fromBlock: fromBlock,
- toBlock: toBlock,
- );
- return client.events(filter).map((_i1.FilterEvent result) {
- final decoded = event.decodeResults(
- result.topics!,
- result.data!,
- );
- return RateProposed(
- decoded,
- result,
- );
- });
- }
-
- /// Returns a live stream of all Saved events emitted by this contract.
- Stream<Saved> savedEvents({
- _i1.BlockNum? fromBlock,
- _i1.BlockNum? toBlock,
- }) {
- final event = self.event('Saved');
- final filter = _i1.FilterOptions.events(
- contract: self,
- event: event,
- fromBlock: fromBlock,
- toBlock: toBlock,
- );
- return client.events(filter).map((_i1.FilterEvent result) {
- final decoded = event.decodeResults(
- result.topics!,
- result.data!,
- );
- return Saved(
- decoded,
- result,
- );
- });
- }
-
- /// Returns a live stream of all Withdrawn events emitted by this contract.
- Stream<Withdrawn> withdrawnEvents({
- _i1.BlockNum? fromBlock,
- _i1.BlockNum? toBlock,
- }) {
- final event = self.event('Withdrawn');
- final filter = _i1.FilterOptions.events(
- contract: self,
- event: event,
- fromBlock: fromBlock,
- toBlock: toBlock,
- );
- return client.events(filter).map((_i1.FilterEvent result) {
- final decoded = event.decodeResults(
- result.topics!,
- result.data!,
- );
- return Withdrawn(
- decoded,
- result,
- );
- });
- }
-}
-
-class Savings {
- Savings(List<dynamic> response)
- : saved = (response[0] as BigInt),
- ticks = (response[1] as BigInt);
-
- final BigInt saved;
-
- final BigInt ticks;
-}
-
-class InterestCollected {
- InterestCollected(
- List<dynamic> response,
- this.event,
- ) : account = (response[0] as _i1.EthereumAddress),
- interest = (response[1] as BigInt);
-
- final _i1.EthereumAddress account;
-
- final BigInt interest;
-
- final _i1.FilterEvent event;
-}
-
-class RateChanged {
- RateChanged(
- List<dynamic> response,
- this.event,
- ) : newRate = (response[0] as BigInt);
-
- final BigInt newRate;
-
- final _i1.FilterEvent event;
-}
-
-class RateProposed {
- RateProposed(
- List<dynamic> response,
- this.event,
- ) : who = (response[0] as _i1.EthereumAddress),
- nextRate = (response[1] as BigInt),
- nextChange = (response[2] as BigInt);
-
- final _i1.EthereumAddress who;
-
- final BigInt nextRate;
-
- final BigInt nextChange;
-
- final _i1.FilterEvent event;
-}
-
-class Saved {
- Saved(
- List<dynamic> response,
- this.event,
- ) : account = (response[0] as _i1.EthereumAddress),
- amount = (response[1] as BigInt);
-
- final _i1.EthereumAddress account;
-
- final BigInt amount;
-
- final _i1.FilterEvent event;
-}
-
-class Withdrawn {
- Withdrawn(
- List<dynamic> response,
- this.event,
- ) : account = (response[0] as _i1.EthereumAddress),
- amount = (response[1] as BigInt);
-
- final _i1.EthereumAddress account;
-
- final BigInt amount;
-
- final _i1.FilterEvent event;
-}
diff --git a/cw_ethereum/lib/ethereum_client.dart b/cw_ethereum/lib/ethereum_client.dart
deleted file mode 100644
index 259e7d1..0000000
--- a/cw_ethereum/lib/ethereum_client.dart
+++ /dev/null
@@ -1,76 +0,0 @@
-import 'dart:convert';
-import 'dart:developer';
-import 'dart:typed_data';
-
-import 'package:cw_evm/evm_chain_client.dart';
-import 'package:cw_evm/.secrets.g.dart' as secrets;
-import 'package:cw_evm/evm_chain_transaction_model.dart';
-import 'package:web3dart/web3dart.dart';
-
-class EthereumClient extends EVMChainClient {
- @override
- int get chainId => 1;
-
- @override
- Uint8List prepareSignedTransactionForSending(Uint8List signedTransaction) =>
- prependTransactionType(0x02, signedTransaction);
-
- @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 (jsonResponse['result'] is String) {
- log(jsonResponse['result']);
- return [];
- }
-
- 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 (e) {
- log(e.toString());
- 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 (e) {
- log(e.toString());
- return [];
- }
- }
-}
diff --git a/cw_ethereum/lib/ethereum_mnemonics_exception.dart b/cw_ethereum/lib/ethereum_mnemonics_exception.dart
deleted file mode 100644
index b91a15c..0000000
--- a/cw_ethereum/lib/ethereum_mnemonics_exception.dart
+++ /dev/null
@@ -1,5 +0,0 @@
-class EthereumMnemonicIsIncorrectException implements Exception {
- @override
- String toString() =>
- 'Ethereum mnemonic has incorrect format. Mnemonic should contain 12 or 24 words separated by space.';
-}
diff --git a/cw_ethereum/lib/ethereum_transaction_history.dart b/cw_ethereum/lib/ethereum_transaction_history.dart
deleted file mode 100644
index fbb8ab7..0000000
--- a/cw_ethereum/lib/ethereum_transaction_history.dart
+++ /dev/null
@@ -1,19 +0,0 @@
-import 'dart:core';
-import 'package:cw_ethereum/ethereum_transaction_info.dart';
-import 'package:cw_evm/evm_chain_transaction_history.dart';
-import 'package:cw_evm/evm_chain_transaction_info.dart';
-
-class EthereumTransactionHistory extends EVMChainTransactionHistory {
- EthereumTransactionHistory({
- required super.walletInfo,
- required super.password,
- required super.encryptionFileUtils,
- });
-
- @override
- String getTransactionHistoryFileName() => 'transactions.json';
-
- @override
- EVMChainTransactionInfo getTransactionInfo(Map<String, dynamic> val) =>
- EthereumTransactionInfo.fromJson(val);
-}
diff --git a/cw_ethereum/lib/ethereum_transaction_info.dart b/cw_ethereum/lib/ethereum_transaction_info.dart
deleted file mode 100644
index 610f043..0000000
--- a/cw_ethereum/lib/ethereum_transaction_info.dart
+++ /dev/null
@@ -1,43 +0,0 @@
-import 'package:cw_core/transaction_direction.dart';
-import 'package:cw_evm/evm_chain_transaction_info.dart';
-
-class EthereumTransactionInfo extends EVMChainTransactionInfo {
- EthereumTransactionInfo({
- 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.evmSignatureName,
- super.exponent,
- });
-
- factory EthereumTransactionInfo.fromJson(Map<String, dynamic> data) {
- return EthereumTransactionInfo(
- 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'],
- evmSignatureName: data['evmSignatureName'],
- contractAddress: data['contractAddress'],
- );
- }
-
- @override
- String get feeCurrency => 'ETH';
-}
diff --git a/cw_ethereum/lib/ethereum_wallet.dart b/cw_ethereum/lib/ethereum_wallet.dart
deleted file mode 100644
index a77bff6..0000000
--- a/cw_ethereum/lib/ethereum_wallet.dart
+++ /dev/null
@@ -1,199 +0,0 @@
-import 'dart:convert';
-
-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_ethereum/default_ethereum_erc20_tokens.dart';
-import 'package:cw_ethereum/ethereum_client.dart';
-import 'package:cw_ethereum/ethereum_transaction_history.dart';
-import 'package:cw_ethereum/ethereum_transaction_info.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_transaction_priority.dart';
-import 'package:cw_evm/evm_chain_wallet.dart';
-import 'package:cw_evm/evm_erc20_balance.dart';
-import 'package:web3dart/web3dart.dart';
-
-class EthereumWallet extends EVMChainWallet {
- EthereumWallet({
- required super.client,
- required super.password,
- required super.walletInfo,
- required super.derivationInfo,
- super.mnemonic,
- super.initialBalance,
- super.privateKey,
- required super.encryptionFileUtils,
- super.passphrase,
- }) : super(nativeCurrency: CryptoCurrency.eth);
-
- @override
- int getTotalPriorityFee(EVMChainTransactionPriority priority) {
- return EtherAmount.fromInt(EtherUnit.gwei, priority.tip).getInWei.toInt();
- }
-
- @override
- void addInitialTokens() {
- final initialErc20Tokens = DefaultEthereumErc20Tokens().initialErc20Tokens;
-
- 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
- Future<bool> checkIfScanProviderIsEnabled() async {
- bool isEtherscanEnabled = (await sharedPrefs.future).getBool("use_etherscan") ?? true;
- return isEtherscanEnabled;
- }
-
- @override
- Future<void> initErc20TokensBox() async {
- // This is for ethereum wallets,
- // Other wallets would override and initialize their respective boxes with their boxNames.
- await movePreviousErc20BoxConfigsToNewBox();
- }
-
- /// Majorly for backward compatibility for previous configs that have been set.
- Future<void> movePreviousErc20BoxConfigsToNewBox() async {
- // Opens a box specific to this wallet
- evmChainErc20TokensBox = await CakeHive.openBox<Erc20Token>(
- "${walletInfo.name.replaceAll(" ", "_")}_${Erc20Token.ethereumBoxName}");
-
- //Open the previous token configs box
- erc20TokensBox = await CakeHive.openBox<Erc20Token>(Erc20Token.boxName);
-
- // Check if it's empty, if it is, we stop the flow and return.
- if (erc20TokensBox.isEmpty) {
- // If it's empty, but the new wallet specific box is also empty,
- // we load the initial tokens to the new box.
- if (evmChainErc20TokensBox.isEmpty) addInitialTokens();
- return;
- }
-
- final allValues = erc20TokensBox.values.toList();
-
- // Clear and delete the old token box
- await erc20TokensBox.clear();
- await erc20TokensBox.deleteFromDisk();
-
- // Add all the previous tokens with configs to the new box
- await evmChainErc20TokensBox.addAll(allValues);
- }
-
- @override
- List<String> get getDefaultTokenContractAddresses =>
- DefaultEthereumErc20Tokens().initialErc20Tokens.map((e) => e.contractAddress).toList();
-
- @override
- EVMChainTransactionInfo getTransactionInfo(
- EVMChainTransactionModel transactionModel, String address) {
- final model = EthereumTransactionInfo(
- 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,
- evmSignatureName: transactionModel.evmSignatureName,
- contractAddress: transactionModel.contractAddress,
- );
- return model;
- }
-
- @override
- String getTransactionHistoryFileName() => '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
- EVMChainTransactionHistory setUpTransactionHistory(
- WalletInfo walletInfo, String password, EncryptionFileUtils encryptionFileUtils) {
- return EthereumTransactionHistory(
- walletInfo: walletInfo, password: password, encryptionFileUtils: encryptionFileUtils);
- }
-
- static Future<EthereumWallet> 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 EthereumWallet(
- walletInfo: walletInfo,
- derivationInfo: await walletInfo.getDerivationInfo(),
- password: password,
- mnemonic: keysData.mnemonic,
- privateKey: keysData.privateKey,
- passphrase: keysData.passphrase,
- initialBalance: balance,
- client: EthereumClient(),
- encryptionFileUtils: encryptionFileUtils,
- );
- }
-}
diff --git a/cw_ethereum/lib/ethereum_wallet_service.dart b/cw_ethereum/lib/ethereum_wallet_service.dart
deleted file mode 100644
index 1976c27..0000000
--- a/cw_ethereum/lib/ethereum_wallet_service.dart
+++ /dev/null
@@ -1,170 +0,0 @@
-import 'package:bip39/bip39.dart' as bip39;
-import 'package:cw_core/encryption_file_utils.dart';
-import 'package:cw_core/wallet_base.dart';
-import 'package:cw_core/wallet_info.dart';
-import 'package:cw_core/wallet_type.dart';
-import 'package:cw_ethereum/ethereum_client.dart';
-import 'package:cw_ethereum/ethereum_mnemonics_exception.dart';
-import 'package:cw_ethereum/ethereum_wallet.dart';
-import 'package:cw_evm/evm_chain_wallet_creation_credentials.dart';
-import 'package:cw_evm/evm_chain_wallet_service.dart';
-
-class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
- EthereumWalletService(super.isDirect, {required this.client});
-
- late EthereumClient client;
-
- @override
- WalletType getType() => WalletType.ethereum;
-
- @override
- Future<EthereumWallet> create(EVMChainNewWalletCredentials credentials, {bool? isTestnet}) async {
- final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
-
- final mnemonic = credentials.mnemonic ?? bip39.generateMnemonic(strength: strength);
-
- final wallet = EthereumWallet(
- 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<EthereumWallet> 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 EthereumWallet.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 EthereumWallet.open(
- name: name,
- password: password,
- walletInfo: walletInfo,
- 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 EthereumWallet.open(
- password: password,
- name: currentName,
- walletInfo: currentWalletInfo,
- encryptionFileUtils: encryptionFileUtilsFor(isDirect),
- );
-
- await currentWallet.renameWalletFiles(newName);
- await saveBackup(newName);
-
- final newWalletInfo = currentWalletInfo;
- newWalletInfo.id = WalletBase.idFor(newName, getType());
- newWalletInfo.name = newName;
-
- await newWalletInfo.save();
- }
-
- @override
- Future<EthereumWallet> restoreFromHardwareWallet(
- EVMChainRestoreWalletFromHardware credentials) async {
- final di = await credentials.walletInfo!.getDerivationInfo();
- di.derivationType = DerivationType.bip39;
- di.derivationPath = "m/44'/60'/${credentials.hwAccountData.accountIndex}'/0/0";
- await di.save();
- credentials.walletInfo!.hardwareWalletType = credentials.hardwareWalletType;
- credentials.walletInfo!.address = credentials.hwAccountData.address;
- credentials.walletInfo!.save();
-
- final wallet = EthereumWallet(
- walletInfo: credentials.walletInfo!,
- derivationInfo: di,
- password: credentials.password!,
- client: client,
- encryptionFileUtils: encryptionFileUtilsFor(isDirect),
- );
-
- await wallet.init();
- wallet.addInitialTokens();
- await wallet.save();
-
- return wallet;
- }
-
- @override
- Future<EthereumWallet> restoreFromKeys(EVMChainRestoreWalletFromPrivateKey credentials,
- {bool? isTestnet}) async {
- final wallet = EthereumWallet(
- 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<EthereumWallet> restoreFromSeed(EVMChainRestoreWalletFromSeedCredentials credentials,
- {bool? isTestnet}) async {
- if (!bip39.validateMnemonic(credentials.mnemonic)) {
- throw EthereumMnemonicIsIncorrectException();
- }
-
- final wallet = EthereumWallet(
- 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;
- }
-}
diff --git a/cw_ethereum/pubspec.yaml b/cw_ethereum/pubspec.yaml
deleted file mode 100644
index 1da1e3b..0000000
--- a/cw_ethereum/pubspec.yaml
+++ /dev/null
@@ -1,43 +0,0 @@
-name: cw_ethereum
-description: A new Flutter package project.
-version: 0.0.1
-publish_to: none
-author: Cake Wallet
-homepage: https://cakewallet.com
-
-environment:
- sdk: ^3.5.0
- flutter: ">=1.17.0"
-
-dependencies:
- flutter:
- sdk: flutter
- web3dart: ^2.7.1
- cw_core:
- path: ../cw_core
- cw_evm:
- path: ../cw_evm
- hive: ^2.2.3
-
-dependency_overrides:
- web3dart:
- git:
- url: https://github.com/cake-tech/web3dart.git
- ref: cake
- watcher: ^1.1.0
-
-dev_dependencies:
- flutter_test:
- sdk: flutter
- build_runner: ^2.4.15
-
-flutter:
- # assets:
- # - images/a_dot_burr.jpeg
- # - images/a_dot_ham.jpeg
- # fonts:
- # - family: Schyler
- # fonts:
- # - asset: fonts/Schyler-Regular.ttf
- # - asset: fonts/Schyler-Italic.ttf
- # style: italic
diff --git a/cw_ethereum/test/cw_ethereum_test.dart b/cw_ethereum/test/cw_ethereum_test.dart
deleted file mode 100644
index 72026a4..0000000
--- a/cw_ethereum/test/cw_ethereum_test.dart
+++ /dev/null
@@ -1,12 +0,0 @@
-import 'package:flutter_test/flutter_test.dart';
-
-import 'package:cw_ethereum/cw_ethereum.dart';
-
-void main() {
- test('adds one to input values', () {
- final calculator = Calculator();
- expect(calculator.addOne(2), 3);
- expect(calculator.addOne(-7), -6);
- expect(calculator.addOne(0), 1);
- });
-}
diff --git a/cw_evm/lib/clients/arbitrum_client.dart b/cw_evm/lib/clients/arbitrum_client.dart
new file mode 100644
index 0000000..5d25aae
--- /dev/null
+++ b/cw_evm/lib/clients/arbitrum_client.dart
@@ -0,0 +1,46 @@
+import 'package:cw_evm/clients/evm_chain_client.dart';
+import 'package:flutter/foundation.dart';
+import 'package:web3dart/web3dart.dart';
+
+class ArbitrumClient extends EVMChainClient {
+ ArbitrumClient() : super(chainId: 42161);
+
+ @override
+ Transaction createTransaction({
+ required EthereumAddress from,
+ required EthereumAddress to,
+ required EtherAmount amount,
+ EtherAmount? maxPriorityFeePerGas,
+ Uint8List? data,
+ int? maxGas,
+ EtherAmount? gasPrice,
+ EtherAmount? maxFeePerGas,
+ int? nonce,
+ }) {
+ 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,
+ nonce: nonce,
+ // maxFeePerGas: maxFeePerGas,
+ // maxPriorityFeePerGas: maxPriorityFeePerGas,
+ );
+ }
+
+ @override
+ Uint8List prepareSignedTransactionForSending(Uint8List signedTransaction) => signedTransaction;
+
+ @override
+ int get chainId => 42161;
+}
+
diff --git a/cw_evm/lib/clients/base_client.dart b/cw_evm/lib/clients/base_client.dart
new file mode 100644
index 0000000..859cc7f
--- /dev/null
+++ b/cw_evm/lib/clients/base_client.dart
@@ -0,0 +1,46 @@
+import 'package:cw_evm/clients/evm_chain_client.dart';
+import 'package:flutter/foundation.dart';
+import 'package:web3dart/web3dart.dart';
+
+class BaseClient extends EVMChainClient {
+ BaseClient() : super(chainId: 8453);
+
+ @override
+ Transaction createTransaction({
+ required EthereumAddress from,
+ required EthereumAddress to,
+ required EtherAmount amount,
+ EtherAmount? maxPriorityFeePerGas,
+ Uint8List? data,
+ int? maxGas,
+ EtherAmount? gasPrice,
+ EtherAmount? maxFeePerGas,
+ int? nonce,
+ }) {
+ 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,
+ nonce: nonce,
+ // maxFeePerGas: maxFeePerGas,
+ // maxPriorityFeePerGas: maxPriorityFeePerGas,
+ );
+ }
+
+ @override
+ Uint8List prepareSignedTransactionForSending(Uint8List signedTransaction) => signedTransaction;
+
+ @override
+ int get chainId => 8453;
+}
+
diff --git a/cw_evm/lib/clients/ethereum_client.dart b/cw_evm/lib/clients/ethereum_client.dart
new file mode 100644
index 0000000..eb0f5dd
--- /dev/null
+++ b/cw_evm/lib/clients/ethereum_client.dart
@@ -0,0 +1,13 @@
+import 'dart:typed_data';
+
+import 'package:cw_evm/clients/evm_chain_client.dart';
+import 'package:web3dart/web3dart.dart';
+
+class EthereumClient extends EVMChainClient {
+ EthereumClient() : super(chainId: 1);
+
+ @override
+ Uint8List prepareSignedTransactionForSending(Uint8List signedTransaction) =>
+ prependTransactionType(0x02, signedTransaction);
+}
+
diff --git a/cw_evm/lib/clients/evm_chain_client.dart b/cw_evm/lib/clients/evm_chain_client.dart
new file mode 100644
index 0000000..861a59d
--- /dev/null
+++ b/cw_evm/lib/clients/evm_chain_client.dart
@@ -0,0 +1,599 @@
+import 'dart:async';
+import 'dart:convert';
+import 'dart:developer';
+
+import 'package:cw_core/crypto_currency.dart';
+import 'package:cw_core/erc20_token.dart';
+import 'package:cw_core/node.dart';
+import 'package:cw_core/utils/print_verbose.dart';
+import 'package:cw_core/utils/proxy_wrapper.dart';
+import 'package:cw_evm/evm_chain_transaction_model.dart';
+import 'package:cw_evm/evm_chain_transaction_priority.dart';
+import 'package:cw_evm/evm_erc20_balance.dart';
+import 'package:cw_evm/pending_evm_chain_transaction.dart';
+import 'package:cw_evm/.secrets.g.dart' as secrets;
+import 'package:cw_evm/utils/evm_chain_utils.dart';
+import 'package:flutter/foundation.dart';
+import 'package:hex/hex.dart' as hex;
+import 'package:web3dart/web3dart.dart';
+
+import '../contract/erc20.dart';
+
+class EVMChainClient {
+ late final client = ProxyWrapper().getHttpIOClient();
+ Web3Client? _client;
+ final int _chainId;
+
+ EVMChainClient({required int chainId}) : _chainId = chainId;
+
+ //! Can be overridden by child classes
+
+ int get chainId => _chainId;
+
+ Future<List<EVMChainTransactionModel>> fetchTransactions(String address,
+ {String? contractAddress}) async {
+ try {
+ if (secrets.etherScanApiKey.isEmpty) {
+ printV('Etherscan API key is empty, cannot fetch transactions');
+ return [];
+ }
+
+ /// when adding new chains, make sure they are supported by the same api through https://docs.etherscan.io/supported-chains
+ 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 (jsonResponse['result'] is String) {
+ log(jsonResponse['result']);
+ return [];
+ }
+
+ if (response.statusCode >= 200 && response.statusCode < 300 && jsonResponse['status'] != 0) {
+ final res = (jsonResponse['result'] as List);
+ res.removeWhere((e) => e['value'] == '0');
+
+ // Filter out spam native transactions below 0.00001 ETH (10000000000000 wei)
+ if (contractAddress == null) {
+ final spamThresholdWei = BigInt.from(10000000000000);
+ res.removeWhere((e) {
+ try {
+ final value = BigInt.parse(e['value'] ?? '0');
+ final isIncoming = e['to']?.toLowerCase() == address.toLowerCase() &&
+ e['from']?.toLowerCase() != address.toLowerCase();
+ return isIncoming && value < spamThresholdWei;
+ } catch (_) {
+ return false;
+ }
+ });
+ }
+
+ final symbol = EVMChainUtils.getFeeCurrency(chainId);
+
+ return res
+ .map(
+ (e) => EVMChainTransactionModel.fromJson(e as Map<String, dynamic>, symbol, chainId),
+ )
+ .toList();
+ }
+
+ return [];
+ } catch (e) {
+ log(e.toString());
+ return [];
+ }
+ }
+
+ Future<List<EVMChainTransactionModel>> fetchInternalTransactions(String address) async {
+ try {
+ if (secrets.etherScanApiKey.isEmpty) {
+ printV('Etherscan API key is empty, cannot fetch internal transactions');
+ return [];
+ }
+
+ 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) {
+ final symbol = EVMChainUtils.getFeeCurrency(chainId);
+
+ return (jsonResponse['result'] as List)
+ .map((e) =>
+ EVMChainTransactionModel.fromJson(e as Map<String, dynamic>, symbol, chainId))
+ .toList();
+ }
+
+ printV(
+ 'Etherscan API returned invalid response for internal transactions: status=${jsonResponse['status']}, statusCode=${response.statusCode}');
+ return [];
+ } catch (e, stackTrace) {
+ printV('Error fetching internal transactions: ${e.toString()}');
+ printV('Stack trace: ${stackTrace.toString()}');
+ return [];
+ }
+ }
+
+ Uint8List prepareSignedTransactionForSending(Uint8List signedTransaction) => signedTransaction;
+
+ //! Common methods across all child classes
+
+ bool connect(Node node) {
+ try {
+ Uri? rpcUri;
+ bool isModifiedNodeUri = false;
+
+ if (node.uriRaw.contains('nownodes.io')) {
+ isModifiedNodeUri = true;
+ String nowNodeApiKey = secrets.nowNodesApiKey;
+
+ if (nowNodeApiKey.isEmpty) {
+ printV('NowNodes API key is empty, cannot connect to ${node.uriRaw}');
+ return false;
+ }
+
+ rpcUri = Uri.https(node.uriRaw, '/$nowNodeApiKey');
+ }
+
+ _client = Web3Client(isModifiedNodeUri ? rpcUri!.toString() : node.uri.toString(), client);
+
+ return true;
+ } catch (e) {
+ printV('Error connecting to node ${node.uriRaw}: ${e.toString()}');
+ return false;
+ }
+ }
+
+ void setListeners(EthereumAddress userAddress, Function() onNewTransaction) async {
+ // _client?.pendingTransactions().listen((transactionHash) async {
+ // final transaction = await _client!.getTransactionByHash(transactionHash);
+ //
+ // if (transaction.from.hex == userAddress || transaction.to?.hex == userAddress) {
+ // onNewTransaction();
+ // }
+ // });
+ }
+
+ Future<EtherAmount> getBalance(EthereumAddress address) async {
+ try {
+ return await _client!.getBalance(address);
+ } catch (_) {
+ rethrow;
+ }
+ }
+
+ Future<int> getGasUnitPrice() async {
+ try {
+ final gasPrice = await _client!.getGasPrice();
+
+ return gasPrice.getInWei.toInt();
+ } catch (e) {
+ printV('Error getting gas unit price: ${e.toString()}');
+ rethrow;
+ }
+ }
+
+ Future<int?> getGasBaseFee() async {
+ try {
+ final blockInfo = await _client!.getBlockInformation(isContainFullObj: false);
+ final baseFee = blockInfo.baseFeePerGas;
+
+ return baseFee?.getInWei.toInt();
+ } catch (e) {
+ printV('Error getting gas base fee: ${e.toString()}');
+ return null;
+ }
+ }
+
+ Future<int> getEstimatedGasUnitsForTransaction({
+ required EthereumAddress toAddress,
+ required EthereumAddress senderAddress,
+ required EtherAmount value,
+ String? contractAddress,
+ EtherAmount? gasPrice,
+ EtherAmount? maxFeePerGas,
+ Uint8List? data,
+ }) async {
+ try {
+ if (contractAddress == null) {
+ final estimatedGas = await _client!.estimateGas(
+ sender: senderAddress,
+ to: toAddress,
+ value: value,
+ data: data,
+ );
+
+ return estimatedGas.toInt();
+ } else {
+ final contract = DeployedContract(
+ ethereumContractAbi,
+ EthereumAddress.fromHex(contractAddress),
+ );
+
+ final transfer = contract.function('transfer');
+
+ // Estimate gas units
+ final gasEstimate = await _client!.estimateGas(
+ sender: senderAddress,
+ to: EthereumAddress.fromHex(contractAddress),
+ data: data ??
+ transfer.encodeCall([
+ toAddress,
+ value.getInWei,
+ ]),
+ );
+
+ return gasEstimate.toInt();
+ }
+ } catch (_) {
+ return 0;
+ }
+ }
+
+ Uint8List getEncodedDataForApprovalTransaction({
+ required EthereumAddress toAddress,
+ required EtherAmount value,
+ required EthereumAddress contractAddress,
+ }) {
+ final contract = DeployedContract(ethereumContractAbi, contractAddress);
+
+ final approve = contract.function('approve');
+
+ return approve.encodeCall([
+ toAddress,
+ value.getInWei,
+ ]);
+ }
+
+ Future<PendingEVMChainTransaction> signTransaction({
+ required Credentials privateKey,
+ required String toAddress,
+ required BigInt amount,
+ required BigInt gasFee,
+ required int estimatedGasUnits,
+ required int maxFeePerGas,
+ required EVMChainTransactionPriority? priority,
+ required CryptoCurrency currency,
+ required String feeCurrency,
+ required int exponent,
+ String? contractAddress,
+ String? data,
+ int? gasPrice,
+ bool useBlinkProtection = true,
+ }) async {
+ assert(currency == CryptoCurrency.eth ||
+ currency == CryptoCurrency.maticpoly ||
+ currency == CryptoCurrency.baseEth ||
+ currency == CryptoCurrency.arbEth ||
+ contractAddress != null);
+
+ bool isNativeToken = currency == CryptoCurrency.eth ||
+ currency == CryptoCurrency.maticpoly ||
+ currency == CryptoCurrency.baseEth ||
+ currency == CryptoCurrency.arbEth;
+
+ // Get nonce with "pending" block tag to include pending transactions
+ // This prevents "Nonce too low" errors when sending multiple transactions quickly
+ final nonce = await _client!.getTransactionCount(
+ privateKey.address,
+ atBlock: const BlockNum.pending(),
+ );
+
+ final Transaction transaction = createTransaction(
+ from: privateKey.address,
+ to: EthereumAddress.fromHex(toAddress),
+ 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,
+ maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
+ gasPrice: gasPrice != null ? EtherAmount.fromInt(EtherUnit.wei, gasPrice) : null,
+ nonce: nonce,
+ );
+
+ Uint8List signedTransaction;
+
+ final Function _sendTransaction;
+
+ if (isNativeToken) {
+ signedTransaction = await _client!.signTransaction(privateKey, transaction, chainId: chainId);
+ } else {
+ final erc20 = ERC20(
+ client: _client!,
+ address: EthereumAddress.fromHex(contractAddress!),
+ chainId: chainId,
+ );
+
+ signedTransaction = await erc20.transfer(
+ EthereumAddress.fromHex(toAddress),
+ amount,
+ credentials: privateKey,
+ transaction: transaction,
+ );
+ }
+
+ _sendTransaction = () async =>
+ await sendTransaction(signedTransaction, useBlinkProtection: useBlinkProtection);
+
+ return PendingEVMChainTransaction(
+ signedTransaction: prepareSignedTransactionForSending(signedTransaction),
+ amount: amount.toString(),
+ fee: gasFee,
+ feeCurrency: feeCurrency,
+ sendTransaction: _sendTransaction,
+ exponent: exponent,
+ );
+ }
+
+ Future<PendingEVMChainTransaction> signApprovalTransaction({
+ required Credentials privateKey,
+ required String spender,
+ required BigInt amount,
+ required BigInt gasFee,
+ required String feeCurrency,
+ required int estimatedGasUnits,
+ required int maxFeePerGas,
+ required EVMChainTransactionPriority? priority,
+ required int exponent,
+ required String contractAddress,
+ int? gasPrice,
+ bool useBlinkProtection = true,
+ }) async {
+ final nonce = await _client!.getTransactionCount(
+ privateKey.address,
+ atBlock: const BlockNum.pending(),
+ );
+
+ final Transaction transaction = createTransaction(
+ from: privateKey.address,
+ to: EthereumAddress.fromHex(contractAddress),
+ maxPriorityFeePerGas:
+ priority != null ? EtherAmount.fromInt(EtherUnit.gwei, priority.tip) : null,
+ amount: EtherAmount.zero(),
+ maxGas: estimatedGasUnits,
+ maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
+ gasPrice: gasPrice != null ? EtherAmount.fromInt(EtherUnit.wei, gasPrice) : null,
+ nonce: nonce,
+ );
+
+ final erc20 = ERC20(
+ client: _client!,
+ address: EthereumAddress.fromHex(contractAddress),
+ chainId: chainId,
+ );
+
+ final signedTransaction = await erc20.approve(
+ EthereumAddress.fromHex(spender),
+ amount,
+ credentials: privateKey,
+ transaction: transaction,
+ );
+
+ return PendingEVMChainTransaction(
+ signedTransaction: prepareSignedTransactionForSending(signedTransaction),
+ amount: amount.toString(),
+ fee: gasFee,
+ feeCurrency: feeCurrency,
+ sendTransaction: () =>
+ sendTransaction(signedTransaction, useBlinkProtection: useBlinkProtection),
+ exponent: exponent,
+ isInfiniteApproval: amount.toRadixString(16) ==
+ 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
+ );
+ }
+
+ Transaction createTransaction({
+ required EthereumAddress from,
+ required EthereumAddress to,
+ required EtherAmount amount,
+ EtherAmount? maxPriorityFeePerGas,
+ EtherAmount? gasPrice,
+ EtherAmount? maxFeePerGas,
+ Uint8List? data,
+ int? maxGas,
+ int? nonce,
+ }) {
+ return Transaction(
+ from: from,
+ to: to,
+ maxPriorityFeePerGas: maxPriorityFeePerGas,
+ value: amount,
+ data: data,
+ maxGas: maxGas,
+ gasPrice: gasPrice,
+ maxFeePerGas: maxFeePerGas,
+ nonce: nonce,
+ );
+ }
+
+ String _blinkUrl(String apiKey) => 'https://eth.blinklabs.xyz/v1/$apiKey';
+
+ Future<String> sendTransaction(
+ Uint8List signedTransaction, {
+ bool useBlinkProtection = false,
+ }) async {
+ final prepared = prepareSignedTransactionForSending(signedTransaction);
+
+ if (useBlinkProtection && secrets.blinkApiKey.isNotEmpty) {
+ final blinkClient = Web3Client(_blinkUrl(secrets.blinkApiKey), client);
+ try {
+ return await blinkClient.sendRawTransaction(prepared);
+ } finally {
+ await blinkClient.dispose();
+ }
+ }
+
+ return await _client!.sendRawTransaction(prepared);
+ }
+
+ Future getTransactionDetails(String transactionHash) async {
+ // Wait for the transaction receipt to become available
+ TransactionReceipt? receipt;
+ while (receipt == null) {
+ receipt = await _client!.getTransactionReceipt(transactionHash);
+ await Future.delayed(const Duration(seconds: 1));
+ }
+
+ // Print the receipt information
+ log('Transaction Hash: ${receipt.transactionHash}');
+ log('Block Hash: ${receipt.blockHash}');
+ log('Block Number: ${receipt.blockNumber}');
+ log('Gas Used: ${receipt.gasUsed}');
+
+ /*
+ Transaction Hash: [112, 244, 4, 238, 89, 199, 171, 191, 210, 236, 110, 42, 185, 202, 220, 21, 27, 132, 123, 221, 137, 90, 77, 13, 23, 43, 12, 230, 93, 63, 221, 116]
+ I/flutter ( 4474): Block Hash: [149, 44, 250, 119, 111, 104, 82, 98, 17, 89, 30, 190, 25, 44, 218, 118, 127, 189, 241, 35, 213, 106, 25, 95, 195, 37, 55, 131, 185, 180, 246, 200]
+ I/flutter ( 4474): Block Number: 17120242
+ I/flutter ( 4474): Gas Used: 21000
+ */
+
+ // Wait for the transaction receipt to become available
+ TransactionInformation? transactionInformation;
+ while (transactionInformation == null) {
+ log("********************************");
+ transactionInformation = await _client!.getTransactionByHash(transactionHash);
+ await Future.delayed(const Duration(seconds: 1));
+ }
+ // Print the receipt information
+ log('Transaction Hash: ${transactionInformation.hash}');
+ log('Block Hash: ${transactionInformation.blockHash}');
+ log('Block Number: ${transactionInformation.blockNumber}');
+ log('Gas Used: ${transactionInformation.gas}');
+
+ /*
+ Transaction Hash: 0x70f404ee59c7abbfd2ec6e2ab9cadc151b847bdd895a4d0d172b0ce65d3fdd74
+ I/flutter ( 4474): Block Hash: 0x952cfa776f68526211591ebe192cda767fbdf123d56a195fc3253783b9b4f6c8
+ I/flutter ( 4474): Block Number: 17120242
+ I/flutter ( 4474): Gas Used: 53000
+ */
+ }
+
+ Future<EVMChainERC20Balance> fetchERC20Balances(
+ EthereumAddress userAddress, String contractAddress) async {
+ try {
+ final erc20 = ERC20(address: EthereumAddress.fromHex(contractAddress), client: _client!);
+ final balance = await erc20.balanceOf(userAddress);
+
+ int exponent = (await erc20.decimals()).toInt();
+
+ return EVMChainERC20Balance(balance, exponent: exponent);
+ } on RangeError catch (_) {
+ throw Exception('Invalid token contract for this network.');
+ } catch (e) {
+ throw Exception('Could not fetch balances: ${e.toString()}');
+ }
+ }
+
+ Future<Erc20Token?> getErc20Token(String contractAddress, String chainName) async {
+ try {
+ final token = await getErcTokenInfoFromNode(contractAddress, chainName);
+
+ if (token == null || token.name.isEmpty || token.symbol.isEmpty) {
+ return await getErc20TokenFromMoralis(contractAddress, chainName);
+ }
+
+ return token;
+ } catch (e) {
+ try {
+ return await getErc20TokenFromMoralis(contractAddress, chainName);
+ } catch (e) {
+ return null;
+ }
+ }
+ }
+
+ Future<Erc20Token?> getErc20TokenFromMoralis(String contractAddress, String chainName) async {
+ final uri = Uri.https(
+ 'deep-index.moralis.io',
+ '/api/v2.2/erc20/metadata',
+ {
+ "chain": chainName,
+ "addresses": contractAddress,
+ },
+ );
+
+ final response = await client.get(
+ uri,
+ headers: {
+ "Accept": "application/json",
+ "X-API-Key": secrets.moralisApiKey,
+ },
+ );
+
+ final decodedResponse = jsonDecode(response.body)[0] as Map<String, dynamic>;
+
+ final symbol = (decodedResponse['symbol'] ?? '') as String;
+ String filteredSymbol = symbol.replaceFirst(RegExp('^\\\$'), '');
+
+ final name = (decodedResponse['name'] ?? '').toString();
+ final decimal = decodedResponse['decimals'] ?? '0';
+ final iconPath = decodedResponse['logo'] ?? '';
+
+ return Erc20Token(
+ name: name,
+ symbol: filteredSymbol,
+ contractAddress: contractAddress,
+ decimal: int.tryParse(decimal) ?? 0,
+ iconPath: iconPath,
+ );
+ }
+
+ Future<Erc20Token?> getErcTokenInfoFromNode(String contractAddress, String chainName) async {
+ final erc20 = ERC20(address: EthereumAddress.fromHex(contractAddress), client: _client!);
+ final name = await erc20.name();
+ final symbol = await erc20.symbol();
+ final decimal = await erc20.decimals();
+
+ return Erc20Token(
+ name: name,
+ symbol: symbol,
+ contractAddress: contractAddress,
+ decimal: decimal.toInt(),
+ );
+ }
+
+ Uint8List hexToBytes(String hexString) {
+ return Uint8List.fromList(
+ hex.HEX.decode(hexString.startsWith('0x') ? hexString.substring(2) : hexString));
+ }
+
+ void stop() {
+ _client?.dispose();
+ }
+
+ Web3Client? getWeb3Client() {
+ return _client;
+ }
+
+// Future<int> _getDecimalPlacesForContract(DeployedContract contract) async {
+// final String abi = await rootBundle.loadString("assets/abi_json/erc20_abi.json");
+// final contractAbi = ContractAbi.fromJson(abi, "ERC20");
+//
+// final contract = DeployedContract(
+// contractAbi,
+// EthereumAddress.fromHex(_erc20Currencies[erc20Currency]!),
+// );
+// final decimalsFunction = contract.function('decimals');
+// final decimals = await _client!.call(
+// contract: contract,
+// function: decimalsFunction,
+// params: [],
+// );
+//
+// int exponent = int.parse(decimals.first.toString());
+// return exponent;
+// }
+}
diff --git a/cw_evm/lib/clients/polygon_client.dart b/cw_evm/lib/clients/polygon_client.dart
new file mode 100644
index 0000000..56ee9fb
--- /dev/null
+++ b/cw_evm/lib/clients/polygon_client.dart
@@ -0,0 +1,46 @@
+import 'package:cw_evm/clients/evm_chain_client.dart';
+import 'package:flutter/foundation.dart';
+import 'package:web3dart/web3dart.dart';
+
+class PolygonClient extends EVMChainClient {
+ PolygonClient() : super(chainId: 137);
+
+ @override
+ Transaction createTransaction({
+ required EthereumAddress from,
+ required EthereumAddress to,
+ required EtherAmount amount,
+ EtherAmount? maxPriorityFeePerGas,
+ Uint8List? data,
+ int? maxGas,
+ EtherAmount? gasPrice,
+ EtherAmount? maxFeePerGas,
+ int? nonce,
+ }) {
+ 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,
+ nonce: nonce,
+ // maxFeePerGas: maxFeePerGas,
+ // maxPriorityFeePerGas: maxPriorityFeePerGas,
+ );
+ }
+
+ @override
+ Uint8List prepareSignedTransactionForSending(Uint8List signedTransaction) => signedTransaction;
+
+ @override
+ int get chainId => 137;
+}
+
diff --git a/cw_evm/lib/deuro/deuro_savings.dart b/cw_evm/lib/deuro/deuro_savings.dart
new file mode 100644
index 0000000..e35020c
--- /dev/null
+++ b/cw_evm/lib/deuro/deuro_savings.dart
@@ -0,0 +1,203 @@
+import 'package:cw_core/crypto_currency.dart';
+import 'package:cw_evm/deuro/deuro_savings_contract.dart';
+import 'package:cw_evm/evm_chain_wallet.dart';
+import 'package:cw_evm/contract/erc20.dart';
+import 'package:cw_evm/evm_chain_exceptions.dart';
+import 'package:cw_evm/evm_chain_transaction_priority.dart';
+import 'package:cw_evm/pending_evm_chain_transaction.dart';
+import 'package:web3dart/crypto.dart';
+import 'package:web3dart/web3dart.dart';
+
+const String savingsGatewayAddress = "0x073493d73258C4BEb6542e8dd3e1b2891C972303";
+
+const String dEuroAddress = "0xbA3f535bbCcCcA2A154b573Ca6c5A49BAAE0a3ea";
+const String frontendCode = "0x00000000000000000000000000000000000000000043616b652057616c6c6574";
+
+class DEuro {
+ final SavingsGateway _savingsGateway;
+ final ERC20 _dEuro;
+ final EVMChainWallet _wallet;
+
+ DEuro(EVMChainWallet wallet)
+ : _wallet = wallet,
+ _savingsGateway = _getSavingsGateway(wallet.getWeb3Client()!),
+ _dEuro = _getDEuroToken(wallet.getWeb3Client()!);
+
+ static SavingsGateway _getSavingsGateway(Web3Client client) => SavingsGateway(
+ address: EthereumAddress.fromHex(savingsGatewayAddress),
+ client: client,
+ );
+
+ static ERC20 _getDEuroToken(Web3Client client) => ERC20(
+ address: EthereumAddress.fromHex(dEuroAddress),
+ client: client,
+ );
+
+ EthereumAddress get _address => EthereumAddress.fromHex(_wallet.walletAddresses.primaryAddress);
+
+ Future<BigInt> get savingsBalance async =>
+ (await _savingsGateway.savings(accountOwner: _address)).saved;
+
+ Future<BigInt> get accruedInterest => _savingsGateway.accruedInterest(accountOwner: _address);
+
+ Future<BigInt> get interestRate => _savingsGateway.currentRatePPM();
+
+ Future<BigInt> get approvedBalance => _dEuro.allowance(_address, _savingsGateway.self.address);
+
+ Future<void> _checkEthBalanceForGasFees(EVMChainTransactionPriority priority) async {
+ final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
+ final currentBalance = ethBalance.getInWei;
+
+ final gasFeesModel = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
+ amount: BigInt.zero,
+ contractAddress: _savingsGateway.self.address.hexEip55,
+ receivingAddressHex: _savingsGateway.self.address.hexEip55,
+ priority: priority,
+ data: _savingsGateway.self.abi.functions[17]
+ .encodeCall([BigInt.zero, hexToBytes(frontendCode)]),
+ );
+
+ final estimatedGasFee = BigInt.from(gasFeesModel.estimatedGasFee);
+ final requiredBalance = estimatedGasFee;
+
+ if (currentBalance < requiredBalance) {
+ throw InsufficientGasFeeException(
+ requiredGasFee: requiredBalance,
+ currentBalance: currentBalance,
+ );
+ }
+ }
+
+ Future<PendingEVMChainTransaction> depositSavings(
+ BigInt amount, EVMChainTransactionPriority priority) async {
+ try {
+ await _checkEthBalanceForGasFees(priority);
+
+ final signedTransaction = await _savingsGateway.save(
+ (amount: amount, frontendCode: hexToBytes(frontendCode)),
+ credentials: _wallet.evmChainPrivateKey,
+ );
+
+ final fee = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
+ amount: amount,
+ contractAddress: _savingsGateway.self.address.hexEip55,
+ receivingAddressHex: _savingsGateway.self.address.hexEip55,
+ priority: priority,
+ data: _savingsGateway.self.abi.functions[17].encodeCall([amount, hexToBytes(frontendCode)]),
+ );
+
+ final sendTransaction = () => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
+
+ return PendingEVMChainTransaction(
+ feeCurrency: "ETH",
+ sendTransaction: sendTransaction,
+ signedTransaction: signedTransaction,
+ fee: BigInt.from(fee.estimatedGasFee),
+ amount: amount.toString(),
+ exponent: 18,
+ );
+ } catch (e) {
+ if (e.toString().contains('insufficient funds for gas')) {
+ final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
+ throw InsufficientGasFeeException(currentBalance: ethBalance.getInWei);
+ }
+ rethrow;
+ }
+ }
+
+ Future<PendingEVMChainTransaction> withdrawSavings(
+ BigInt amount, EVMChainTransactionPriority priority) async {
+ try {
+ await _checkEthBalanceForGasFees(priority);
+
+ final signedTransaction = await _savingsGateway.withdraw(
+ (target: _address, amount: amount, frontendCode: hexToBytes(frontendCode)),
+ credentials: _wallet.evmChainPrivateKey,
+ );
+
+ final fee = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
+ amount: amount,
+ contractAddress: _savingsGateway.self.address.hexEip55,
+ receivingAddressHex: _savingsGateway.self.address.hexEip55,
+ priority: priority,
+ data: _savingsGateway.self.abi.functions[24].encodeCall([_address, amount, hexToBytes(frontendCode)]),
+ );
+
+ final sendTransaction = () => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
+
+ return PendingEVMChainTransaction(
+ sendTransaction: sendTransaction,
+ signedTransaction: signedTransaction,
+ fee: BigInt.from(fee.estimatedGasFee),
+ amount: amount.toString(),
+ feeCurrency: "ETH",
+ exponent: 18);
+ } catch (e) {
+ if (e.toString().contains('insufficient funds for gas')) {
+ final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
+ throw InsufficientGasFeeException(currentBalance: ethBalance.getInWei);
+ }
+ rethrow;
+ }
+ }
+
+ Future<PendingEVMChainTransaction> reinvestInterest(EVMChainTransactionPriority priority) async {
+ try {
+ await _checkEthBalanceForGasFees(priority);
+
+ final signedTransaction = await _savingsGateway.refreshBalance((owner: _address),
+ credentials: _wallet.evmChainPrivateKey,
+ );
+
+ final fee = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
+ amount: BigInt.zero,
+ contractAddress: _savingsGateway.self.address.hexEip55,
+ receivingAddressHex: _savingsGateway.self.address.hexEip55,
+ priority: priority,
+ data: _savingsGateway.self.abi.functions[15].encodeCall([_address]),
+ );
+
+ final sendTransaction = () => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
+
+ return PendingEVMChainTransaction(
+ feeCurrency: "ETH",
+ sendTransaction: sendTransaction,
+ signedTransaction: signedTransaction,
+ fee: BigInt.from(fee.estimatedGasFee),
+ amount: 0.toString(),
+ exponent: 18,
+ );
+ } catch (e) {
+ if (e.toString().contains('insufficient funds for gas')) {
+ final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
+ throw InsufficientGasFeeException(currentBalance: ethBalance.getInWei);
+ }
+ rethrow;
+ }
+ }
+
+ // Set an infinite approval to save gas in the future
+ Future<PendingEVMChainTransaction> enableSavings(EVMChainTransactionPriority priority) async {
+ try {
+ await _checkEthBalanceForGasFees(priority);
+
+ return (await _wallet.createApprovalTransaction(
+ BigInt.parse(
+ 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
+ radix: 16,
+ ),
+ _savingsGateway.self.address.hexEip55,
+ CryptoCurrency.deuro,
+ priority,
+ "ETH",
+ )) as PendingEVMChainTransaction;
+ } catch (e) {
+ if (e.toString().contains('insufficient funds for gas')) {
+ final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
+ throw InsufficientGasFeeException(currentBalance: ethBalance.getInWei);
+ }
+ rethrow;
+ }
+ }
+}
+
diff --git a/cw_evm/lib/deuro/deuro_savings_contract.dart b/cw_evm/lib/deuro/deuro_savings_contract.dart
new file mode 100644
index 0000000..77316f4
--- /dev/null
+++ b/cw_evm/lib/deuro/deuro_savings_contract.dart
@@ -0,0 +1,543 @@
+// ignore_for_file: type=lint
+// ignore_for_file: unused_local_variable, unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'package:web3dart/web3dart.dart' as _i1;
+import 'dart:typed_data' as _i2;
+
+final _contractAbi = _i1.ContractAbi.fromJson(
+ '[{"inputs":[{"internalType":"contract IDecentralizedEURO","name":"deuro_","type":"address"},{"internalType":"uint24","name":"initialRatePPM","type":"uint24"},{"internalType":"address","name":"gateway_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ChangeNotReady","type":"error"},{"inputs":[],"name":"ModuleDisabled","type":"error"},{"inputs":[],"name":"NoPendingChange","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"interest","type":"uint256"}],"name":"InterestCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint24","name":"newRate","type":"uint24"}],"name":"RateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"who","type":"address"},{"indexed":false,"internalType":"uint24","name":"nextRate","type":"uint24"},{"indexed":false,"internalType":"uint40","name":"nextChange","type":"uint40"}],"name":"RateProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint192","name":"amount","type":"uint192"}],"name":"Saved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint192","name":"amount","type":"uint192"}],"name":"Withdrawn","type":"event"},{"inputs":[],"name":"GATEWAY","outputs":[{"internalType":"contract IFrontendGateway","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"accountOwner","type":"address"}],"name":"accruedInterest","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"accountOwner","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"accruedInterest","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint192","name":"targetAmount","type":"uint192"},{"internalType":"bytes32","name":"frontendCode","type":"bytes32"}],"name":"adjust","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint192","name":"targetAmount","type":"uint192"}],"name":"adjust","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"applyChange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint192","name":"saved","type":"uint192"},{"internalType":"uint64","name":"ticks","type":"uint64"}],"internalType":"struct Savings.Account","name":"account","type":"tuple"},{"internalType":"uint64","name":"ticks","type":"uint64"}],"name":"calculateInterest","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentRatePPM","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentTicks","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deuro","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"equity","outputs":[{"internalType":"contract IReserve","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextChange","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextRatePPM","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint24","name":"newRatePPM_","type":"uint24"},{"internalType":"address[]","name":"helpers","type":"address[]"}],"name":"proposeChange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"refreshBalance","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"refreshMyBalance","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint192","name":"amount","type":"uint192"},{"internalType":"bytes32","name":"frontendCode","type":"bytes32"}],"name":"save","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint192","name":"amount","type":"uint192"}],"name":"save","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint192","name":"amount","type":"uint192"},{"internalType":"bytes32","name":"frontendCode","type":"bytes32"}],"name":"save","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint192","name":"amount","type":"uint192"}],"name":"save","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"savings","outputs":[{"internalType":"uint192","name":"saved","type":"uint192"},{"internalType":"uint64","name":"ticks","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"ticks","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint192","name":"amount","type":"uint192"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint192","name":"amount","type":"uint192"},{"internalType":"bytes32","name":"frontendCode","type":"bytes32"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]',
+ 'SavingsGateway',
+);
+
+class SavingsGateway extends _i1.GeneratedContract {
+ SavingsGateway({
+ required _i1.EthereumAddress address,
+ required _i1.Web3Client client,
+ int? chainId,
+ }) : super(
+ _i1.DeployedContract(
+ _contractAbi,
+ address,
+ ),
+ client,
+ chainId,
+ );
+
+ /// The optional [atBlock] parameter can be used to view historical data. When
+ /// set, the function will be evaluated in the specified block. By default, the
+ /// latest on-chain block will be used.
+ Future<_i1.EthereumAddress> GATEWAY({_i1.BlockNum? atBlock}) async {
+ final function = self.abi.functions[1];
+ assert(checkSignature(function, '338c5371'));
+ final params = [];
+ final response = await read(
+ function,
+ params,
+ atBlock,
+ );
+ return (response[0] as _i1.EthereumAddress);
+ }
+
+ /// The optional [atBlock] parameter can be used to view historical data. When
+ /// set, the function will be evaluated in the specified block. By default, the
+ /// latest on-chain block will be used.
+ Future<BigInt> accruedInterest({
+ required _i1.EthereumAddress accountOwner,
+ _i1.BlockNum? atBlock,
+ }) async {
+ final function = self.abi.functions[2];
+ assert(checkSignature(function, '77267ec3'));
+ final params = [accountOwner];
+ final response = await read(
+ function,
+ params,
+ atBlock,
+ );
+ return (response[0] as BigInt);
+ }
+
+ /// The optional [atBlock] parameter can be used to view historical data. When
+ /// set, the function will be evaluated in the specified block. By default, the
+ /// latest on-chain block will be used.
+ Future<BigInt> accruedInterest$2(
+ ({_i1.EthereumAddress accountOwner, BigInt timestamp}) args, {
+ _i1.BlockNum? atBlock,
+ }) async {
+ final function = self.abi.functions[3];
+ assert(checkSignature(function, 'a696399d'));
+ final params = [
+ args.accountOwner,
+ args.timestamp,
+ ];
+ final response = await read(
+ function,
+ params,
+ atBlock,
+ );
+ return (response[0] as BigInt);
+ }
+
+ /// The optional [transaction] parameter can be used to override parameters
+ /// like the gas price, nonce and max gas. The `data` and `to` fields will be
+ /// set by the contract.
+ Future<String> adjust(
+ ({BigInt targetAmount, _i2.Uint8List frontendCode}) args, {
+ required _i1.Credentials credentials,
+ _i1.Transaction? transaction,
+ }) async {
+ final function = self.abi.functions[4];
+ assert(checkSignature(function, '753ef93c'));
+ final params = [
+ args.targetAmount,
+ args.frontendCode,
+ ];
+ return write(
+ credentials,
+ transaction,
+ function,
+ params,
+ );
+ }
+
+ /// The optional [atBlock] parameter can be used to view historical data. When
+ /// set, the function will be evaluated in the specified block. By default, the
+ /// latest on-chain block will be used.
+ Future<BigInt> calculateInterest(
+ ({dynamic account, BigInt ticks}) args, {
+ _i1.BlockNum? atBlock,
+ }) async {
+ final function = self.abi.functions[7];
+ assert(checkSignature(function, '7915ce20'));
+ final params = [
+ args.account,
+ args.ticks,
+ ];
+ final response = await read(
+ function,
+ params,
+ atBlock,
+ );
+ return (response[0] as BigInt);
+ }
+
+ /// The optional [atBlock] parameter can be used to view historical data. When
+ /// set, the function will be evaluated in the specified block. By default, the
+ /// latest on-chain block will be used.
+ Future<BigInt> currentRatePPM({_i1.BlockNum? atBlock}) async {
+ final function = self.abi.functions[8];
+ assert(checkSignature(function, '06a7b376'));
+ final params = [];
+ final response = await read(
+ function,
+ params,
+ atBlock,
+ );
+ return (response[0] as BigInt);
+ }
+
+ /// The optional [atBlock] parameter can be used to view historical data. When
+ /// set, the function will be evaluated in the specified block. By default, the
+ /// latest on-chain block will be used.
+ Future<BigInt> currentTicks({_i1.BlockNum? atBlock}) async {
+ final function = self.abi.functions[9];
+ assert(checkSignature(function, 'b079f163'));
+ final params = [];
+ final response = await read(
+ function,
+ params,
+ atBlock,
+ );
+ return (response[0] as BigInt);
+ }
+
+ /// The optional [atBlock] parameter can be used to view historical data. When
+ /// set, the function will be evaluated in the specified block. By default, the
+ /// latest on-chain block will be used.
+ Future<_i1.EthereumAddress> deuro({_i1.BlockNum? atBlock}) async {
+ final function = self.abi.functions[10];
+ assert(checkSignature(function, '82b8eaf5'));
+ final params = [];
+ final response = await read(
+ function,
+ params,
+ atBlock,
+ );
+ return (response[0] as _i1.EthereumAddress);
+ }
+
+ /// The optional [atBlock] parameter can be used to view historical data. When
+ /// set, the function will be evaluated in the specified block. By default, the
+ /// latest on-chain block will be used.
+ Future<_i1.EthereumAddress> equity({_i1.BlockNum? atBlock}) async {
+ final function = self.abi.functions[11];
+ assert(checkSignature(function, '91a0ac6a'));
+ final params = [];
+ final response = await read(
+ function,
+ params,
+ atBlock,
+ );
+ return (response[0] as _i1.EthereumAddress);
+ }
+
+ /// The optional [atBlock] parameter can be used to view historical data. When
+ /// set, the function will be evaluated in the specified block. By default, the
+ /// latest on-chain block will be used.
+ Future<BigInt> nextChange({_i1.BlockNum? atBlock}) async {
+ final function = self.abi.functions[12];
+ assert(checkSignature(function, 'b6f83c17'));
+ final params = [];
+ final response = await read(
+ function,
+ params,
+ atBlock,
+ );
+ return (response[0] as BigInt);
+ }
+
+ /// The optional [atBlock] parameter can be used to view historical data. When
+ /// set, the function will be evaluated in the specified block. By default, the
+ /// latest on-chain block will be used.
+ Future<BigInt> nextRatePPM({_i1.BlockNum? atBlock}) async {
+ final function = self.abi.functions[13];
+ assert(checkSignature(function, '2e4b20ab'));
+ final params = [];
+ final response = await read(
+ function,
+ params,
+ atBlock,
+ );
+ return (response[0] as BigInt);
+ }
+
+ /// The optional [transaction] parameter can be used to override parameters
+ /// like the gas price, nonce and max gas. The `data` and `to` fields will be
+ /// set by the contract.
+ Future<_i2.Uint8List> refreshBalance(
+ ({_i1.EthereumAddress owner}) args, {
+ required _i1.Credentials credentials,
+ _i1.Transaction? transaction,
+ }) async {
+ final function = self.abi.functions[15];
+ assert(checkSignature(function, 'b77cd1c7'));
+ final params = [args.owner];
+ return writeRaw(
+ credentials,
+ transaction,
+ function,
+ params,
+ );
+ }
+
+ /// The optional [transaction] parameter can be used to override parameters
+ /// like the gas price, nonce and max gas. The `data` and `to` fields will be
+ /// set by the contract.
+ Future<_i2.Uint8List> refreshMyBalance({
+ required _i1.Credentials credentials,
+ _i1.Transaction? transaction,
+ }) async {
+ final function = self.abi.functions[16];
+ assert(checkSignature(function, '85bd12d1'));
+ final params = [];
+ return writeRaw(
+ credentials,
+ transaction,
+ function,
+ params,
+ );
+ }
+
+ /// The optional [transaction] parameter can be used to override parameters
+ /// like the gas price, nonce and max gas. The `data` and `to` fields will be
+ /// set by the contract.
+ Future<_i2.Uint8List> save(
+ ({BigInt amount, _i2.Uint8List frontendCode}) args, {
+ required _i1.Credentials credentials,
+ _i1.Transaction? transaction,
+ }) async {
+ final function = self.abi.functions[17];
+ assert(checkSignature(function, '9e2363dc'));
+ final params = [
+ args.amount,
+ args.frontendCode,
+ ];
+ return writeRaw(
+ credentials,
+ transaction,
+ function,
+ params,
+ );
+ }
+
+ /// The optional [transaction] parameter can be used to override parameters
+ /// like the gas price, nonce and max gas. The `data` and `to` fields will be
+ /// set by the contract.
+ Future<_i2.Uint8List> saveTo(
+ ({
+ _i1.EthereumAddress owner,
+ BigInt amount,
+ _i2.Uint8List frontendCode
+ }) args, {
+ required _i1.Credentials credentials,
+ _i1.Transaction? transaction,
+ }) async {
+ final function = self.abi.functions[19];
+ assert(checkSignature(function, 'cbcf9676'));
+ final params = [
+ args.owner,
+ args.amount,
+ args.frontendCode,
+ ];
+ return writeRaw(
+ credentials,
+ transaction,
+ function,
+ params,
+ );
+ }
+
+ /// The optional [atBlock] parameter can be used to view historical data. When
+ /// set, the function will be evaluated in the specified block. By default, the
+ /// latest on-chain block will be used.
+ Future<Savings> savings({
+ required _i1.EthereumAddress accountOwner,
+ _i1.BlockNum? atBlock,
+ }) async {
+ final function = self.abi.functions[21];
+ assert(checkSignature(function, '1f7cdd5f'));
+ final params = [accountOwner];
+ final response = await read(
+ function,
+ params,
+ atBlock,
+ );
+ return Savings(response);
+ }
+
+ /// The optional [transaction] parameter can be used to override parameters
+ /// like the gas price, nonce and max gas. The `data` and `to` fields will be
+ /// set by the contract.
+ Future<_i2.Uint8List> withdraw(
+ ({
+ _i1.EthereumAddress target,
+ BigInt amount,
+ _i2.Uint8List frontendCode
+ }) args, {
+ required _i1.Credentials credentials,
+ _i1.Transaction? transaction,
+ }) async {
+ final function = self.abi.functions[24];
+ assert(checkSignature(function, '829a0476'));
+ final params = [
+ args.target,
+ args.amount,
+ args.frontendCode,
+ ];
+ return writeRaw(
+ credentials,
+ transaction,
+ function,
+ params,
+ );
+ }
+
+ /// Returns a live stream of all InterestCollected events emitted by this contract.
+ Stream<InterestCollected> interestCollectedEvents({
+ _i1.BlockNum? fromBlock,
+ _i1.BlockNum? toBlock,
+ }) {
+ final event = self.event('InterestCollected');
+ final filter = _i1.FilterOptions.events(
+ contract: self,
+ event: event,
+ fromBlock: fromBlock,
+ toBlock: toBlock,
+ );
+ return client.events(filter).map((_i1.FilterEvent result) {
+ final decoded = event.decodeResults(
+ result.topics!,
+ result.data!,
+ );
+ return InterestCollected(
+ decoded,
+ result,
+ );
+ });
+ }
+
+ /// Returns a live stream of all RateChanged events emitted by this contract.
+ Stream<RateChanged> rateChangedEvents({
+ _i1.BlockNum? fromBlock,
+ _i1.BlockNum? toBlock,
+ }) {
+ final event = self.event('RateChanged');
+ final filter = _i1.FilterOptions.events(
+ contract: self,
+ event: event,
+ fromBlock: fromBlock,
+ toBlock: toBlock,
+ );
+ return client.events(filter).map((_i1.FilterEvent result) {
+ final decoded = event.decodeResults(
+ result.topics!,
+ result.data!,
+ );
+ return RateChanged(
+ decoded,
+ result,
+ );
+ });
+ }
+
+ /// Returns a live stream of all RateProposed events emitted by this contract.
+ Stream<RateProposed> rateProposedEvents({
+ _i1.BlockNum? fromBlock,
+ _i1.BlockNum? toBlock,
+ }) {
+ final event = self.event('RateProposed');
+ final filter = _i1.FilterOptions.events(
+ contract: self,
+ event: event,
+ fromBlock: fromBlock,
+ toBlock: toBlock,
+ );
+ return client.events(filter).map((_i1.FilterEvent result) {
+ final decoded = event.decodeResults(
+ result.topics!,
+ result.data!,
+ );
+ return RateProposed(
+ decoded,
+ result,
+ );
+ });
+ }
+
+ /// Returns a live stream of all Saved events emitted by this contract.
+ Stream<Saved> savedEvents({
+ _i1.BlockNum? fromBlock,
+ _i1.BlockNum? toBlock,
+ }) {
+ final event = self.event('Saved');
+ final filter = _i1.FilterOptions.events(
+ contract: self,
+ event: event,
+ fromBlock: fromBlock,
+ toBlock: toBlock,
+ );
+ return client.events(filter).map((_i1.FilterEvent result) {
+ final decoded = event.decodeResults(
+ result.topics!,
+ result.data!,
+ );
+ return Saved(
+ decoded,
+ result,
+ );
+ });
+ }
+
+ /// Returns a live stream of all Withdrawn events emitted by this contract.
+ Stream<Withdrawn> withdrawnEvents({
+ _i1.BlockNum? fromBlock,
+ _i1.BlockNum? toBlock,
+ }) {
+ final event = self.event('Withdrawn');
+ final filter = _i1.FilterOptions.events(
+ contract: self,
+ event: event,
+ fromBlock: fromBlock,
+ toBlock: toBlock,
+ );
+ return client.events(filter).map((_i1.FilterEvent result) {
+ final decoded = event.decodeResults(
+ result.topics!,
+ result.data!,
+ );
+ return Withdrawn(
+ decoded,
+ result,
+ );
+ });
+ }
+}
+
+class Savings {
+ Savings(List<dynamic> response)
+ : saved = (response[0] as BigInt),
+ ticks = (response[1] as BigInt);
+
+ final BigInt saved;
+
+ final BigInt ticks;
+}
+
+class InterestCollected {
+ InterestCollected(
+ List<dynamic> response,
+ this.event,
+ ) : account = (response[0] as _i1.EthereumAddress),
+ interest = (response[1] as BigInt);
+
+ final _i1.EthereumAddress account;
+
+ final BigInt interest;
+
+ final _i1.FilterEvent event;
+}
+
+class RateChanged {
+ RateChanged(
+ List<dynamic> response,
+ this.event,
+ ) : newRate = (response[0] as BigInt);
+
+ final BigInt newRate;
+
+ final _i1.FilterEvent event;
+}
+
+class RateProposed {
+ RateProposed(
+ List<dynamic> response,
+ this.event,
+ ) : who = (response[0] as _i1.EthereumAddress),
+ nextRate = (response[1] as BigInt),
+ nextChange = (response[2] as BigInt);
+
+ final _i1.EthereumAddress who;
+
+ final BigInt nextRate;
+
+ final BigInt nextChange;
+
+ final _i1.FilterEvent event;
+}
+
+class Saved {
+ Saved(
+ List<dynamic> response,
+ this.event,
+ ) : account = (response[0] as _i1.EthereumAddress),
+ amount = (response[1] as BigInt);
+
+ final _i1.EthereumAddress account;
+
+ final BigInt amount;
+
+ final _i1.FilterEvent event;
+}
+
+class Withdrawn {
+ Withdrawn(
+ List<dynamic> response,
+ this.event,
+ ) : account = (response[0] as _i1.EthereumAddress),
+ amount = (response[1] as BigInt);
+
+ final _i1.EthereumAddress account;
+
+ final BigInt amount;
+
+ final _i1.FilterEvent event;
+}
diff --git a/cw_evm/lib/evm_chain_client.dart b/cw_evm/lib/evm_chain_client.dart
deleted file mode 100644
index 574c2b4..0000000
--- a/cw_evm/lib/evm_chain_client.dart
+++ /dev/null
@@ -1,446 +0,0 @@
-import 'dart:async';
-import 'dart:convert';
-import 'dart:developer';
-
-import 'package:cw_core/crypto_currency.dart';
-import 'package:cw_core/erc20_token.dart';
-import 'package:cw_core/node.dart';
-import 'package:cw_core/utils/proxy_wrapper.dart';
-import 'package:cw_evm/evm_chain_transaction_model.dart';
-import 'package:cw_evm/evm_chain_transaction_priority.dart';
-import 'package:cw_evm/evm_erc20_balance.dart';
-import 'package:cw_evm/pending_evm_chain_transaction.dart';
-import 'package:cw_evm/.secrets.g.dart' as secrets;
-import 'package:flutter/foundation.dart';
-import 'package:hex/hex.dart' as hex;
-import 'package:web3dart/web3dart.dart';
-
-import 'contract/erc20.dart';
-
-abstract class EVMChainClient {
- late final client = ProxyWrapper().getHttpIOClient();
- Web3Client? _client;
-
- //! To be overridden by all child classes
-
- int get chainId;
-
- Future<List<EVMChainTransactionModel>> fetchTransactions(String address,
- {String? contractAddress});
-
- Future<List<EVMChainTransactionModel>> fetchInternalTransactions(String address);
-
- Uint8List prepareSignedTransactionForSending(Uint8List signedTransaction);
-
- //! Common methods across all child classes
-
- bool connect(Node node) {
- try {
- Uri? rpcUri;
- bool isModifiedNodeUri = false;
-
- if (node.uriRaw.contains('nownodes.io')) {
- isModifiedNodeUri = true;
- String nowNodeApiKey = secrets.nowNodesApiKey;
-
- rpcUri = Uri.https(node.uriRaw, '/$nowNodeApiKey');
- }
-
- _client = Web3Client(isModifiedNodeUri ? rpcUri!.toString() : node.uri.toString(), client);
-
- return true;
- } catch (e) {
- return false;
- }
- }
-
- void setListeners(EthereumAddress userAddress, Function() onNewTransaction) async {
- // _client?.pendingTransactions().listen((transactionHash) async {
- // final transaction = await _client!.getTransactionByHash(transactionHash);
- //
- // if (transaction.from.hex == userAddress || transaction.to?.hex == userAddress) {
- // onNewTransaction();
- // }
- // });
- }
-
- Future<EtherAmount> getBalance(EthereumAddress address, {bool throwOnError = false}) async {
- try {
- return await _client!.getBalance(address);
- } catch (_) {
- if (throwOnError) {
- rethrow;
- }
- return EtherAmount.zero();
- }
- }
-
- Future<int> getGasUnitPrice() async {
- try {
- final gasPrice = await _client!.getGasPrice();
-
- return gasPrice.getInWei.toInt();
- } catch (_) {
- return 0;
- }
- }
-
- Future<int?> getGasBaseFee() async {
- try {
- final blockInfo = await _client!.getBlockInformation(isContainFullObj: false);
- final baseFee = blockInfo.baseFeePerGas;
-
- return baseFee?.getInWei.toInt();
- } catch (_) {
- return 0;
- }
- }
-
- Future<int> getEstimatedGasUnitsForTransaction({
- required EthereumAddress toAddress,
- required EthereumAddress senderAddress,
- required EtherAmount value,
- String? contractAddress,
- EtherAmount? gasPrice,
- EtherAmount? maxFeePerGas,
- Uint8List? data,
- }) async {
- try {
- if (contractAddress == null) {
- final estimatedGas = await _client!.estimateGas(
- sender: senderAddress,
- to: toAddress,
- value: value,
- data: data,
- );
-
- return estimatedGas.toInt();
- } else {
- final contract = DeployedContract(
- ethereumContractAbi,
- EthereumAddress.fromHex(contractAddress),
- );
-
- final transfer = contract.function('transfer');
-
- // Estimate gas units
- final gasEstimate = await _client!.estimateGas(
- sender: senderAddress,
- to: EthereumAddress.fromHex(contractAddress),
- data: data ??
- transfer.encodeCall([
- toAddress,
- value.getInWei,
- ]),
- );
-
- return gasEstimate.toInt();
- }
- } catch (_) {
- return 0;
- }
- }
-
- Uint8List getEncodedDataForApprovalTransaction({
- required EthereumAddress toAddress,
- required EtherAmount value,
- required EthereumAddress contractAddress,
- }) {
- final contract = DeployedContract(ethereumContractAbi, contractAddress);
-
- final approve = contract.function('approve');
-
- return approve.encodeCall([
- toAddress,
- value.getInWei,
- ]);
- }
-
- Future<PendingEVMChainTransaction> signTransaction({
- required Credentials privateKey,
- required String toAddress,
- required BigInt amount,
- required BigInt gasFee,
- required int estimatedGasUnits,
- required int maxFeePerGas,
- required EVMChainTransactionPriority? priority,
- required CryptoCurrency currency,
- required String feeCurrency,
- required int exponent,
- String? contractAddress,
- String? data,
- int? gasPrice,
- }) async {
- assert(currency == CryptoCurrency.eth ||
- currency == CryptoCurrency.maticpoly ||
- currency == CryptoCurrency.baseEth ||
- currency == CryptoCurrency.arbEth ||
- contractAddress != null);
-
- 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: priority != null ? EtherAmount.fromInt(EtherUnit.gwei, priority.tip) : null,
- amount: isNativeToken ? EtherAmount.inWei(amount) : EtherAmount.zero(),
- data: data != null ? hexToBytes(data) : null,
- maxGas: estimatedGasUnits,
- maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
- gasPrice: gasPrice != null ? EtherAmount.fromInt(EtherUnit.wei, gasPrice) : null,
- );
-
- Uint8List signedTransaction;
-
- final Function _sendTransaction;
-
- if (isNativeToken) {
- signedTransaction = await _client!.signTransaction(privateKey, transaction, chainId: chainId);
- } else {
- final erc20 = ERC20(
- client: _client!,
- address: EthereumAddress.fromHex(contractAddress!),
- chainId: chainId,
- );
-
- signedTransaction = await erc20.transfer(
- EthereumAddress.fromHex(toAddress),
- amount,
- credentials: privateKey,
- transaction: transaction,
- );
- }
-
- _sendTransaction = () async => await sendTransaction(signedTransaction);
-
- return PendingEVMChainTransaction(
- signedTransaction: prepareSignedTransactionForSending(signedTransaction),
- amount: amount.toString(),
- fee: gasFee,
- feeCurrency: feeCurrency,
- sendTransaction: _sendTransaction,
- exponent: exponent,
- );
- }
-
- Future<PendingEVMChainTransaction> signApprovalTransaction({
- required Credentials privateKey,
- required String spender,
- required BigInt amount,
- required BigInt gasFee,
- required String feeCurrency,
- required int estimatedGasUnits,
- required int maxFeePerGas,
- required EVMChainTransactionPriority? priority,
- required int exponent,
- required String contractAddress,
- int? gasPrice,
- }) async {
- final Transaction transaction = createTransaction(
- from: privateKey.address,
- to: EthereumAddress.fromHex(contractAddress),
- maxPriorityFeePerGas:priority != null ? EtherAmount.fromInt(EtherUnit.gwei, priority.tip) : null,
- amount: EtherAmount.zero(),
- maxGas: estimatedGasUnits,
- maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
- gasPrice: gasPrice != null ? EtherAmount.fromInt(EtherUnit.wei, gasPrice) : null,
- );
-
- final erc20 = ERC20(
- client: _client!,
- address: EthereumAddress.fromHex(contractAddress),
- chainId: chainId,
- );
-
- final signedTransaction = await erc20.approve(
- EthereumAddress.fromHex(spender),
- amount,
- credentials: privateKey,
- transaction: transaction,
- );
-
- return PendingEVMChainTransaction(
- signedTransaction: prepareSignedTransactionForSending(signedTransaction),
- amount: amount.toString(),
- fee: gasFee,
- feeCurrency: feeCurrency,
- sendTransaction: () => sendTransaction(signedTransaction),
- exponent: exponent,
- isInfiniteApproval: amount.toRadixString(16) ==
- 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
- );
- }
-
- Transaction createTransaction({
- required EthereumAddress from,
- required EthereumAddress to,
- required EtherAmount amount,
- EtherAmount? maxPriorityFeePerGas,
- EtherAmount? gasPrice,
- EtherAmount? maxFeePerGas,
- Uint8List? data,
- int? maxGas,
- }) {
- return Transaction(
- from: from,
- to: to,
- maxPriorityFeePerGas: maxPriorityFeePerGas,
- value: amount,
- data: data,
- maxGas: maxGas,
- gasPrice: gasPrice,
- maxFeePerGas: maxFeePerGas,
- );
- }
-
- Future<String> sendTransaction(Uint8List signedTransaction) async {
- return await _client!.sendRawTransaction(prepareSignedTransactionForSending(signedTransaction));
- }
-
- Future getTransactionDetails(String transactionHash) async {
- // Wait for the transaction receipt to become available
- TransactionReceipt? receipt;
- while (receipt == null) {
- receipt = await _client!.getTransactionReceipt(transactionHash);
- await Future.delayed(const Duration(seconds: 1));
- }
-
- // Print the receipt information
- log('Transaction Hash: ${receipt.transactionHash}');
- log('Block Hash: ${receipt.blockHash}');
- log('Block Number: ${receipt.blockNumber}');
- log('Gas Used: ${receipt.gasUsed}');
-
- /*
- Transaction Hash: [112, 244, 4, 238, 89, 199, 171, 191, 210, 236, 110, 42, 185, 202, 220, 21, 27, 132, 123, 221, 137, 90, 77, 13, 23, 43, 12, 230, 93, 63, 221, 116]
- I/flutter ( 4474): Block Hash: [149, 44, 250, 119, 111, 104, 82, 98, 17, 89, 30, 190, 25, 44, 218, 118, 127, 189, 241, 35, 213, 106, 25, 95, 195, 37, 55, 131, 185, 180, 246, 200]
- I/flutter ( 4474): Block Number: 17120242
- I/flutter ( 4474): Gas Used: 21000
- */
-
- // Wait for the transaction receipt to become available
- TransactionInformation? transactionInformation;
- while (transactionInformation == null) {
- log("********************************");
- transactionInformation = await _client!.getTransactionByHash(transactionHash);
- await Future.delayed(const Duration(seconds: 1));
- }
- // Print the receipt information
- log('Transaction Hash: ${transactionInformation.hash}');
- log('Block Hash: ${transactionInformation.blockHash}');
- log('Block Number: ${transactionInformation.blockNumber}');
- log('Gas Used: ${transactionInformation.gas}');
-
- /*
- Transaction Hash: 0x70f404ee59c7abbfd2ec6e2ab9cadc151b847bdd895a4d0d172b0ce65d3fdd74
- I/flutter ( 4474): Block Hash: 0x952cfa776f68526211591ebe192cda767fbdf123d56a195fc3253783b9b4f6c8
- I/flutter ( 4474): Block Number: 17120242
- I/flutter ( 4474): Gas Used: 53000
- */
- }
-
- Future<EVMChainERC20Balance> fetchERC20Balances(
- EthereumAddress userAddress, String contractAddress) async {
- try {
- final erc20 = ERC20(address: EthereumAddress.fromHex(contractAddress), client: _client!);
- final balance = await erc20.balanceOf(userAddress);
-
- int exponent = (await erc20.decimals()).toInt();
-
- return EVMChainERC20Balance(balance, exponent: exponent);
- } on RangeError catch (_) {
- throw Exception('Invalid token contract for this network.');
- } catch (e) {
- throw Exception('Could not fetch balances: ${e.toString()}');
- }
- }
-
- Future<Erc20Token?> getErc20Token(String contractAddress, String chainName) async {
- try {
- final uri = Uri.https(
- 'deep-index.moralis.io',
- '/api/v2.2/erc20/metadata',
- {
- "chain": chainName,
- "addresses": contractAddress,
- },
- );
-
- final response = await client.get(
- uri,
- headers: {
- "Accept": "application/json",
- "X-API-Key": secrets.moralisApiKey,
- },
- );
-
- final decodedResponse = jsonDecode(response.body)[0] as Map<String, dynamic>;
-
- final symbol = (decodedResponse['symbol'] ?? '') as String;
- String filteredSymbol = symbol.replaceFirst(RegExp('^\\\$'), '');
-
- final name = decodedResponse['name'] ?? '';
- final decimal = decodedResponse['decimals'] ?? '0';
- final iconPath = decodedResponse['logo'] ?? '';
-
- return Erc20Token(
- name: name,
- symbol: filteredSymbol,
- contractAddress: contractAddress,
- decimal: int.tryParse(decimal) ?? 0,
- iconPath: iconPath,
- );
- } catch (e) {
- try {
- final erc20 = ERC20(address: EthereumAddress.fromHex(contractAddress), client: _client!);
- final name = await erc20.name();
- final symbol = await erc20.symbol();
- final decimal = await erc20.decimals();
-
- return Erc20Token(
- name: name,
- symbol: symbol,
- contractAddress: contractAddress,
- decimal: decimal.toInt(),
- );
- } catch (_) {}
-
- return null;
- }
- }
-
- Uint8List hexToBytes(String hexString) {
- return Uint8List.fromList(
- hex.HEX.decode(hexString.startsWith('0x') ? hexString.substring(2) : hexString));
- }
-
- void stop() {
- _client?.dispose();
- }
-
- Web3Client? getWeb3Client() {
- return _client;
- }
-
-// Future<int> _getDecimalPlacesForContract(DeployedContract contract) async {
-// final String abi = await rootBundle.loadString("assets/abi_json/erc20_abi.json");
-// final contractAbi = ContractAbi.fromJson(abi, "ERC20");
-//
-// final contract = DeployedContract(
-// contractAbi,
-// EthereumAddress.fromHex(_erc20Currencies[erc20Currency]!),
-// );
-// final decimalsFunction = contract.function('decimals');
-// final decimals = await _client!.call(
-// contract: contract,
-// function: decimalsFunction,
-// params: [],
-// );
-//
-// int exponent = int.parse(decimals.first.toString());
-// return exponent;
-// }
-}
diff --git a/cw_evm/lib/evm_chain_client_factory.dart b/cw_evm/lib/evm_chain_client_factory.dart
new file mode 100644
index 0000000..61c73fd
--- /dev/null
+++ b/cw_evm/lib/evm_chain_client_factory.dart
@@ -0,0 +1,36 @@
+import 'package:cw_evm/clients/arbitrum_client.dart';
+import 'package:cw_evm/clients/base_client.dart';
+import 'package:cw_evm/clients/ethereum_client.dart';
+import 'package:cw_evm/clients/polygon_client.dart';
+import 'package:cw_evm/clients/evm_chain_client.dart';
+import 'package:cw_evm/evm_chain_registry.dart';
+
+/// Factory to create appropriate EVMChainClient based on chainId
+class EVMChainClientFactory {
+ static final EvmChainRegistry _registry = EvmChainRegistry();
+
+ /// Create an EVMChainClient for the given chainId
+ ///
+ /// Throws an exception if chainId is not registered
+ static EVMChainClient createClient(int chainId) {
+ final config = _registry.getChainConfig(chainId);
+
+ if (config == null) {
+ throw Exception('Chain config not found for chainId: $chainId');
+ }
+
+ // Check if chain needs custom client
+ switch (chainId) {
+ case 1:
+ return EthereumClient();
+ case 137:
+ return PolygonClient();
+ case 8453:
+ return BaseClient();
+ case 42161:
+ return ArbitrumClient();
+ default:
+ return EVMChainClient(chainId: chainId);
+ }
+ }
+}
diff --git a/cw_evm/lib/evm_chain_default_tokens.dart b/cw_evm/lib/evm_chain_default_tokens.dart
new file mode 100644
index 0000000..bc1496f
--- /dev/null
+++ b/cw_evm/lib/evm_chain_default_tokens.dart
@@ -0,0 +1,22 @@
+import 'package:cw_core/erc20_token.dart';
+import 'package:cw_evm/tokens/arbitrum_tokens.dart';
+import 'package:cw_evm/tokens/base_tokens.dart';
+import 'package:cw_evm/tokens/ethereum_tokens.dart';
+import 'package:cw_evm/tokens/polygon_tokens.dart';
+
+/// Default ERC20 tokens for each EVM chain
+class EVMChainDefaultTokens {
+ static List<Erc20Token> getDefaultTokensByChainId(int chainId) {
+ return switch (chainId) {
+ 1 => EthereumTokens.tokens,
+ 137 => PolygonTokens.tokens,
+ 8453 => BaseTokens.tokens,
+ 42161 => ArbitrumTokens.tokens,
+ _ => [],
+ };
+ }
+
+ static List<String> getDefaultTokenAddresses(int chainId) {
+ return getDefaultTokensByChainId(chainId).map((token) => token.contractAddress).toList();
+ }
+}
diff --git a/cw_evm/lib/evm_chain_exceptions.dart b/cw_evm/lib/evm_chain_exceptions.dart
index 8d4e94d..ff3545a 100644
--- a/cw_evm/lib/evm_chain_exceptions.dart
+++ b/cw_evm/lib/evm_chain_exceptions.dart
@@ -16,7 +16,10 @@ class EVMChainTransactionCreationException implements Exception {
class EVMChainTransactionFeesException implements Exception {
final String exceptionMessage;
- EVMChainTransactionFeesException(String currency)
+ EVMChainTransactionFeesException(String message)
+ : exceptionMessage = message;
+
+ EVMChainTransactionFeesException.fromCurrency(String currency)
: exceptionMessage = 'Transaction failed due to insufficient $currency balance to cover the fees.';
@override
diff --git a/cw_evm/lib/evm_chain_formatter.dart b/cw_evm/lib/evm_chain_formatter.dart
deleted file mode 100644
index e537371..0000000
--- a/cw_evm/lib/evm_chain_formatter.dart
+++ /dev/null
@@ -1,43 +0,0 @@
-import 'dart:math';
-
-class EVMChainFormatter {
- static int _divider = 0;
-
- static int parseEVMChainAmount(String amount) {
- try {
- final decimalLength = _getDividerForInput(amount);
- _divider = decimalLength;
- return (double.parse(amount) * pow(10, decimalLength)).round();
- } catch (_) {
- return 0;
- }
- }
-
- static double parseEVMChainAmountToDouble(int amount) {
- try {
- return amount / pow(10, _divider);
- } catch (_) {
- return 0;
- }
- }
-
- static String truncateDecimals(String amount, int decimals) {
- final parts = amount.split(".");
-
- if (parts.length == 2) {
- parts[1] = parts[1].substring(0, parts[1].length > decimals ? decimals : parts[1].length);
- }
-
- return parts.join(".");
- }
-
- static int _getDividerForInput(String amount) {
- final result = amount.split('.');
- if (result.length > 1) {
- final decimalLength = result[1].length;
- return decimalLength;
- } else {
- return 0;
- }
- }
-}
diff --git a/cw_evm/lib/evm_chain_registry.dart b/cw_evm/lib/evm_chain_registry.dart
new file mode 100644
index 0000000..4ea89a7
--- /dev/null
+++ b/cw_evm/lib/evm_chain_registry.dart
@@ -0,0 +1,203 @@
+import 'package:cw_core/crypto_currency.dart';
+import 'package:cw_core/wallet_type.dart';
+import 'package:cw_evm/utils/network_chain_utils.dart';
+
+/// Centralized registry for all EVM chain configurations
+class EvmChainRegistry {
+ static final EvmChainRegistry _instance = EvmChainRegistry._internal();
+ factory EvmChainRegistry() => _instance;
+ EvmChainRegistry._internal() {
+ initialize();
+ }
+
+ final Map<int, ChainConfig> _chains = {};
+ final Map<WalletType, int> _walletTypeToChainId = {};
+ final Map<int, WalletType> _chainIdToWalletType = {};
+ final Map<String, int> _tagToChainId = {};
+ final Map<String, int> _caip2ToChainId = {};
+
+ bool _initialized = false;
+
+ /// Initialize registry with all supported EVM chains
+ void initialize() {
+ if (_initialized) return;
+ _initialized = true;
+
+ // Ethereum Mainnet
+ _registerChain(
+ const ChainConfig(
+ chainId: 1,
+ name: 'Ethereum',
+ shortCode: 'eth',
+ caip2: 'eip155:1',
+ nativeCurrency: CryptoCurrency.eth,
+ capabilities: ChainCapabilities(
+ supportsERC20: true,
+ supportsEIP1559: true,
+ supportsInternalTx: true,
+ supportsSubscriptions: false,
+ supportsENS: true,
+ ),
+ defaultRpcEndpoints: [
+ 'ethereum-rpc.publicnode.com',
+ 'eth.llamarpc.com',
+ 'rpc.flashbots.net',
+ 'eth-mainnet.public.blastapi.io',
+ 'eth.nownodes.io',
+ 'ethereum.publicnode.com',
+ ],
+ explorerUrls: [
+ 'https://etherscan.io',
+ ],
+ feeModel: FeeModel(
+ type: FeeType.eip1559,
+ defaultGasLimit: 21000,
+ ),
+ ),
+ WalletType.ethereum,
+ 'ETH',
+ );
+
+ // Polygon
+ _registerChain(
+ const ChainConfig(
+ chainId: 137,
+ name: 'Polygon',
+ shortCode: 'polygon',
+ caip2: 'eip155:137',
+ nativeCurrency: CryptoCurrency.maticpoly,
+ capabilities: ChainCapabilities(
+ supportsERC20: true,
+ supportsEIP1559: true,
+ supportsInternalTx: true,
+ supportsSubscriptions: false,
+ supportsENS: false,
+ ),
+ defaultRpcEndpoints: [
+ 'polygon-rpc.com',
+ 'polygon-bor-rpc.publicnode.com',
+ 'polygon.llamarpc.com',
+ 'matic.nownodes.io',
+ ],
+ explorerUrls: [
+ 'https://polygonscan.com',
+ ],
+ feeModel: FeeModel(
+ type: FeeType.eip1559,
+ defaultGasLimit: 21000,
+ ),
+ ),
+ WalletType.polygon,
+ 'POL',
+ );
+
+ // Base
+ _registerChain(
+ const ChainConfig(
+ chainId: 8453,
+ name: 'Base',
+ shortCode: 'base',
+ caip2: 'eip155:8453',
+ nativeCurrency: CryptoCurrency.baseEth,
+ capabilities: ChainCapabilities(
+ supportsERC20: true,
+ supportsEIP1559: true,
+ supportsInternalTx: true,
+ supportsSubscriptions: false,
+ supportsENS: false,
+ ),
+ defaultRpcEndpoints: [
+ 'base.nownodes.io',
+ 'base.llamarpc.com',
+ 'base-rpc.publicnode.com',
+ '1rpc.io/base',
+ ],
+ explorerUrls: [
+ 'https://basescan.org',
+ ],
+ feeModel: FeeModel(
+ type: FeeType.eip1559,
+ defaultGasLimit: 21000,
+ ),
+ ),
+ WalletType.base,
+ 'BASE',
+ );
+
+ // Arbitrum
+ _registerChain(
+ const ChainConfig(
+ chainId: 42161,
+ name: 'Arbitrum',
+ shortCode: 'arbitrum',
+ caip2: 'eip155:42161',
+ nativeCurrency: CryptoCurrency.arbEth,
+ capabilities: ChainCapabilities(
+ supportsERC20: true,
+ supportsEIP1559: true,
+ supportsInternalTx: true,
+ supportsSubscriptions: false,
+ supportsENS: false,
+ ),
+ defaultRpcEndpoints: [
+ 'arbitrum.nownodes.io',
+ 'arbitrum.drpc.org',
+ 'arbitrum-one-rpc.publicnode.com',
+ ],
+ explorerUrls: [
+ 'https://arbiscan.io',
+ ],
+ feeModel: FeeModel(
+ type: FeeType.eip1559,
+ defaultGasLimit: 21000,
+ ),
+ ),
+ WalletType.arbitrum,
+ 'ARB',
+ );
+ }
+
+ void _registerChain(
+ ChainConfig config,
+ WalletType walletType,
+ String tag,
+ ) {
+ _chains[config.chainId] = config;
+ _walletTypeToChainId[walletType] = config.chainId;
+ _chainIdToWalletType[config.chainId] = walletType;
+ _tagToChainId[tag.toUpperCase()] = config.chainId;
+ _caip2ToChainId[config.caip2] = config.chainId;
+ }
+
+ ChainConfig? getChainConfig(int chainId) => _chains[chainId];
+
+ ChainConfig? getChainConfigByWalletType(WalletType walletType) {
+ final chainId = _walletTypeToChainId[walletType];
+ return chainId != null ? _chains[chainId] : null;
+ }
+
+ /// Get chain configuration by tag (e.g., 'ETH', 'POL', 'BASE', 'ARB')
+ ChainConfig? getChainConfigByTag(String tag) {
+ final chainId = _tagToChainId[tag.toUpperCase()];
+ return chainId != null ? _chains[chainId] : null;
+ }
+
+ /// Get chain configuration by CAIP-2 identifier (e.g. 'eip155:1')
+ ChainConfig? getChainConfigByCaip2(String caip2) {
+ final chainId = _caip2ToChainId[caip2];
+ return chainId != null ? _chains[chainId] : null;
+ }
+
+ WalletType? getWalletTypeByChainId(int chainId) => _chainIdToWalletType[chainId];
+
+ int? getChainIdByWalletType(WalletType walletType) => _walletTypeToChainId[walletType];
+
+ bool isChainRegistered(int chainId) => _chains.containsKey(chainId);
+
+ List<int> getRegisteredChainIds() => _chains.keys.toList();
+
+ List<ChainConfig> getAllChains() => _chains.values.toList();
+
+ List<WalletType> getRegisteredWalletTypes() =>
+ _walletTypeToChainId.keys.toList();
+}
diff --git a/cw_evm/lib/evm_chain_transaction_credentials.dart b/cw_evm/lib/evm_chain_transaction_credentials.dart
index 5b5bdf1..327608a 100644
--- a/cw_evm/lib/evm_chain_transaction_credentials.dart
+++ b/cw_evm/lib/evm_chain_transaction_credentials.dart
@@ -8,10 +8,12 @@ class EVMChainTransactionCredentials {
required this.priority,
required this.currency,
this.feeRate,
+ this.useBlinkProtection = true,
});
final List<OutputInfo> outputs;
final EVMChainTransactionPriority? priority;
final int? feeRate;
final CryptoCurrency currency;
+ final bool useBlinkProtection;
}
diff --git a/cw_evm/lib/evm_chain_transaction_history.dart b/cw_evm/lib/evm_chain_transaction_history.dart
index 7c46ebe..8c8d72f 100644
--- a/cw_evm/lib/evm_chain_transaction_history.dart
+++ b/cw_evm/lib/evm_chain_transaction_history.dart
@@ -3,21 +3,25 @@ import 'dart:core';
import 'dart:developer';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/pathForWallet.dart';
+import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_evm/evm_chain_transaction_info.dart';
+import 'package:cw_evm/utils/evm_chain_utils.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_core/transaction_history.dart';
part 'evm_chain_transaction_history.g.dart';
-abstract class EVMChainTransactionHistory = EVMChainTransactionHistoryBase
- with _$EVMChainTransactionHistory;
+class EVMChainTransactionHistory = EVMChainTransactionHistoryBase with _$EVMChainTransactionHistory;
abstract class EVMChainTransactionHistoryBase
extends TransactionHistoryBase<EVMChainTransactionInfo> with Store {
- EVMChainTransactionHistoryBase(
- {required this.walletInfo, required String password, required this.encryptionFileUtils})
- : _password = password {
+ EVMChainTransactionHistoryBase({
+ required this.walletInfo,
+ required String password,
+ required this.encryptionFileUtils,
+ required this.getCurrentChainId,
+ }) : _password = password {
transactions = ObservableMap<String, EVMChainTransactionInfo>();
}
@@ -26,13 +30,17 @@ abstract class EVMChainTransactionHistoryBase
final WalletInfo walletInfo;
final EncryptionFileUtils encryptionFileUtils;
- //! Method to be overridden by all child classes
+ /// Function to get the current chain ID (allows transaction history to use correct file)
+ final int Function() getCurrentChainId;
- String getTransactionHistoryFileName();
-
- EVMChainTransactionInfo getTransactionInfo(Map<String, dynamic> val);
+ /// Get transaction history file name based on current chain ID
+ String getTransactionHistoryFileName() {
+ return EVMChainUtils.getTransactionHistoryFileName(getCurrentChainId());
+ }
- //! Common methods across all child classes
+ EVMChainTransactionInfo getTransactionInfo(Map<String, dynamic> val) {
+ return EVMChainTransactionInfo.fromJson(val, getCurrentChainId());
+ }
Future<void> init() async {
clear();
@@ -45,7 +53,19 @@ abstract class EVMChainTransactionHistoryBase
try {
final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
String path = '$dirPath/$transactionsHistoryFileNameForWallet';
- final data = json.encode({'transactions': transactions});
+
+ // Filter transactions by current chainId before saving
+ // This ensures we only save transactions for the current chain, preventing
+ // transactions from other chains from being saved to the wrong file
+ final currentChainId = getCurrentChainId();
+ final filteredTransactions = <String, EVMChainTransactionInfo>{};
+ for (final entry in transactions.entries) {
+ if (entry.value.chainId == currentChainId) {
+ filteredTransactions[entry.key] = entry.value;
+ }
+ }
+
+ final data = json.encode({'transactions': filteredTransactions});
await encryptionFileUtils.write(path: path, password: _password, data: data);
} catch (e, s) {
log('Error while saving ${walletInfo.type.name} transaction history: ${e.toString()}');
@@ -54,11 +74,27 @@ abstract class EVMChainTransactionHistoryBase
}
@override
- void addOne(EVMChainTransactionInfo transaction) => transactions[transaction.id] = transaction;
+ void addOne(EVMChainTransactionInfo transaction) {
+ if (transaction.chainId == getCurrentChainId()) {
+ transactions[transaction.id] = transaction;
+ }
+ }
@override
- void addMany(Map<String, EVMChainTransactionInfo> transactions) =>
- this.transactions.addAll(transactions);
+ void addMany(Map<String, EVMChainTransactionInfo> transactionsToAdd) {
+ final currentChainId = getCurrentChainId();
+
+ // First, remove any transactions that don't match the current chainId
+ // This prevents transactions from other chains from persisting in the map
+ transactions.removeWhere((key, value) => value.chainId != currentChainId);
+
+ // Then add/update transactions for the current chain
+ for (final entry in transactionsToAdd.entries) {
+ if (entry.value.chainId == currentChainId) {
+ transactions[entry.key] = entry.value;
+ }
+ }
+ }
Future<Map<String, dynamic>> _read() async {
final transactionsHistoryFileNameForWallet = getTransactionHistoryFileName();
diff --git a/cw_evm/lib/evm_chain_transaction_info.dart b/cw_evm/lib/evm_chain_transaction_info.dart
index 781dd57..9f4bc63 100644
--- a/cw_evm/lib/evm_chain_transaction_info.dart
+++ b/cw_evm/lib/evm_chain_transaction_info.dart
@@ -3,10 +3,12 @@
import 'dart:math';
import 'package:cw_core/format_amount.dart';
+import 'package:cw_core/format_fixed.dart';
import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/transaction_info.dart';
+import 'package:cw_evm/utils/evm_chain_utils.dart';
-abstract class EVMChainTransactionInfo extends TransactionInfo {
+class EVMChainTransactionInfo extends TransactionInfo {
EVMChainTransactionInfo({
required this.id,
required this.height,
@@ -22,6 +24,7 @@ abstract class EVMChainTransactionInfo extends TransactionInfo {
required this.from,
this.evmSignatureName,
this.contractAddress,
+ required this.chainId,
}) : amount = ethAmount.toInt(),
fee = ethFee.toInt();
@@ -42,9 +45,10 @@ abstract class EVMChainTransactionInfo extends TransactionInfo {
final String? from;
final String? evmSignatureName;
final String? contractAddress;
+ final int chainId;
- //! Getter to be overridden in child classes
- String get feeCurrency;
+ /// Get fee currency symbol based on wallet type
+ String get feeCurrency => EVMChainUtils.getFeeCurrency(chainId);
@override
String amountFormatted() {
@@ -60,8 +64,28 @@ abstract class EVMChainTransactionInfo extends TransactionInfo {
@override
String feeFormatted() {
- final amount = (ethFee / BigInt.from(10).pow(18)).toString();
- return '${amount.substring(0, min(18, amount.length))} $feeCurrency';
+ final amount = formatFixed(ethFee, 18);
+ return '$amount $feeCurrency';
+ }
+
+ factory EVMChainTransactionInfo.fromJson(Map<String, dynamic> data, int chainId) {
+ return EVMChainTransactionInfo(
+ id: data['id'] as String,
+ height: data['height'] as int,
+ ethAmount: BigInt.parse(data['amount'] as String),
+ exponent: data['exponent'] as int? ?? 18,
+ ethFee: BigInt.parse(data['fee'] as String),
+ direction: TransactionDirection.values[data['direction'] as int],
+ date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
+ isPending: data['isPending'] as bool? ?? false,
+ confirmations: data['confirmations'] as int,
+ tokenSymbol: data['tokenSymbol'] as String,
+ to: data['to'] as String?,
+ from: data['from'] as String?,
+ evmSignatureName: data['evmSignatureName'] as String?,
+ contractAddress: data['contractAddress'] as String?,
+ chainId: chainId,
+ );
Why this scored 44/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.