Compare commits

...

29 commits
v5.0.1 ... main

Author SHA1 Message Date
Omar Hatem
c2cca1ff37
v5.1.0 Release Candidate (#2332)
* v5.1.0 Release Candidate

* update app versions [skip ci]

* separate secrets for Monero.com [skip ci]

* fix: amount getting wiped when pasting address

* update Trocador Monero api key [skip ci]

* move fiat api key to headers [skip ci]

* latest Release candidate
2025-06-27 22:20:18 +03:00
David Adegoke
5aeb6b7522
Deuro Savings Error Handling (#2340)
* feat(deuro): Enhance gas fee handling and error management for Deuro Savings Transactions.

This change:
- Introduces DeuroGasFeeException to handle insufficient ETH for gas fees.
- Adds check for ETH balance before savings transactions to prevent failures due to insufficient funds.
- Updates savings transaction methods to include error handling.
- Adds UI feedback for transaction failures in DEuroSavingsPage.

* Fix conflicts

* Update cw_ethereum/lib/deuro/deuro_savings.dart

Co-authored-by: Konstantin Ullrich <konstantinullrich12@gmail.com>

---------

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
Co-authored-by: Konstantin Ullrich <konstantinullrich12@gmail.com>
2025-06-27 17:53:46 +03:00
Konstantin Ullrich
b3c20a5818
Generic fixes for payjoin (#2342)
* feat: enhance Payjoin transaction details with block explorer link

* feat: enhance Payjoin transaction details with block explorer link

* fix: handle connectivity errors in Payjoin session operations
2025-06-27 16:38:09 +03:00
Serhii
d0827dd39e
add USDT Binance Smart Chain (BSC) support (#2341) 2025-06-27 15:47:34 +03:00
cyan
900304d405
fix(cw_monero): keys nullcheck handling (#2338)
add default values to monero wallet keys functions in order to prevent null check crashes
2025-06-27 15:06:56 +03:00
cyan
73588071ba
feat(tor): add Tor endpoints for fiat-api and service-api (#2339) 2025-06-27 15:05:54 +03:00
Serhii
1a5601f755
bugfix-unspent-coins-back-button (#2335)
* Update unspent_coins_list_page.dart

* minor fix

* close the page after the update is done
2025-06-26 19:02:05 +03:00
Konstantin Ullrich
04c86c567f
fix: use cake wallet frontend code, remove unused imports (#2337) 2025-06-26 16:43:16 +03:00
David Adegoke
65bb917bfb
CW-1094-WalletConnect-Issues (#2318)
* feat(walletconnect): Minor update to WalletConnect tile UI to fix expanded image issue

* feat(walletconnect): Minor update to WalletConnect tile UI to fix expanded image issue

* feat(walletconnect): Enhance WalletConnect EVM chain service.

This change:
- Improves signTypedDataV4 method handing and data parsing in extractPermitData.
- Adjusts UI for One Click Auth requests

* feat(walletconnect): Add redirect to PairingMetadata in WalletKit setup

* fix(walletconnect): Ensure session null checks before handling redirects in EvmChainService

* fix(walletconnect): Add null safety checks for permitData properties in EvmChainService

* refactor(walletconnect): Update WCPairingItemWidget layout and improve error handling for image loading

* fix(walletconnect): Handle break in connection flow triggered by global exception handler when SVGParser fails on invalid SvgData and triggers FlutterError.

* refactor(solana): Remove redundant session request responses and simplify error handling in SolanaChainService

---------

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
2025-06-24 05:48:27 +03:00
David Adegoke
af89603b81
CW-1103:Token Validation Issues (#2327)
* feat(token_validation): Improve flow for adding new tokens across wallets

This change:
- Implements check to see if a token is already added, preventing duplicates
- Triggers dialog warning if its a duplicate token
- Takes EVM Chains contract adddress case-insensitivity when making checks for potential scams.

* refactor(token_validation): Modify token management flow

This change:
- Removes duplicate token check during token addition in EVMChainWalletBase.
- Introduces a flag to indicate if a token is being edited
- Adjusts token addition validation to bypass checks when editing an existing token.

* Update lib/src/screens/dashboard/edit_token_page.dart

---------

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
2025-06-24 05:47:21 +03:00
David Adegoke
4434ad7363
CW-1086: Polygon Issues (#2329)
* fix(polygon): Polygon wallets not showing in address book when sending tokens

* feat(polygon_issues): Enhance gas price handling in EVM Chains

This change:
- Updates gas price calculation to account for minimum priority fee in Polygon
- Adjusts gas price handling to use maxFeePerGas when gasPrice is not provided.
- Fixes issue with send all for Polygon
2025-06-24 05:41:43 +03:00
cyan
5082dc20f3
CW-519 Enable built-in Tor (#1950)
* tor wip

* Enable tor on iOS

* Prevent app lag when node is exceptionally slow (usually over tor)

* fix: logic in daemonBlockchainHeight refresh
fix: storing tor state

* Pin ledger_flutter_plus dependency to fix builds

* bump arti version

* wip

* add single httpclient

* route everything I was able to catch trough the built-in tor node

* Enable proxy for http.Client [run tests]

* add tor proxy support to cw_evm, cw_tron and cw_polygon [run tests]

* remove log pollution, cleanup [skip slack]

* fix tests not working in latest main [skip slack] [run tests]

* remove cw_wownero import

* fix build issues

* migrate all remaining calls to use ProxyWrapper
add a CI action to enforce using ProxyWrapper instead of http/http.dart to prevent leaks

* fix tor background sync (will work on test builds after #2142 is merged and this PR is rebased on top)

* wip [skip ci]

* relicense to GPLv3 add socks5 license, build fixes

* use ProxyWrapper instead of http in robinhood

* Revert "relicense to GPLv3"

* feat(cw_bitcoin): support socks proxy and CakeTor

* fix(tor): migrate OCP and EVM over to ProxyWrapper()

* chore: cleanup
fix: show tor loading screen when app is starting

* fix: tor switch properly dismisses fullscreen loading dialog
fix: connectToNode after tor startup on app start

* fix(tor): status check for xmr/wow/zano

* fix(tor): onramper request fix

* fix(api): ServicesResponse is now being cached and doesn't fetch data everytime DashboardViewModel is being rebuilt
fix(tor): do not fallback to clearnet when tor failed.
fix(tor): do not leak connections during app startup
chore: refactor bootstrap() function to be separated into bootstrapOffline and bootstrapOnline
fix(cw_bitcoin): migrate payjoin to use ProxyWrapper

* [skip ci] remove print

* address comments from review

* fix: derusting tor implementation

Instead of rust-based Arti I've moved back to the
OG C++ tor implementation.
This fixed all issues we had with Tor.
- onion services now work
- all requests are going through without random errors
- we don't have to navigate a maze of multiple forks of multiple packages
- fully working `torrc` config file (probably will be needed for Tari).
- logging for Tor client
- and so on.

feat: network logging tab
feat: use built-in proxy on Tails - this should resolve all issues for Tails users (needs testing though)

* fix conflicts with main
bump https to fix build issue
relax store() call

* fix(cw_wownero): tor connection
fix(tor): connection issues

* fix(cw_evm): add missing chainId
fix(cw_core): solana rpc fix

* feat: mark tor as experimental
fix: drop anonpay onion authority
fix: drop fiatapi onion authority
fix: drop trocador onion authority
fix: disable networkimage when tor is enabled
fix: handle cakepay errors gracefully

* fix re-formatting [skip ci]

* changes from review

* Delete android/.kotlin/sessions/kotlin-compiler-2468481326039681181.salive

* fix missing imports

* Update pubspec_base.yaml

---------

Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>
2025-06-20 22:56:18 +03:00
Konstantin Ullrich
18c2ba9366
CW-1090-ledger-issues (#2314)
* build: bump ledger_flutter_plus dependencies

* fix: handle connection errors occurring during connecting to a ledger device

* fix: enhance ledger error handling and improve wallet connection reliability

* fix: enhance ledger error handling and improve wallet connection reliability

* feat: add localized strings for "Try again" and update Ledger error handling on auth

* fix: handle rethrow behavior in onAuthenticationStateChange

* fix: improve Ledger error handling and refine error code interpretation

* fix: refine Ledger error code interpretation and enhance error handling [skip-ci]

* add missing ledger error codes

---------

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
2025-06-19 20:00:16 +03:00
Konstantin Ullrich
4b137bc968
CW-1091-payjoin-error-handeling (#2317)
* feat: stop polling payjoin on switch wallet

* refactor: improve Payjoin session handling and cleanup unused methods

- Replaced `initReceiver` with `getUnusedReceiver` to reuse existing Payjoin sessions.
- Streamlined session initialization by removing `spawnNewReceiver`.
- Adjusted wallet sync reactions to resume Payjoin sessions when necessary.

* fix: Receiver.fromJson correctly handle parameter format in Payjoin manager

* fix: try reloading unspents if unspents are empty; No Unpsents available are now recoverable errors

* fix: ensure transaction details display only if transactionInfo is available and adjust payjoin success status handling

* fix: adjust payjoin success status handling for pending transactions

* fix: add error handling for Payjoin initialization and receiver creation [skip-ci]

* fix: add unrecoverable error handling for Payjoin sender sessions
2025-06-19 19:55:41 +03:00
OmarHatem
21d5c51cc9 remove unused file [skip ci] 2025-06-19 05:38:58 +03:00
Konstantin Ullrich
edaf485993
CW-1069-implement-deuro-dapp-support (#2304)
* feat: started dEuro Savings integration

* fix: merge conflict regarding theming

* feat: Add dEuro Savings Screen

* feat: Change DEuro Savings UI

* feat: Complete DEuro Savings integration with UI enhancements and transaction support

* style: remove forgotten print statements

* feat: localize dEuro subtitle

* feat: add approval flow and priority handling to DEuro Savings integration

- Introduced approval flow for DEuro Savings to enable token approvals.
- Added priority handling for deposit and withdrawal operations.
- Updated UI to support approval state and interactions.
- Localized new strings for multiple languages.
- Enhanced transaction handling with separate approval and commit actions.

* feat: add support for ERC20 token approval transactions

- Introduced `signApprovalTransaction` and `createApprovalTransaction` methods.
- Added handling for infinite approvals.
- Implemented encoding for approval transaction data.
- Enhanced transaction creation flow with approval-specific functionality.

* Update UI

* feat: enhance DEuro Savings logic and UI with computed property and fix gradient background

* feat: localize transaction confirmation content for DEuro Savings

* feat: enable interest collection for DEuro Savings with localized support

* fix reformatting [skip ci]

---------

Co-authored-by: tuxsudo <tuxsudo@tux.pizza>
Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>
2025-06-19 05:37:41 +03:00
Konstantin Ullrich
150becb679
CW-1073 Implement Monero wallet definition URI scheme (#2323)
* feat: add optional parameter to customize address extraction pattern

* refactor: add parameter to control address extraction surrounding whitespace validation

* fix: ensure proper handling of unmounted context in address extraction logic

* test: add comprehensive unit tests for AddressResolver and AddressValidator classes
2025-06-18 17:20:03 +03:00
David Adegoke
c6cb48096d
refactor: Update CakeImageWidget to allow nullable BoxFit (#2326) 2025-06-17 23:24:18 +03:00
JoeGruffins
65402ba1eb
dcr: Always fetch the current dir path. (#2242)
* dcr: Always fetch the current dir path.

On ios devices the path will change between updates breaking decred.
Never save the path and always check to ensure it is up to date.
Previous wallets were also not creating a directory in the correct
place. Move those when found.

* Update cw_decred/lib/wallet_service.dart

* dcr: Update libwallet dep.

---------

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
2025-06-17 01:37:49 +03:00
cyan
17d99e5451
feat(ui): add app logo to qr code (#2072)
fix(ui): use high instead of low error correction for QR codes
2025-06-17 01:32:11 +03:00
Konstantin Ullrich
85d3e727e2
CW-1092-restoring-from-backup-doesnt-maintain-hardware-wallets (#2319)
* feat: add hardware wallet verification during backup restoration

* style: improve readability of verifyHardwareWallets in backup_service_v3.dart
2025-06-17 01:31:49 +03:00
Serhii
4fb2fc47ad
fix: extra ID for Trocador swap (#2307) 2025-06-16 17:52:15 +03:00
cyan
a96b493b60
CW 1080: fix(cw_monero): call store() directly after commiting tx (#2312)
* fix(cw_monero): call store() directly after commiting tx to make sure that tx key is written to cache
also, store it in TransactionDescription hive box

* Update lib/view_model/send/send_view_model.dart

---------

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
2025-06-16 17:49:43 +03:00
David Adegoke
fe0c9ecc0e
CW-1084: Solana Issues (#2305)
* fix(solana-issues): Fix missing solana transaction history entries

* fix(solana-issues): Fixes issues relating to Solana Transaction History

This change:
- Modifies transaction parsing logic to handle more scenarios and better parse Solana transaction data.
- Adds partial filter for spam transactions

* fix(solana-issues): Enhance transaction parsing for Associated Token Account (ATA) programs

This change:
- Adds logic to differentiate between create account and token transfer transactions for the ATA program.
- Introduces a check to skip transactions that only create accounts without associated token transfers.

* fix(solana-issues): Improve transaction update logic and enhance error handling

This change:
- Updates the transaction update callback to only trigger when new valid transactions are present.
- Enhances error handling for insufficient funds by distinguishing between errors for sender and receiver.
2025-06-14 04:18:46 +03:00
Luis Miguel
e5d0194f11
Improving spanish translation (#2313)
Thanks for contibuting
2025-06-12 00:48:10 +03:00
Konstantin Ullrich
8457a45c2a
update-payjoin (#2281)
* feat: upgrade to flutter_payjoin 0.23.0

* fix: dependency discrepancy freezed_annotation

* feat(cw_bitcoin): use latest payjoin deps

* ci: update rust toolchain

* Update pubspec.yaml

* trial: downgrade flutter payjoin ffi

* some checks

* trial 2

* [skip ci] use correct image for CI

* fix: bump flutter sdk to 3.29.0

* partial revert

---------

Co-authored-by: Czarek Nakamoto <cyjan@mrcyjanek.net>
Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
2025-06-11 19:44:06 +03:00
David Adegoke
b0edf1fe75
fix(desktop-icon): Desktop Icons Fix (#2303)
This change:
- Adds svg icons for history and notification
- Updates the sidebar and services widget to use new images
- Fixes services widget icon looking very tiny on desktop
2025-06-04 20:15:36 +03:00
David Adegoke
1d6e594e04
CW-959: Swap Status on Transaction Screen (#2299)
* feat(swap-status-monitor): add real-time swap status monitoring and UI updates

- Introduce SwapManager for automatic tracking of active-wallet swaps.
- Automatically queues new or updated trades from the Hive box.
- Periodically fetch and persist swap statuses via the corresponding trade provider.
- Implement start(wallet, providers), stop(), and dispose() for lifecycle control.
- Apply user's ExchangeApiMode(disabled, tor-only, enabled) when fetching updates.
- Remove swaps from the watchlist on any final state (completed, expired, failed).
- Dispose SwapManager in AppState.dispose() to cancel polling and the Hive subscription.

* refactor(swap-status): replace SwapManager with TradeMonitor for improved trade monitoring.

This change improves the flow by simplifying the trade monitoring logic.

- Removes SwapManager class and replace with TradeMonitor implementation
- Update di and Appstate to register and dispose TradeMonitor
- Modify DashboardViewModel to use TradeMonitor instead of SwapManager

* fix: Modify trade monitoring logic to ensure trade timers are properly disposed when wallet switching occurs

* fix(swap-status): Fix receive amount for exchanges showing as .00 because of null values

* feat(swap-status): Enhance Trade Monitoring

This change:
- Adds a privacy settings option to disable automatic exchange status updates.
- Prevents trade monitoring when privacy settings option is enabled.
- Disables trade monitoring when the app is in background, we only want to run these checks in foreground.
- Refactors the trade monitoring logic to remove unneccessary checks and use of resources.

* feat(swap-status): Enhance Trade Monitoring

This change:
- Adds a privacy settings option to disable automatic exchange status updates.
- Prevents trade monitoring when privacy settings option is enabled.
- Disables trade monitoring when the app is in background, we only want to run these checks in foreground.
- Refactors the trade monitoring logic to remove unneccessary checks and use of resources.

* fix(swap-staus): Prevent unneccessary calls

* feat(swap-status): Prevent api request calls as long as last update time is less than specified interval
2025-06-04 18:24:56 +03:00
OmarHatem
1134915920 fix cakepay terms and conditions link [skip ci] 2025-06-03 03:22:04 +03:00
254 changed files with 7426 additions and 1901 deletions

View file

@ -55,7 +55,7 @@ jobs:
- name: Flutter action
uses: subosito/flutter-action@v1
with:
flutter-version: "3.27.4"
flutter-version: "3.27.0"
channel: stable
- name: Install package dependencies
@ -153,8 +153,8 @@ jobs:
echo "const shortKey = '${{ secrets.SHORT_KEY }}';" >> lib/.secrets.g.dart
echo "const backupSalt = '${{ secrets.BACKUP_SALT }}';" >> lib/.secrets.g.dart
echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart
echo "const changeNowApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
echo "const changeNowApiKeyDesktop = '${{ secrets.CHANGE_NOW_API_KEY_DESKTOP }}';" >> lib/.secrets.g.dart
echo "const changeNowCakeWalletApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
echo "const changeNowMoneroApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
echo "const wyreSecretKey = '${{ secrets.WYRE_SECRET_KEY }}';" >> lib/.secrets.g.dart
echo "const wyreApiKey = '${{ secrets.WYRE_API_KEY }}';" >> lib/.secrets.g.dart
echo "const wyreAccountId = '${{ secrets.WYRE_ACCOUNT_ID }}';" >> lib/.secrets.g.dart
@ -168,6 +168,7 @@ jobs:
echo "const ioniaClientId = '${{ secrets.IONIA_CLIENT_ID }}';" >> lib/.secrets.g.dart
echo "const twitterBearerToken = '${{ secrets.TWITTER_BEARER_TOKEN }}';" >> lib/.secrets.g.dart
echo "const trocadorApiKey = '${{ secrets.TROCADOR_API_KEY }}';" >> lib/.secrets.g.dart
echo "const trocadorMoneroApiKey = '${{ secrets.TROCADOR_API_KEY }}';" >> lib/.secrets.g.dart
echo "const trocadorExchangeMarkup = '${{ secrets.TROCADOR_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart
echo "const anonPayReferralCode = '${{ secrets.ANON_PAY_REFERRAL_CODE }}';" >> lib/.secrets.g.dart
echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart
@ -178,7 +179,8 @@ 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 chatwootWebsiteToken = '${{ secrets.CHATWOOT_WEBSITE_TOKEN }}';" >> lib/.secrets.g.dart
echo "const exolixApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
echo "const exolixCakeWalletApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
echo "const exolixMoneroApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
echo "const robinhoodApplicationId = '${{ secrets.ROBINHOOD_APPLICATION_ID }}';" >> lib/.secrets.g.dart
echo "const exchangeHelperApiKey = '${{ secrets.ROBINHOOD_CID_CLIENT_SECRET }}';" >> lib/.secrets.g.dart
echo "const walletConnectProjectId = '${{ secrets.WALLET_CONNECT_PROJECT_ID }}';" >> lib/.secrets.g.dart

21
.github/workflows/no_http_imports.yaml vendored Normal file
View file

@ -0,0 +1,21 @@
name: No http imports
on: [pull_request]
jobs:
PR_test_build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Check for http package usage
if: github.event_name == 'pull_request'
run: |
GIT_GREP_OUT="$(git grep package:http | (grep .dart: || test $? = 1) | (grep -v proxy_wrapper.dart || test $? = 1) | (grep -v very_insecure_http_do_not_use || test $? = 1) || true)"
[[ "x$GIT_GREP_OUT" == "x" ]] && exit 0
echo "$GIT_GREP_OUT"
echo "There are .dart files which use http imports"
echo "Using http package breaks proxy integration"
echo "Please use ProxyWrapper.getHttpClient() from package:cw_core/utils/proxy_wrapper.dart"
exit 1

View file

@ -9,7 +9,7 @@ jobs:
PR_test_build:
runs-on: linux-amd64
container:
image: ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.4-go1.24.1
image: ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.0-go1.24.1-ruststablenightly
env:
STORE_PASS: test@cake_wallet
KEY_PASS: test@cake_wallet
@ -98,8 +98,8 @@ jobs:
else
echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart
fi
echo "const changeNowApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
echo "const changeNowApiKeyDesktop = '${{ secrets.CHANGE_NOW_API_KEY_DESKTOP }}';" >> lib/.secrets.g.dart
echo "const changeNowCakeWalletApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
echo "const changeNowMoneroApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
echo "const wyreSecretKey = '${{ secrets.WYRE_SECRET_KEY }}';" >> lib/.secrets.g.dart
echo "const wyreApiKey = '${{ secrets.WYRE_API_KEY }}';" >> lib/.secrets.g.dart
echo "const wyreAccountId = '${{ secrets.WYRE_ACCOUNT_ID }}';" >> lib/.secrets.g.dart
@ -113,6 +113,7 @@ jobs:
echo "const ioniaClientId = '${{ secrets.IONIA_CLIENT_ID }}';" >> lib/.secrets.g.dart
echo "const twitterBearerToken = '${{ secrets.TWITTER_BEARER_TOKEN }}';" >> lib/.secrets.g.dart
echo "const trocadorApiKey = '${{ secrets.TROCADOR_API_KEY }}';" >> lib/.secrets.g.dart
echo "const trocadorMoneroApiKey = '${{ secrets.TROCADOR_API_KEY }}';" >> lib/.secrets.g.dart
echo "const trocadorExchangeMarkup = '${{ secrets.TROCADOR_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart
echo "const anonPayReferralCode = '${{ secrets.ANON_PAY_REFERRAL_CODE }}';" >> lib/.secrets.g.dart
echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart
@ -124,7 +125,8 @@ jobs:
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 chatwootWebsiteToken = '${{ secrets.CHATWOOT_WEBSITE_TOKEN }}';" >> lib/.secrets.g.dart
echo "const exolixApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
echo "const exolixCakeWalletApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
echo "const exolixMoneroApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
echo "const robinhoodApplicationId = '${{ secrets.ROBINHOOD_APPLICATION_ID }}';" >> lib/.secrets.g.dart
echo "const exchangeHelperApiKey = '${{ secrets.ROBINHOOD_CID_CLIENT_SECRET }}';" >> lib/.secrets.g.dart
echo "const walletConnectProjectId = '${{ secrets.WALLET_CONNECT_PROJECT_ID }}';" >> lib/.secrets.g.dart
@ -253,6 +255,11 @@ jobs:
- name: Build generated code
run: |
flutter --version
flutter clean
rm -rf .dart_tool
rm pubspec.lock
flutter pub get
./model_generator.sh async
- name: Generate key properties

View file

@ -9,7 +9,7 @@ jobs:
PR_test_build:
runs-on: linux-amd64
container:
image: ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.4-go1.24.1
image: ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.0-go1.24.1-ruststablenightly
env:
STORE_PASS: test@cake_wallet
KEY_PASS: test@cake_wallet
@ -91,8 +91,8 @@ jobs:
else
echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart
fi
echo "const changeNowApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
echo "const changeNowApiKeyDesktop = '${{ secrets.CHANGE_NOW_API_KEY_DESKTOP }}';" >> lib/.secrets.g.dart
echo "const changeNowCakeWalletApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
echo "const changeNowMoneroApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
echo "const wyreSecretKey = '${{ secrets.WYRE_SECRET_KEY }}';" >> lib/.secrets.g.dart
echo "const wyreApiKey = '${{ secrets.WYRE_API_KEY }}';" >> lib/.secrets.g.dart
echo "const wyreAccountId = '${{ secrets.WYRE_ACCOUNT_ID }}';" >> lib/.secrets.g.dart
@ -106,6 +106,7 @@ jobs:
echo "const ioniaClientId = '${{ secrets.IONIA_CLIENT_ID }}';" >> lib/.secrets.g.dart
echo "const twitterBearerToken = '${{ secrets.TWITTER_BEARER_TOKEN }}';" >> lib/.secrets.g.dart
echo "const trocadorApiKey = '${{ secrets.TROCADOR_API_KEY }}';" >> lib/.secrets.g.dart
echo "const trocadorMoneroApiKey = '${{ secrets.TROCADOR_API_KEY }}';" >> lib/.secrets.g.dart
echo "const trocadorExchangeMarkup = '${{ secrets.TROCADOR_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart
echo "const anonPayReferralCode = '${{ secrets.ANON_PAY_REFERRAL_CODE }}';" >> lib/.secrets.g.dart
echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart
@ -117,7 +118,8 @@ jobs:
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 chatwootWebsiteToken = '${{ secrets.CHATWOOT_WEBSITE_TOKEN }}';" >> lib/.secrets.g.dart
echo "const exolixApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
echo "const exolixCakeWalletApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
echo "const exolixMoneroApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
echo "const robinhoodApplicationId = '${{ secrets.ROBINHOOD_APPLICATION_ID }}';" >> lib/.secrets.g.dart
echo "const exchangeHelperApiKey = '${{ secrets.ROBINHOOD_CID_CLIENT_SECRET }}';" >> lib/.secrets.g.dart
echo "const walletConnectProjectId = '${{ secrets.WALLET_CONNECT_PROJECT_ID }}';" >> lib/.secrets.g.dart

View file

@ -1,4 +1,4 @@
# docker buildx build --push --pull --platform linux/amd64,linux/arm64 . -f Dockerfile -t ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.4-go1.24.1
# docker buildx build --push --pull --platform linux/amd64,linux/arm64 . -f Dockerfile -t ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.0-go1.24.1-ruststablenightly
# Heavily inspired by cirrusci images
# https://github.com/cirruslabs/docker-images-android/blob/master/sdk/tools/Dockerfile
@ -15,11 +15,11 @@ LABEL org.opencontainers.image.source=https://github.com/cake-tech/cake_wallet
ENV GOLANG_VERSION=1.24.1
# Pin Flutter version to latest known-working version
ENV FLUTTER_VERSION=3.27.4
ENV FLUTTER_VERSION=3.27.0
# Pin Android Studio, platform, and build tools versions to latest known-working version
# Comes from https://developer.android.com/studio/#command-tools
ENV ANDROID_SDK_TOOLS_VERSION=11076708
ENV ANDROID_SDK_TOOLS_VERSION=13114758
# Comes from https://developer.android.com/studio/releases/build-tools
ENV ANDROID_PLATFORM_VERSION=35
ENV ANDROID_BUILD_TOOLS_VERSION=34.0.0
@ -164,9 +164,12 @@ RUN (addgroup kvm || true) && \
ENV PATH=${HOME}/.cargo/bin:${PATH}
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y && \
cargo install cargo-ndk && \
for toolchain in stable nightly; \
do \
for target in aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu aarch64-unknown-linux-gnu; \
do \
rustup target add --toolchain stable $target; \
rustup target add --toolchain $toolchain $target; \
done \
done
# Download and install Flutter
@ -175,8 +178,11 @@ ENV FLUTTER_HOME=${HOME}/sdks/flutter/${FLUTTER_VERSION}
ENV FLUTTER_ROOT=$FLUTTER_HOME
ENV PATH=${PATH}:${FLUTTER_HOME}/bin:${FLUTTER_HOME}/bin/cache/dart-sdk/bin
RUN git clone --depth 1 --branch ${FLUTTER_VERSION} https://github.com/flutter/flutter.git ${FLUTTER_HOME} \
&& yes | flutter doctor --android-licenses \
RUN git clone --branch ${FLUTTER_VERSION} https://github.com/flutter/flutter.git ${FLUTTER_HOME} && \
cd ${FLUTTER_HOME} && \
git fetch -a
RUN yes | flutter doctor --android-licenses \
&& flutter doctor \
&& chown -R root:root ${FLUTTER_HOME}

10
assets/images/history.svg Normal file
View file

@ -0,0 +1,10 @@
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_67_21)">
<path d="M70.4443 8.33334C101.173 8.33336 126 33.1598 126 63.889C126 94.6181 101.173 119.445 70.4443 119.445C53.0832 119.445 37.4581 111.458 27.3887 98.9583L37.2842 89.0628C44.7495 98.9587 56.9027 105.556 70.4443 105.556C93.361 105.556 112.111 86.8056 112.111 63.889C112.111 40.9723 93.361 22.222 70.4443 22.222C56.9027 22.222 44.7495 28.8193 37.2842 38.7152C33.1175 44.0971 30.3405 50.521 29.2988 57.639C28.9516 59.7223 28.7773 61.8057 28.7773 63.889H42.667L21.833 84.722L1 63.889H14.8887C14.8887 61.9793 14.8891 60.2431 15.2363 58.3333C15.7572 52.7778 17.146 47.5692 19.0557 42.5345C27.3891 22.5694 47.3542 8.33334 70.4443 8.33334ZM75.6523 35.4163V60.9378L93.708 78.9925L83.8125 88.889L61.7637 66.6663V35.4163H75.6523Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_67_21">
<rect width="128" height="128" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 984 B

3
assets/images/notif.svg Normal file
View file

@ -0,0 +1,3 @@
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M77.9219 103.359C77.9219 111.953 71.0469 119 62.4531 119C53.8594 119 46.8125 111.953 46.8125 103.359C46.8125 102.672 46.8125 102.156 46.9844 101.641H9V87.8906H21.0312V51.9688C21.0312 28.25 40.2812 9 64 9C87.7188 9 106.969 28.25 106.969 51.9688V87.8906H119V101.641H77.9219V103.359ZM64 22.75C47.8438 22.75 34.7812 35.8125 34.7812 51.9688V87.8906H93.2188V51.9688C93.2188 35.8125 80.1562 22.75 64 22.75Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 532 B

BIN
assets/images/qr-cake.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View file

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 150.5 91.1" enable-background="new 0 0 150.5 91.1" xml:space="preserve">
<g>
<g>
<g id="layer3_22_" transform="translate(-92,-63.999774)">
<g id="layer5_22_">
<g id="path2554_38_">
<path fill="#68B044" d="M176.9,70.8l-2.4,9.6c3.4-6.8,8.9-11.9,15.2-16.4c-4.6,5.3-8.8,10.6-11.3,16
c4.3-6.1,10.1-9.4,16.6-11.7c-8.7,7.7-15.6,16.1-20.8,24.4l-4.2-1.8C170.7,84.2,173.2,77.4,176.9,70.8L176.9,70.8z"/>
</g>
<g id="path2534_13_">
<path fill="#F5F8DE" d="M165.8,89.1l7.9,3.3c0,2-0.2,8.2,1.1,10c13.2,17,11,51.2-2.7,52c-20.8,0-28.8-14.1-28.8-27.1
c0-11.9,14.2-19.7,22.7-26.7C168.3,98.7,167.9,94.5,165.8,89.1L165.8,89.1z"/>
</g>
<g id="path2536_23_">
<path fill="#7E4798" d="M173.7,92.3l2.9,1.5c-0.3,1.9,0.1,6.1,2,7.1c8.4,5.2,16.2,10.8,19.3,16.5c11,19.9-7.7,38.4-24,36.6
c8.8-6.5,11.4-19.9,8.1-34.6c-1.3-5.7-3.4-10.9-7.1-16.8C173.3,99.9,173.9,96.3,173.7,92.3L173.7,92.3z"/>
</g>
</g>
<g id="layer4_22_">
<g id="path2540_23_">
<path fill="#010101" d="M170.5,101.8c-0.6,3.1-1.3,8.7-4,10.8c-1.1,0.8-2.3,1.6-3.5,2.4c-4.9,3.3-9.7,6.4-11.9,14.3
c-0.5,1.7-0.1,3.5,0.3,5.2c1.2,4.9,4.6,10.1,7.3,13.2c0,0.1,0.5,0.5,0.5,0.6c2.2,2.6,2.9,3.4,11.3,5.3l-0.2,0.9
c-5.1-1.3-9.2-2.6-11.9-5.6c0-0.1-0.5-0.5-0.5-0.5c-2.8-3.2-6.3-8.6-7.5-13.7c-0.5-2-0.9-3.6-0.3-5.7
c2.3-8.2,7.3-11.5,12.3-14.9c1.1-0.7,2.5-1.4,3.6-2.3C168.3,110.3,169.4,105.6,170.5,101.8L170.5,101.8z"/>
</g>
<g id="path2542_23_">
<path fill="#010101" d="M172.7,114.8c0.1,3.5-0.3,5.3,0.6,7.8c0.5,1.5,2.4,3.5,2.9,5.5c0.7,2.6,1.5,5.5,1.5,7.3
c0,2-0.1,5.8-1,9.8c-0.7,3.3-2.2,6.2-4.8,7.8c-2.7-0.5-5.8-1.5-7.6-3.1c-3.6-3.1-6.7-8.3-7.1-12.8c-0.3-3.7,3.1-9.2,7.9-11.9
c4-2.4,5-5,5.9-9.4c-1.2,3.8-2.4,6.9-6.3,9c-5.7,3-8.6,7.9-8.3,12.7c0.4,6.1,2.8,10.2,7.6,13.5c2,1.4,5.8,2.9,8.2,3.3v-0.3
c1.8-0.3,4.1-3.3,5.3-7.2c1-3.6,1.4-8.1,1.3-11c-0.1-1.7-0.8-5.3-2.2-8.6c-0.7-1.8-1.9-3.6-2.6-4.9
C173.1,120.8,173.1,118,172.7,114.8z"/>
</g>
<g id="path2544_23_">
<path fill="#010101" d="M172.1,128.5c0.1,2.4,1,5.4,1.4,8.5c0.3,2.3,0.2,4.6,0.1,6.6c-0.1,2.3-0.8,6.5-1.9,8.6
c-1-0.5-1.4-1-2.1-1.8c-0.8-1.1-1.4-2.3-1.9-3.6c-0.4-1-0.9-2.2-1.1-3.5c-0.3-2-0.2-5.2,2.1-8.4c1.8-2.6,2.2-2.8,2.8-5.7
c-0.8,2.6-1.4,2.9-3.3,5.1c-2.1,2.4-2.4,6-2.4,8.9c0,1.2,0.5,2.6,1,3.8c0.5,1.3,1,2.7,1.7,3.7c1.1,1.6,2.5,2.6,3.2,2.7
c0,0,0,0,0,0c0,0,0,0,0,0v-0.1c1.3-1.5,2.1-2.9,2.4-4.4c0.3-1.8,0.4-3.5,0.6-5.6c0.2-1.8,0.1-4.1-0.4-6.5
C173.7,133.8,172.6,130.7,172.1,128.5L172.1,128.5z"/>
</g>
<g id="path2550_23_">
<path fill="#010101" d="M172.5,99c0.1,3.5,0.3,10,1.3,12.6c0.3,0.9,2.8,4.7,4.5,9.4c1.2,3.2,1.5,6.2,1.7,7.1
c0.8,3.8-0.2,10.3-1.5,16.4c-0.7,3.3-3,7.4-5.6,9l-0.5,0.9c1.5-0.1,5.1-3.6,6.4-8.1c2.2-7.5,3-11,2-19.4
c-0.1-0.8-0.5-3.6-1.8-6.5c-1.9-4.5-4.6-8.8-4.9-9.7C173.4,109.3,172.6,103.1,172.5,99L172.5,99z"/>
</g>
<g id="path2552_23_">
<path fill="#010101" d="M173.7,92.6c-0.2,3.6-0.2,6.4,0.4,9.1c0.7,2.9,4.5,7.1,6.1,11.9c3,9.2,2.2,21.2,0.1,30.5
c-0.8,3.3-4.6,8.1-8.5,9.6l2.8,0.7c1.5-0.1,5.5-3.8,7.1-8c2.5-6.7,3-14.6,2-23c-0.1-0.8-1.4-8-2.7-11
c-1.8-4.5-4.7-7.7-5.7-10.5c-0.8-2.1-1.1-7.7-0.6-8.8L173.7,92.6z"/>
</g>
</g>
</g>
<g id="path2528_18_">
<path fill="#7E4798" d="M3.8,8.8h54.1c2,0,3.8,1.7,3.8,3.8v16c0,2.1-1.8,3.8-3.8,3.8H46.8c-2.5,0-3.6,1.4-3.6,3v52.3
c0,1.8-1.4,3.1-3.1,3.1H21.7c-1.7,0-3-1.3-3-3.1V34.9c0-1.6-1.5-2.6-2.6-2.6H3.8c-2.1,0-3.8-1.7-3.8-3.8v-16
C0,10.5,1.7,8.8,3.8,8.8z"/>
</g>
<g id="path2532_18_">
<path fill="#7E4798" d="M142.6,30h4.8c1.8,0,3.2,1.4,3.2,3.1v17.1c0,2.2,0.1,3.1-2.6,3.1c-5.3,0-7.7,2.8-7.7,5.9v28.9
c0,1.3-1.3,2.5-2.8,2.5h-17.2c-1.5,0-2.8-1.1-2.8-2.5V55.2c0-0.6,0-1.4,0.1-1.9c0.9-12.2,10.5-21.9,22.6-23.2
C140.5,30.1,141.9,30,142.6,30L142.6,30z"/>
</g>
</g>
<g>
<path fill="#010101" d="M101.7,46.3c-2.9-2.6-6.5-4.8-10.3-6.9c-1.7-0.9-6.9-5-5.1-10.8l-13.1-5.4l-0.9,0.7
c4.4,7.9,2.1,12.1-0.1,13.5c-4.4,3-10.8,6.8-13.9,10.1c-6.1,6.3-7.9,12.3-7.3,20.1c0.6,10.1,7.9,18.5,17.8,21.8
c4.3,1.4,8.3,1.6,12.7,1.6c7.1,0,14.5-1.9,19.8-6.3c5.7-4.7,9-11.8,9-19.1C110.3,58.3,107.2,51.3,101.7,46.3z M99.8,83.2
c-4.9,4-13.7,6.8-18.4,6.6c-5.2-0.3-10.3-1.1-14.8-3.3c-7.9-3.8-13.1-12.1-13.5-18.8C52.4,54,59,50.1,65.1,45.1
c3.4-2.8,8.2-4.2,10.9-9.2c0.5-1.1,0.8-3.5,0.2-6c-0.3-0.9-1.5-3.9-2-4.6l9.8,4.3c-1.2,4.5,2.5,9.2,5.5,10.9
c3,1.7,7.7,4.9,10.6,7.5c5.1,4.5,7.7,10.9,7.7,17.6C107.8,72.3,105,78.9,99.8,83.2z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 920 B

View file

@ -1,3 +1,4 @@
New themes and UI/UX improvements
Ledger flow enhancements
Add built-in Tor support (experimental)
Ledger improvements
UI/UX improvements
Bug fixes

View file

@ -1,4 +1,9 @@
New themes and UI/UX improvements
Payjoin enhancements
Ledger flow enhancements
Add built-in Tor support (experimental)
Add dEuro investments
Solana fixes/enhancements
Polygon fixes/enhancements
WalletConnect improvements
Ledger improvements
Payjoin improvements
UI/UX improvements
Bug fixes

View file

@ -266,6 +266,12 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
derivationPath: walletInfo.derivationInfo!.derivationPath!);
}
@override
Future<void> close({bool shouldCleanup = false}) async {
payjoinManager.cleanupSessions();
super.close(shouldCleanup: shouldCleanup);
}
late final PayjoinManager payjoinManager;
bool get isPayjoinAvailable => unspentCoinsInfo.values

View file

@ -31,12 +31,10 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
final PayjoinManager payjoinManager;
@observable
payjoin.Receiver? currentPayjoinReceiver;
@computed
String? get payjoinEndpoint =>
currentPayjoinReceiver?.pjUriBuilder().build().pjEndpoint();
@observable
String? payjoinEndpoint = null;
@override
String getAddress(
@ -59,16 +57,32 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
return generateP2WPKHAddress(hd: hd, index: index, network: network);
}
@action
Future<void> initPayjoin() async {
currentPayjoinReceiver = await payjoinManager.initReceiver(primaryAddress);
try {
await payjoinManager.initPayjoin();
currentPayjoinReceiver = await payjoinManager.getUnusedReceiver(primaryAddress);
payjoinEndpoint = (await currentPayjoinReceiver?.pjUri())?.pjEndpoint();
payjoinManager.resumeSessions();
} catch (e) {
printV(e);
// Ignore Connectivity errors
if (!e.toString().contains("error sending request for url")) rethrow;
}
}
@action
Future<void> newPayjoinReceiver() async {
currentPayjoinReceiver = await payjoinManager.initReceiver(primaryAddress);
try {
currentPayjoinReceiver = await payjoinManager.getUnusedReceiver(primaryAddress);
payjoinEndpoint = (await currentPayjoinReceiver?.pjUri())?.pjEndpoint();
printV("Initializing new Payjoin Receiver");
payjoinManager.spawnNewReceiver(receiver: currentPayjoinReceiver!);
payjoinManager.spawnReceiver(receiver: currentPayjoinReceiver!);
} catch (e) {
printV(e);
// Ignore Connectivity errors
if (!e.toString().contains("error sending request for url")) rethrow;
}
}
}

View file

@ -5,6 +5,8 @@ import 'dart:typed_data';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/utils/proxy_socket/abstract.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:flutter/foundation.dart';
import 'package:rxdart/rxdart.dart';
@ -42,7 +44,7 @@ class ElectrumClient {
static const aliveTimerDuration = Duration(seconds: 4);
bool get isConnected => _isConnected;
Socket? socket;
ProxySocket? socket;
void Function(ConnectionStatus)? onConnectionStatusChange;
int _id;
final Map<String, SocketTask> _tasks;
@ -72,18 +74,11 @@ class ElectrumClient {
} catch (_) {}
socket = null;
final ssl = !(useSSL == false || (useSSL == null && uri.toString().contains("btc-electrum")));
try {
if (useSSL == false || (useSSL == null && uri.toString().contains("btc-electrum"))) {
socket = await Socket.connect(host, port, timeout: connectionTimeout);
} else {
socket = await SecureSocket.connect(
host,
port,
timeout: connectionTimeout,
onBadCertificate: (_) => true,
);
}
socket = await ProxyWrapper().getSocksSocket(ssl, host, port, connectionTimeout: connectionTimeout);
} catch (e) {
printV("connect: $e");
if (e is HandshakeException) {
useSSL = !(useSSL ?? false);
}
@ -105,7 +100,6 @@ class ElectrumClient {
// use ping to determine actual connection status since we could've just not timed out yet:
// _setConnectionStatus(ConnectionStatus.connected);
socket!.listen(
(Uint8List event) {
try {

View file

@ -4,6 +4,7 @@ import 'dart:io';
import 'dart:isolate';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
import 'package:cw_core/format_amount.dart';
import 'package:cw_core/utils/print_verbose.dart';
@ -49,7 +50,6 @@ import 'package:mobx/mobx.dart';
import 'package:rxdart/subjects.dart';
import 'package:sp_scanner/sp_scanner.dart';
import 'package:hex/hex.dart';
import 'package:http/http.dart' as http;
part 'electrum_wallet.g.dart';
@ -493,10 +493,9 @@ abstract class ElectrumWalletBase
Future<void> updateFeeRates() async {
if (await checkIfMempoolAPIIsEnabled() && type == WalletType.bitcoin) {
try {
final response = await http
.get(Uri.parse("https://mempool.cakewallet.com/api/v1/fees/recommended"))
.timeout(Duration(seconds: 5));
final response = await ProxyWrapper()
.get(clearnetUri: Uri.parse("https://mempool.cakewallet.com/api/v1/fees/recommended"))
.timeout(Duration(seconds: 15));
final result = json.decode(response.body) as Map<String, dynamic>;
final slowFee = (result['economyFee'] as num?)?.toInt() ?? 0;
int mediumFee = (result['hourFee'] as num?)?.toInt() ?? 0;
@ -1176,9 +1175,7 @@ abstract class ElectrumWalletBase
}
});
return PendingBitcoinTransaction(
transaction,
type,
return PendingBitcoinTransaction(transaction, type,
electrumClient: electrumClient,
amount: estimatedTx.amount,
fee: estimatedTx.fee,
@ -1188,8 +1185,8 @@ abstract class ElectrumWalletBase
isSendAll: estimatedTx.isSendAll,
hasTaprootInputs: hasTaprootInputs,
utxos: estimatedTx.utxos,
publicKeys: estimatedTx.publicKeys
)..addListener((transaction) async {
publicKeys: estimatedTx.publicKeys)
..addListener((transaction) async {
transactionHistory.addOne(transaction);
if (estimatedTx.spendsSilentPayment) {
transactionHistory.transactions.values.forEach((tx) {
@ -1880,20 +1877,17 @@ abstract class ElectrumWalletBase
if (height != null && height > 0 && await checkIfMempoolAPIIsEnabled()) {
try {
final blockHash = await http.get(
Uri.parse(
"https://mempool.cakewallet.com/api/v1/block-height/$height",
),
);
final blockHash = await ProxyWrapper()
.get(clearnetUri: Uri.parse("https://mempool.cakewallet.com/api/v1/block-height/$height"))
.timeout(Duration(seconds: 15));
if (blockHash.statusCode == 200 &&
blockHash.body.isNotEmpty &&
jsonDecode(blockHash.body) != null) {
final blockResponse = await http.get(
Uri.parse(
"https://mempool.cakewallet.com/api/v1/block/${blockHash.body}",
),
);
final blockResponse = await ProxyWrapper()
.get(clearnetUri: Uri.parse("https://mempool.cakewallet.com/api/v1/block/${blockHash}"))
.timeout(Duration(seconds: 15));
if (blockResponse.statusCode == 200 &&
blockResponse.body.isNotEmpty &&
jsonDecode(blockResponse.body)['timestamp'] != null) {

View file

@ -6,6 +6,7 @@ import 'dart:typed_data';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cw_bitcoin/bitcoin_wallet.dart';
import 'package:cw_bitcoin/bitcoin_wallet_addresses.dart';
import 'package:cw_bitcoin/payjoin/payjoin_persister.dart';
import 'package:cw_bitcoin/payjoin/payjoin_receive_worker.dart';
import 'package:cw_bitcoin/payjoin/payjoin_send_worker.dart';
import 'package:cw_bitcoin/payjoin/payjoin_session_errors.dart';
@ -16,6 +17,7 @@ import 'package:cw_core/utils/print_verbose.dart';
import 'package:payjoin_flutter/common.dart';
import 'package:payjoin_flutter/receive.dart';
import 'package:payjoin_flutter/send.dart';
import 'package:payjoin_flutter/src/config.dart' as pj_config;
import 'package:payjoin_flutter/uri.dart' as PayjoinUri;
class PayjoinManager {
@ -31,11 +33,13 @@ class PayjoinManager {
'https://ohttp.cakewallet.com',
];
static Future<PayjoinUri.Url> randomOhttpRelayUrl() => PayjoinUri.Url.fromStr(
ohttpRelayUrls[Random.secure().nextInt(ohttpRelayUrls.length)]);
static String randomOhttpRelayUrl() =>
ohttpRelayUrls[Random.secure().nextInt(ohttpRelayUrls.length)];
static const payjoinDirectoryUrl = 'https://payjo.in';
Future<void> initPayjoin() => pj_config.PConfig.initializeApp();
Future<void> resumeSessions() async {
final allSessions = _payjoinStorage.readAllOpenSessions(_wallet.id);
@ -43,13 +47,13 @@ class PayjoinManager {
if (session.isSenderSession) {
printV("Resuming Payjoin Sender Session ${session.pjUri!}");
return _spawnSender(
sender: Sender.fromJson(session.sender!),
sender: Sender.fromJson(json: session.sender!),
pjUri: session.pjUri!,
);
}
final receiver = Receiver.fromJson(session.receiver!);
final receiver = Receiver.fromJson(json: session.receiver!);
printV("Resuming Payjoin Receiver Session ${receiver.id()}");
return _spawnReceiver(receiver: receiver);
return spawnReceiver(receiver: receiver);
});
printV("Resumed ${spawnedSessions.length} Payjoin Sessions");
@ -66,7 +70,12 @@ class PayjoinManager {
psbtBase64: originalPsbt,
pjUri: pjUri,
);
return senderBuilder.buildRecommended(minFeeRate: minFeeRateSatPerKwu);
final persister = PayjoinSenderPersister.impl();
final newSender =
await senderBuilder.buildRecommended(minFeeRate: minFeeRateSatPerKwu);
final senderToken = await newSender.persist(persister: persister);
return Sender.load(token: senderToken, persister: persister);
} catch (e) {
throw Exception('Error initializing Payjoin Sender: $e');
}
@ -112,15 +121,13 @@ class PayjoinManager {
}
} catch (e) {
_cleanupSession(pjUri);
printV(e);
await _payjoinStorage.markSenderSessionUnrecoverable(pjUri);
completer.completeError(e);
await _payjoinStorage.markSenderSessionUnrecoverable(pjUri, e.toString());
completer.complete();
}
} else if (message is PayjoinSessionError) {
_cleanupSession(pjUri);
if (message is UnrecoverableError) {
printV(message.message);
await _payjoinStorage.markSenderSessionUnrecoverable(pjUri);
await _payjoinStorage.markSenderSessionUnrecoverable(pjUri, message.message);
completer.complete();
} else if (message is RecoverableError) {
completer.complete();
@ -140,42 +147,41 @@ class PayjoinManager {
return completer.future;
}
Future<Receiver> initReceiver(String address,
Future<Receiver> getUnusedReceiver(String address,
[bool isTestnet = false]) async {
try {
final payjoinDirectory =
final session = _payjoinStorage.getUnusedActiveReceiverSession(_wallet.id);
if (session != null) {
await PayjoinUri.Url.fromStr(payjoinDirectoryUrl);
return Receiver.fromJson(json: session.receiver!);
}
return initReceiver(address);
}
Future<Receiver> initReceiver(String address, [bool isTestnet = false]) async {
final ohttpKeys = await PayjoinUri.fetchOhttpKeys(
ohttpRelay: await randomOhttpRelayUrl(),
payjoinDirectory: payjoinDirectory,
payjoinDirectory: payjoinDirectoryUrl,
);
final receiver = await Receiver.create(
final newReceiver = await NewReceiver.create(
address: address,
network: isTestnet ? Network.testnet : Network.bitcoin,
directory: payjoinDirectory,
directory: payjoinDirectoryUrl,
ohttpKeys: ohttpKeys,
ohttpRelay: await randomOhttpRelayUrl(),
);
final persister = PayjoinReceiverPersister.impl();
final receiverToken = await newReceiver.persist(persister: persister);
final receiver = await Receiver.load(persister: persister, token: receiverToken);
await _payjoinStorage.insertReceiverSession(receiver, _wallet.id);
return receiver;
} catch (e) {
throw Exception('Error initializing Payjoin Receiver: $e');
}
}
Future<void> spawnNewReceiver({
required Receiver receiver,
bool isTestnet = false,
}) async {
await _payjoinStorage.insertReceiverSession(receiver, _wallet.id);
return _spawnReceiver(isTestnet: isTestnet, receiver: receiver);
}
Future<void> _spawnReceiver({
Future<void> spawnReceiver({
required Receiver receiver,
bool isTestnet = false,
}) async {
@ -195,7 +201,8 @@ class PayjoinManager {
rawAmount = getOutputAmountFromTx(tx, _wallet);
break;
case PayjoinReceiverRequestTypes.checkIsOwned:
(_wallet.walletAddresses as BitcoinWalletAddresses).newPayjoinReceiver();
(_wallet.walletAddresses as BitcoinWalletAddresses)
.newPayjoinReceiver();
_payjoinStorage.markReceiverSessionInProgress(receiver.id());
final inputScript = message['input_script'] as Uint8List;
@ -219,6 +226,10 @@ class PayjoinManager {
case PayjoinReceiverRequestTypes.getCandidateInputs:
utxos = _wallet.getUtxoWithPrivateKeys();
if (utxos.isEmpty) {
await _wallet.updateAllUnspents();
utxos = _wallet.getUtxoWithPrivateKeys();
}
mainToIsolateSendPort?.send({
'requestId': message['requestId'],
'result': utxos,

View file

@ -0,0 +1,66 @@
import 'package:payjoin_flutter/src/generated/api/receive.dart';
import 'package:payjoin_flutter/src/generated/api/send.dart';
class PayjoinSenderPersister implements DartSenderPersister {
static DartSenderPersister impl() {
final impl = PayjoinSenderPersister();
return DartSenderPersister(
save: (sender) => impl.save(sender: sender),
load: (token) => impl.load(token: token),
);
}
final Map<String, FfiSender> _store = {};
Future<SenderToken> save({required FfiSender sender}) async {
final token = sender.key();
_store[token.toBytes().toString()] = sender;
return token;
}
Future<FfiSender> load({required SenderToken token}) async {
final sender = _store[token.toBytes().toString()];
if (sender == null) {
throw Exception('Sender not found for the provided token.');
}
return sender;
}
@override
void dispose() => _store.clear();
@override
bool get isDisposed => _store.isEmpty;
}
class PayjoinReceiverPersister implements DartReceiverPersister {
static DartReceiverPersister impl() {
final impl = PayjoinReceiverPersister();
return DartReceiverPersister(
save: (receiver) => impl.save(receiver: receiver),
load: (token) => impl.load(token: token),
);
}
final Map<String, FfiReceiver> _store = {};
Future<ReceiverToken> save({required FfiReceiver receiver}) async {
final token = receiver.key();
_store[token.toBytes().toString()] = receiver;
return token;
}
Future<FfiReceiver> load({required ReceiverToken token}) async {
final receiver = _store[token.toBytes().toString()];
if (receiver == null) {
throw Exception('Receiver not found for the provided token.');
}
return receiver;
}
@override
void dispose() => _store.clear();
@override
bool get isDisposed => _store.isEmpty;
}

View file

@ -4,14 +4,16 @@ import 'dart:isolate';
import 'dart:typed_data';
import 'package:blockchain_utils/blockchain_utils.dart';
import 'package:cw_bitcoin/payjoin/manager.dart';
import 'package:cw_bitcoin/payjoin/payjoin_session_errors.dart';
import 'package:cw_bitcoin/psbt/signer.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:http/http.dart' as http;
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:payjoin_flutter/bitcoin_ffi.dart';
import 'package:payjoin_flutter/common.dart';
import 'package:payjoin_flutter/receive.dart';
import 'package:payjoin_flutter/src/generated/frb_generated.dart' as pj;
import 'package:http/http.dart' as very_insecure_http_do_not_use; // for errors
enum PayjoinReceiverRequestTypes {
processOriginalTx,
@ -27,7 +29,7 @@ class PayjoinReceiverWorker {
final pendingRequests = <String, Completer<dynamic>>{};
PayjoinReceiverWorker._(this.sendPort);
static final client = ProxyWrapper().getHttpIOClient();
static Future<void> run(List<Object> args) async {
await pj.core.init();
@ -41,11 +43,10 @@ class PayjoinReceiverWorker {
receivePort.listen(worker.handleMessage);
try {
final httpClient = http.Client();
final receiver = Receiver.fromJson(receiverJson);
final receiver = Receiver.fromJson(json: receiverJson);
final uncheckedProposal =
await worker.receiveUncheckedProposal(httpClient, receiver);
await worker.receiveUncheckedProposal(receiver);
final originalTx = await uncheckedProposal.extractTxToScheduleBroadcast();
sendPort.send({
@ -56,14 +57,14 @@ class PayjoinReceiverWorker {
final payjoinProposal = await worker.processPayjoinProposal(
uncheckedProposal,
);
final psbt = await worker.sendFinalProposal(httpClient, payjoinProposal);
final psbt = await worker.sendFinalProposal(payjoinProposal);
sendPort.send({
'type': PayjoinReceiverRequestTypes.proposalSent,
'psbt': psbt,
});
} catch (e) {
if (e is HttpException ||
(e is http.ClientException &&
(e is very_insecure_http_do_not_use.ClientException &&
e.message.contains("Software caused connection abort"))) {
sendPort.send(PayjoinSessionError.recoverable(e.toString()));
} else {
@ -97,15 +98,16 @@ class PayjoinReceiverWorker {
return completer.future;
}
Future<UncheckedProposal> receiveUncheckedProposal(
http.Client httpClient, Receiver session) async {
Future<UncheckedProposal> receiveUncheckedProposal(Receiver session) async {
while (true) {
printV("Polling for Proposal (${session.id()})");
final extractReq = await session.extractReq();
final extractReq = await session.extractReq(
ohttpRelay: await PayjoinManager.randomOhttpRelayUrl(),
);
final request = extractReq.$1;
final url = Uri.parse(request.url.asString());
final httpRequest = await httpClient.post(url,
final httpRequest = await client.post(url,
headers: {'Content-Type': request.contentType}, body: request.body);
final proposal = await session.processRes(
@ -114,13 +116,14 @@ class PayjoinReceiverWorker {
}
}
Future<String> sendFinalProposal(
http.Client httpClient, PayjoinProposal finalProposal) async {
final req = await finalProposal.extractV2Req();
Future<String> sendFinalProposal(PayjoinProposal finalProposal) async {
final req = await finalProposal.extractReq(
ohttpRelay: await PayjoinManager.randomOhttpRelayUrl(),
);
final proposalReq = req.$1;
final proposalCtx = req.$2;
final request = await httpClient.post(
final request = await client.post(
Uri.parse(proposalReq.url.asString()),
headers: {"Content-Type": proposalReq.contentType},
body: proposalReq.body,
@ -171,7 +174,7 @@ class PayjoinReceiverWorker {
final listUnspent =
await _sendRequest(PayjoinReceiverRequestTypes.getCandidateInputs);
final unspent = listUnspent as List<UtxoWithPrivateKey>;
if (unspent.isEmpty) throw Exception('No unspent outputs available');
if (unspent.isEmpty) throw RecoverableError('No unspent outputs available');
final selectedUtxo = await _inputPairFromUtxo(unspent[0]);
final pj6 = await pj5.contributeInputs(replacementInputs: [selectedUtxo]);
@ -214,6 +217,6 @@ class PayjoinReceiverWorker {
sequence: 0,
);
return InputPair.newInstance(txin, psbtin);
return InputPair.newInstance(txin: txin, psbtin: psbtin);
}
}

View file

@ -5,10 +5,12 @@ import 'dart:isolate';
import 'package:cw_bitcoin/payjoin/manager.dart';
import 'package:cw_bitcoin/payjoin/payjoin_session_errors.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:http/http.dart' as http;
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:payjoin_flutter/common.dart';
import 'package:payjoin_flutter/send.dart';
import 'package:payjoin_flutter/src/generated/frb_generated.dart' as pj;
import 'package:payjoin_flutter/src/generated/api/send/error.dart' as pj_error;
import 'package:payjoin_flutter/uri.dart' as pj_uri;
enum PayjoinSenderRequestTypes {
requestPosted,
@ -29,7 +31,7 @@ class PayjoinSenderWorker {
final senderJson = args[1] as String;
final pjUrl = args[2] as String;
final sender = Sender.fromJson(senderJson);
final sender = Sender.fromJson(json: senderJson);
final worker = PayjoinSenderWorker._(sendPort, pjUrl);
try {
@ -42,19 +44,17 @@ class PayjoinSenderWorker {
sendPort.send(e);
}
}
final client = ProxyWrapper().getHttpIOClient();
/// Run a payjoin sender (V2 protocol first, fallback to V1).
Future<String> runSender(Sender sender) async {
final httpClient = http.Client();
try {
return await _runSenderV2(sender, httpClient);
return await _runSenderV2(sender);
} catch (e) {
printV(e);
if (e is PayjoinException &&
// TODO condition on error type instead of message content
e.message?.contains('parse receiver public key') == true) {
return await _runSenderV1(sender, httpClient);
if (e is pj_error.FfiCreateRequestError) {
return await _runSenderV1(sender);
} else if (e is HttpException) {
printV(e);
throw Exception(PayjoinSessionError.recoverable(e.toString()));
@ -65,13 +65,14 @@ class PayjoinSenderWorker {
}
/// Attempt to send payjoin using the V2 of the protocol.
Future<String> _runSenderV2(Sender sender, http.Client httpClient) async {
Future<String> _runSenderV2(Sender sender) async {
try {
final postRequest = await sender.extractV2(
ohttpProxyUrl: await PayjoinManager.randomOhttpRelayUrl(),
ohttpProxyUrl:
await pj_uri.Url.fromStr(PayjoinManager.randomOhttpRelayUrl()),
);
final postResult = await _postRequest(httpClient, postRequest.$1);
final postResult = await _postRequest(postRequest.$1);
final getContext =
await postRequest.$2.processResponse(response: postResult);
@ -83,7 +84,7 @@ class PayjoinSenderWorker {
final getRequest = await getContext.extractReq(
ohttpRelay: await PayjoinManager.randomOhttpRelayUrl(),
);
final getRes = await _postRequest(httpClient, getRequest.$1);
final getRes = await _postRequest(getRequest.$1);
final proposalPsbt = await getContext.processResponse(
response: getRes,
ohttpCtx: getRequest.$2,
@ -97,20 +98,20 @@ class PayjoinSenderWorker {
}
/// Attempt to send payjoin using the V1 of the protocol.
Future<String> _runSenderV1(Sender sender, http.Client httpClient) async {
Future<String> _runSenderV1(Sender sender) async {
try {
final postRequest = await sender.extractV1();
final response = await _postRequest(httpClient, postRequest.$1);
final response = await _postRequest(postRequest.$1);
sendPort.send({'type': PayjoinSenderRequestTypes.requestPosted});
return await postRequest.$2.processResponse(response: response);
} catch (e) {
throw PayjoinSessionError.unrecoverable('Send V1 payjoin error: $e');
} catch (e, stack) {
throw PayjoinSessionError.unrecoverable('Send V1 payjoin error: $e, $stack');
}
}
Future<List<int>> _postRequest(http.Client client, Request req) async {
Future<List<int>> _postRequest(Request req) async {
final httpRequest = await client.post(Uri.parse(req.url.asString()),
headers: {'Content-Type': req.contentType}, body: req.body);

View file

@ -23,6 +23,14 @@ class PayjoinStorage {
),
);
PayjoinSession? getUnusedActiveReceiverSession(String walletId) =>
_payjoinSessionSources.values
.where((session) =>
session.walletId == walletId &&
session.status == PayjoinSessionStatus.created.name &&
!session.isSenderSession)
.firstOrNull;
Future<void> markReceiverSessionComplete(
String sessionId, String txId, String amount) async {
final session = _payjoinSessionSources.get("$_receiverPrefix${sessionId}")!;
@ -76,10 +84,11 @@ class PayjoinStorage {
await session.save();
}
Future<void> markSenderSessionUnrecoverable(String pjUrl) async {
Future<void> markSenderSessionUnrecoverable(String pjUrl, String reason) async {
final session = _payjoinSessionSources.get("$_senderPrefix$pjUrl")!;
session.status = PayjoinSessionStatus.unrecoverable.name;
session.error = reason;
await session.save();
}

View file

@ -5,34 +5,39 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8"
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
url: "https://pub.dev"
source: hosted
version: "47.0.0"
version: "76.0.0"
_macros:
dependency: transitive
description: dart
source: sdk
version: "0.3.3"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80"
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
url: "https://pub.dev"
source: hosted
version: "4.7.0"
version: "6.11.0"
args:
dependency: transitive
description:
name: args
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
url: "https://pub.dev"
source: hosted
version: "2.6.0"
version: "2.7.0"
asn1lib:
dependency: transitive
description:
name: asn1lib
sha256: "4bae5ae63e6d6dd17c4aac8086f3dec26c0236f6a0f03416c6c19d830c367cf5"
sha256: "1c296cd268f486cabcc3930e9b93a8133169305f18d722916e675959a88f6d2c"
url: "https://pub.dev"
source: hosted
version: "1.5.8"
version: "1.5.9"
async:
dependency: transitive
description:
@ -121,10 +126,10 @@ packages:
dependency: transitive
description:
name: build
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0
url: "https://pub.dev"
source: hosted
version: "2.4.1"
version: "2.4.2"
build_cli_annotations:
dependency: transitive
description:
@ -137,42 +142,42 @@ packages:
dependency: transitive
description:
name: build_config
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
version: "1.1.2"
build_daemon:
dependency: transitive
description:
name: build_daemon
sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9"
sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
version: "4.0.4"
build_resolvers:
dependency: "direct dev"
description:
name: build_resolvers
sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6"
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
url: "https://pub.dev"
source: hosted
version: "2.0.10"
version: "2.4.4"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
url: "https://pub.dev"
source: hosted
version: "2.4.13"
version: "2.4.15"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41"
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
url: "https://pub.dev"
source: hosted
version: "7.2.10"
version: "8.0.0"
built_collection:
dependency: transitive
description:
@ -185,10 +190,10 @@ packages:
dependency: transitive
description:
name: built_value
sha256: "8b158ab94ec6913e480dc3f752418348b5ae099eb75868b5f4775f0572999c61"
sha256: "082001b5c3dc495d4a42f1d5789990505df20d8547d42507c29050af6933ee27"
url: "https://pub.dev"
source: hosted
version: "8.9.4"
version: "8.10.1"
cake_backup:
dependency: transitive
description:
@ -296,10 +301,10 @@ packages:
dependency: transitive
description:
name: dart_style
sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4"
sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
url: "https://pub.dev"
source: hosted
version: "2.2.4"
version: "2.3.8"
dart_varuint_bitcoin:
dependency: transitive
description:
@ -389,10 +394,10 @@ packages:
dependency: transitive
description:
name: flutter_rust_bridge
sha256: "3292ad6085552987b8b3b9a7e5805567f4013372d302736b702801acb001ee00"
sha256: "5a5c7a5deeef2cc2ffe6076a33b0429f4a20ceac22a397297aed2b1eb067e611"
url: "https://pub.dev"
source: hosted
version: "2.7.1"
version: "2.9.0"
flutter_test:
dependency: "direct dev"
description: flutter
@ -402,10 +407,10 @@ packages:
dependency: transitive
description:
name: flutter_web_bluetooth
sha256: "1363831def5eed1e1064d1eca04e8ccb35446e8f758579c3c519e156b77926da"
sha256: ad26a1b3fef95b86ea5f63793b9a0cdc1a33490f35d754e4e711046cae3ebbf8
url: "https://pub.dev"
source: hosted
version: "1.0.0"
version: "1.1.0"
flutter_web_plugins:
dependency: transitive
description: flutter
@ -415,10 +420,10 @@ packages:
dependency: transitive
description:
name: freezed_annotation
sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2
sha256: c87ff004c8aa6af2d531668b46a4ea379f7191dc6dfa066acd53d506da6e044b
url: "https://pub.dev"
source: hosted
version: "2.4.4"
version: "3.0.0"
frontend_server_client:
dependency: transitive
description:
@ -439,18 +444,18 @@ packages:
dependency: transitive
description:
name: google_identity_services_web
sha256: "55580f436822d64c8ff9a77e37d61f5fb1e6c7ec9d632a43ee324e2a05c3c6c9"
sha256: "5d187c46dc59e02646e10fe82665fc3884a9b71bc1c90c2b8b749316d33ee454"
url: "https://pub.dev"
source: hosted
version: "0.3.3"
version: "0.3.3+1"
googleapis_auth:
dependency: transitive
description:
name: googleapis_auth
sha256: befd71383a955535060acde8792e7efc11d2fccd03dd1d3ec434e85b68775938
sha256: b81fe352cc4a330b3710d2b7ad258d9bcef6f909bb759b306bf42973a7d046db
url: "https://pub.dev"
source: hosted
version: "1.6.0"
version: "2.0.0"
graphs:
dependency: transitive
description:
@ -463,10 +468,10 @@ packages:
dependency: "direct main"
description:
name: grpc
sha256: "5b99b7a420937d4361ece68b798c9af8e04b5bc128a7859f2a4be87427694813"
sha256: "30e1edae6846b163a64f6d8716e3443980fe1f7d2d1f086f011d24ea186f2582"
url: "https://pub.dev"
source: hosted
version: "4.0.1"
version: "4.0.4"
hex:
dependency: transitive
description:
@ -487,18 +492,18 @@ packages:
dependency: "direct dev"
description:
name: hive_generator
sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938"
sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4"
url: "https://pub.dev"
source: hosted
version: "1.1.3"
version: "2.0.1"
http:
dependency: "direct main"
description:
name: http
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
url: "https://pub.dev"
source: hosted
version: "1.3.0"
version: "1.4.0"
http2:
dependency: transitive
description:
@ -519,10 +524,10 @@ packages:
dependency: transitive
description:
name: http_parser
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
version: "4.1.2"
intl:
dependency: "direct main"
description:
@ -592,10 +597,10 @@ packages:
dependency: "direct main"
description:
name: ledger_flutter_plus
sha256: "1c03f3c4a9754b5f0170a9eb9552ec54fa86e985f8ee71a255ee2c5629b53d31"
sha256: "531da5daba5731d9eca2732881ef2f039b97bf8aa3564e7098dfa99a9b07a8e6"
url: "https://pub.dev"
source: hosted
version: "1.5.1"
version: "1.5.3"
ledger_litecoin:
dependency: "direct main"
description:
@ -621,6 +626,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
macros:
dependency: transitive
description:
name: macros
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
url: "https://pub.dev"
source: hosted
version: "0.1.3-main.0"
matcher:
dependency: transitive
description:
@ -665,10 +678,10 @@ packages:
dependency: "direct dev"
description:
name: mobx_codegen
sha256: d4beb9cea4b7b014321235f8fdc7c2193ee0fe1d1198e9da7403f8bc85c4407c
sha256: e0abbbc651a69550440f6b65c99ec222a1e2a4afd7baec8ba0f3088c7ca582a8
url: "https://pub.dev"
source: hosted
version: "2.3.0"
version: "2.7.1"
nested:
dependency: transitive
description:
@ -690,10 +703,10 @@ packages:
dependency: transitive
description:
name: package_config
sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67"
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.2.0"
path:
dependency: transitive
description:
@ -714,10 +727,10 @@ packages:
dependency: transitive
description:
name: path_provider_android
sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2"
sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9
url: "https://pub.dev"
source: hosted
version: "2.2.15"
version: "2.2.17"
path_provider_foundation:
dependency: transitive
description:
@ -754,11 +767,11 @@ packages:
dependency: "direct main"
description:
path: "."
ref: "6a3eb32fb9467ac12e7b75d3de47de4ca44fd88c"
resolved-ref: "6a3eb32fb9467ac12e7b75d3de47de4ca44fd88c"
url: "https://github.com/konstantinullrich/payjoin-flutter"
ref: da83a23f3a011cb49eb3b6513cd485b3fb8867ff
resolved-ref: da83a23f3a011cb49eb3b6513cd485b3fb8867ff
url: "https://github.com/OmarHatem28/payjoin-flutter"
source: git
version: "0.21.0"
version: "0.23.0"
petitparser:
dependency: transitive
description:
@ -811,26 +824,26 @@ packages:
dependency: transitive
description:
name: provider
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84"
url: "https://pub.dev"
source: hosted
version: "6.1.2"
version: "6.1.5"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd"
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
url: "https://pub.dev"
source: hosted
version: "2.1.5"
version: "2.2.0"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
sha256: "81876843eb50dc2e1e5b151792c9a985c5ed2536914115ed04e9c8528f6647b0"
sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
url: "https://pub.dev"
source: hosted
version: "1.4.0"
version: "1.5.0"
quiver:
dependency: transitive
description:
@ -859,18 +872,18 @@ packages:
dependency: "direct main"
description:
name: shared_preferences
sha256: "846849e3e9b68f3ef4b60c60cf4b3e02e9321bc7f4d8c4692cf87ffa82fc8a3a"
sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5"
url: "https://pub.dev"
source: hosted
version: "2.5.2"
version: "2.5.3"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: a768fc8ede5f0c8e6150476e14f38e2417c0864ca36bb4582be8e21925a03c22
sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac"
url: "https://pub.dev"
source: hosted
version: "2.4.6"
version: "2.4.10"
shared_preferences_foundation:
dependency: transitive
description:
@ -915,18 +928,18 @@ packages:
dependency: transitive
description:
name: shelf
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
url: "https://pub.dev"
source: hosted
version: "1.4.1"
version: "1.4.2"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "3.0.0"
sky_engine:
dependency: transitive
description: flutter
@ -935,27 +948,37 @@ packages:
socks5_proxy:
dependency: transitive
description:
name: socks5_proxy
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
url: "https://pub.dev"
source: hosted
version: "1.0.6"
path: "."
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
url: "https://github.com/LacticWhale/socks_dart"
source: git
version: "2.1.0"
socks_socket:
dependency: "direct main"
description:
path: "."
ref: e6232c53c1595469931ababa878759a067c02e94
resolved-ref: e6232c53c1595469931ababa878759a067c02e94
url: "https://github.com/sneurlax/socks_socket"
source: git
version: "1.1.1"
source_gen:
dependency: transitive
description:
name: source_gen
sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d"
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
url: "https://pub.dev"
source: hosted
version: "1.2.6"
version: "1.5.0"
source_helper:
dependency: transitive
description:
name: source_helper
sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f"
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
url: "https://pub.dev"
source: hosted
version: "1.3.3"
version: "1.3.5"
source_span:
dependency: transitive
description:
@ -973,14 +996,6 @@ packages:
url: "https://github.com/cake-tech/sp_scanner"
source: git
version: "0.0.1"
sprintf:
dependency: transitive
description:
name: sprintf
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
stack_trace:
dependency: transitive
description:
@ -1033,10 +1048,19 @@ packages:
dependency: transitive
description:
name: timing
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
version: "1.0.1"
tor_binary:
dependency: transitive
description:
path: "."
ref: cb811c610871a9517d47134b87c2f590c15c96c5
resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
url: "https://github.com/MrCyjaneK/flutter-tor_binary"
source: git
version: "4.7.14"
tuple:
dependency: transitive
description:
@ -1057,10 +1081,10 @@ packages:
dependency: transitive
description:
name: universal_ble
sha256: "1fad089150a29db82b3b7d60327e18c5ad6b3a5bb509defc1c690b0a76b9c098"
sha256: "35d210e93a5938c6a6d1fd3c710cf4ac90b1bdd1b11c8eb2beeb32600672e6e6"
url: "https://pub.dev"
source: hosted
version: "0.15.0"
version: "0.17.0"
universal_platform:
dependency: transitive
description:
@ -1077,14 +1101,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.3.0"
uuid:
dependency: transitive
description:
name: uuid
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
url: "https://pub.dev"
source: hosted
version: "4.5.1"
vector_math:
dependency: transitive
description:
@ -1121,18 +1137,18 @@ packages:
dependency: transitive
description:
name: web_socket
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
url: "https://pub.dev"
source: hosted
version: "0.1.6"
version: "1.0.1"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5"
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
url: "https://pub.dev"
source: hosted
version: "3.0.2"
version: "3.0.3"
xdg_directories:
dependency: transitive
description:
@ -1166,5 +1182,5 @@ packages:
source: hosted
version: "2.2.2"
sdks:
dart: ">=3.5.0 <4.0.0"
flutter: ">=3.24.0"
dart: ">=3.6.0 <4.0.0"
flutter: ">=3.27.0"

View file

@ -42,8 +42,8 @@ dependencies:
url: https://github.com/cake-tech/bech32.git
payjoin_flutter:
git:
url: https://github.com/konstantinullrich/payjoin-flutter
ref: 6a3eb32fb9467ac12e7b75d3de47de4ca44fd88c #cake-v1
url: https://github.com/OmarHatem28/payjoin-flutter
ref: da83a23f3a011cb49eb3b6513cd485b3fb8867ff #cake-v2
ledger_flutter_plus: ^1.4.1
ledger_bitcoin:
git:
@ -54,14 +54,18 @@ dependencies:
git:
url: https://github.com/cake-tech/ledger-flutter-plus-plugins
path: packages/ledger-litecoin
socks_socket:
git:
url: https://github.com/sneurlax/socks_socket
ref: e6232c53c1595469931ababa878759a067c02e94
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.4.7
build_resolvers: ^2.0.9
build_runner: ^2.4.15
build_resolvers: ^2.4.4
mobx_codegen: ^2.0.7
hive_generator: ^1.1.3
hive_generator: ^2.0.1
dependency_overrides:
watcher: ^1.1.0

View file

@ -7,7 +7,7 @@ homepage: https://cakewallet.com
environment:
sdk: '>=2.19.0 <3.0.0'
flutter: ">=1.17.0"
flutter: ">=1.20.0"
dependencies:
flutter:
@ -33,9 +33,9 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.4.7
build_runner: ^2.4.15
mobx_codegen: ^2.0.7
hive_generator: ^1.1.3
hive_generator: ^2.0.1
dependency_overrides:
watcher: ^1.1.0

View file

@ -111,7 +111,8 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
CryptoCurrency.zano,
CryptoCurrency.ton,
CryptoCurrency.flip,
CryptoCurrency.deuro
CryptoCurrency.deuro,
CryptoCurrency.usdtbsc,
];
static const havenCurrencies = [
@ -233,6 +234,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
static const zano = CryptoCurrency(title: 'ZANO', tag: 'ZANO', fullName: 'Zano', raw: 96, name: 'zano', iconPath: 'assets/images/zano_icon.png', decimals: 12);
static const flip = CryptoCurrency(title: 'FLIP', tag: 'ETH', fullName: 'Chainflip', raw: 97, name: 'flip', iconPath: 'assets/images/flip_icon.png', decimals: 18);
static const deuro = CryptoCurrency(title: 'DEURO', tag: 'ETH', fullName: 'Decentralized Euro', raw: 98, name: 'deuro', iconPath: 'assets/images/deuro_icon.png', decimals: 18);
static const usdtbsc = CryptoCurrency(title: 'USDT', tag: 'BSC', fullName: 'USDT Binance coin', raw: 99, name: 'usdtbsc', iconPath: 'assets/images/usdtbsc_icon.png', decimals: 18);
static final Map<int, CryptoCurrency> _rawCurrencyMap =
[...all, ...havenCurrencies].fold<Map<int, CryptoCurrency>>(<int, CryptoCurrency>{}, (acc, item) {

View file

@ -1,7 +1,7 @@
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:intl/intl.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
// FIXME: Hardcoded values; Works only for monero
@ -234,10 +234,14 @@ int getHavenHeightByDate({required DateTime date}) {
}
Future<int> getHavenCurrentHeight() async {
final response = await http.get(Uri.parse('https://explorer.havenprotocol.org/api/networkinfo'));
final req = await ProxyWrapper().getHttpClient()
.getUrl(Uri.parse('https://explorer.havenprotocol.org/api/networkinfo'))
.timeout(Duration(seconds: 15));
final response = await req.close();
final stringResponse = await response.transform(utf8.decoder).join();
if (response.statusCode == 200) {
final info = jsonDecode(response.body);
final info = jsonDecode(stringResponse);
return info['data']['height'] as int;
} else {
throw Exception('Failed to load current blockchain height');
@ -269,13 +273,13 @@ const bitcoinDates = {
};
Future<int> getBitcoinHeightByDateAPI({required DateTime date}) async {
final response = await http.get(
Uri.parse(
"https://mempool.cakewallet.com/api/v1/mining/blocks/timestamp/${(date.millisecondsSinceEpoch / 1000).round()}",
),
);
final req = await ProxyWrapper().getHttpClient()
.getUrl(Uri.parse("https://mempool.cakewallet.com/api/v1/mining/blocks/timestamp/${(date.millisecondsSinceEpoch / 1000).round()}"))
.timeout(Duration(seconds: 15));
final response = await req.close();
final stringResponse = await response.transform(utf8.decoder).join();
return jsonDecode(response.body)['height'] as int;
return jsonDecode(stringResponse)['height'] as int;
}
int getBitcoinHeightByDate({required DateTime date}) {

View file

@ -1,12 +1,12 @@
import 'dart:io';
import 'package:cw_core/keyable.dart';
import 'package:cw_core/utils/proxy_socket/abstract.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:hive/hive.dart';
import 'package:cw_core/hive_type_ids.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:http/io_client.dart' as ioc;
import 'dart:math' as math;
import 'package:convert/convert.dart';
@ -184,23 +184,17 @@ class Node extends HiveObject with Keyable {
final body = {'jsonrpc': '2.0', 'id': '0', 'method': "getinfo"};
try {
final authenticatingClient = HttpClient();
authenticatingClient.badCertificateCallback =
((X509Certificate cert, String host, int port) => true);
final http.Client client = ioc.IOClient(authenticatingClient);
final jsonBody = json.encode(body);
final response = await client.post(
rpcUri,
final response = await ProxyWrapper().post(
clearnetUri: rpcUri,
headers: {'Content-Type': 'application/json'},
body: jsonBody,
);
printV("node check response: ${response.body}");
final resBody = json.decode(response.body) as Map<String, dynamic>;
return resBody['result']['height'] != null;
} catch (e) {
printV("error: $e");
@ -218,11 +212,7 @@ class Node extends HiveObject with Keyable {
final body = {'jsonrpc': '2.0', 'id': '0', 'method': methodName};
try {
final authenticatingClient = HttpClient();
authenticatingClient.badCertificateCallback =
((X509Certificate cert, String host, int port) => true);
final http.Client client = ioc.IOClient(authenticatingClient);
final client = ProxyWrapper().getHttpIOClient();
final jsonBody = json.encode(body);
@ -242,15 +232,15 @@ class Node extends HiveObject with Keyable {
return !(response['offline'] as bool);
}
printV("node check response: ${response.body}");
final responseString = await response.body;
if ((response.body.contains("400 Bad Request") // Some other generic error
if ((responseString.contains("400 Bad Request") // Some other generic error
||
response.body.contains("plain HTTP request was sent to HTTPS port") // Cloudflare
responseString.contains("plain HTTP request was sent to HTTPS port") // Cloudflare
||
response.headers["location"] != null // Generic reverse proxy
||
response.body
responseString
.contains("301 Moved Permanently") // Poorly configured generic reverse proxy
) &&
!(useSSL ?? false)) {
@ -277,15 +267,16 @@ class Node extends HiveObject with Keyable {
}
Future<bool> requestNodeWithProxy() async {
if (!isValidProxyAddress /* && !Tor.instance.enabled*/) {
if (!isValidProxyAddress && !CakeTor.instance.enabled) {
return false;
}
String? proxy = socksProxyAddress;
// if ((proxy?.isEmpty ?? true) && Tor.instance.enabled) {
// proxy = "${InternetAddress.loopbackIPv4.address}:${Tor.instance.port}";
// }
if ((proxy?.isEmpty ?? true) && CakeTor.instance.enabled) {
proxy = "${InternetAddress.loopbackIPv4.address}:${CakeTor.instance.port}";
}
printV("proxy: $proxy");
if (proxy == null) {
return false;
}
@ -305,13 +296,9 @@ class Node extends HiveObject with Keyable {
// you try to communicate with it
Future<bool> requestElectrumServer() async {
try {
final Socket socket;
if (useSSL == true) {
socket = await SecureSocket.connect(uri.host, uri.port,
timeout: Duration(seconds: 5), onBadCertificate: (_) => true);
} else {
socket = await Socket.connect(uri.host, uri.port, timeout: Duration(seconds: 5));
}
final ProxySocket socket;
socket = await ProxyWrapper().getSocksSocket(useSSL ?? false, uri.host, uri.port);
socket.destroy();
return true;
@ -322,8 +309,8 @@ class Node extends HiveObject with Keyable {
Future<bool> requestNanoNode() async {
try {
final response = await http.post(
uri,
final response = await ProxyWrapper().post(
clearnetUri: uri,
headers: {"Content-Type": "application/json", "nano-app": "cake-wallet"},
body: jsonEncode(
{
@ -332,7 +319,8 @@ class Node extends HiveObject with Keyable {
},
),
);
final data = await jsonDecode(response.body);
final data = jsonDecode(response.body);
if (response.statusCode != 200 ||
data["error"] != null ||
data["balance"] == null ||
@ -348,13 +336,14 @@ class Node extends HiveObject with Keyable {
Future<bool> requestEthereumServer() async {
try {
final response = await http.get(
uri,
headers: {'Content-Type': 'application/json'},
);
final req = await ProxyWrapper().getHttpClient()
.getUrl(uri,)
.timeout(Duration(seconds: 15));
final response = await req.close();
return response.statusCode >= 200 && response.statusCode < 300;
} catch (_) {
} catch (err) {
printV("Failed to request ethereum server: $err");
return false;
}
}
@ -462,7 +451,7 @@ class DaemonRpc {
/// Perform a JSON-RPC call with Digest Authentication.
Future<Map<String, dynamic>> call(String method, Map<String, dynamic> params) async {
final http.Client client = http.Client();
final client = ProxyWrapper().getHttpIOClient();
final DigestAuth digestAuth = DigestAuth(username, password);
// Initial request to get the `WWW-Authenticate` header.

View file

@ -1,20 +1,19 @@
import 'dart:convert';
import 'package:http/http.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:on_chain/solana/solana.dart';
class SolanaRPCHTTPService implements SolanaJSONRPCService {
SolanaRPCHTTPService(
{required this.url, Client? client, this.defaultRequestTimeout = const Duration(seconds: 30)})
: client = client ?? Client();
{required this.url,
this.defaultRequestTimeout = const Duration(seconds: 30)});
@override
final String url;
final Client client;
final Duration defaultRequestTimeout;
@override
Future<Map<String, dynamic>> call(SolanaRequestDetails params, [Duration? timeout]) async {
final response = await client.post(
Uri.parse(url),
Future<Map<String, dynamic>> call(SolanaRequestDetails params,
[Duration? timeout]) async {
final response = await ProxyWrapper().post(
clearnetUri: Uri.parse(url),
body: params.toRequestBody(),
headers: {
'Content-Type': 'application/json',

View file

@ -0,0 +1,29 @@
import 'dart:typed_data';
import 'package:http/http.dart' as very_insecure_http_do_not_use;
enum RequestNetwork {
clearnet,
tor,
}
enum RequestMethod {
get,
post,
put,
delete,
newHttpClient,
newHttpIOClient,
newProxySocket,
}
abstract class ProxyLogger {
void log({
required Uri? uri,
required RequestMethod method,
required Uint8List body,
required very_insecure_http_do_not_use.Response? response,
required RequestNetwork network,
required String? error,
});
}

View file

@ -0,0 +1,63 @@
import 'dart:typed_data';
import 'package:cw_core/utils/proxy_logger/abstract.dart';
import 'package:http/http.dart' as very_insecure_http_do_not_use;
class MemoryProxyLoggerEntry {
MemoryProxyLoggerEntry({
required this.trace,
required this.uri,
required this.body,
required this.network,
required this.method,
required this.response,
required this.error,
}) : time = DateTime.now();
final StackTrace trace;
final Uri? uri;
final Uint8List body;
final RequestNetwork network;
final very_insecure_http_do_not_use.Response? response;
final RequestMethod method;
final String? error;
final DateTime time;
@override
String toString() => """MemoryProxyLoggerEntry(
uri: $uri,
body: $body,
network: $network,
method: $method,
response:
code: ${response?.statusCode},
headers: ${response?.headers},
body: ${response?.body},
error: $error,
time: $time,
trace: ${trace}
);""";
}
class MemoryProxyLogger implements ProxyLogger {
static List<MemoryProxyLoggerEntry> logs = [];
@override
void log({
required Uri? uri,
required RequestMethod method,
required Uint8List body,
required very_insecure_http_do_not_use.Response? response,
required RequestNetwork network,
required String? error,
}) {
final trace = StackTrace.current;
logs.add(MemoryProxyLoggerEntry(
method: method,
trace: trace,
uri: uri,
body: body,
network: network,
response: response,
error: error,),
);
}
}

View file

@ -0,0 +1,17 @@
import 'dart:typed_data';
import 'package:cw_core/utils/proxy_logger/abstract.dart';
import 'package:http/http.dart' as very_insecure_http_do_not_use;
// we are not doing anything
class SilentProxyLogger implements ProxyLogger {
@override
void log({
required Uri? uri,
required RequestMethod method,
required Uint8List body,
required very_insecure_http_do_not_use.Response? response,
required RequestNetwork network,
required String? error,
}) {}
}

View file

@ -0,0 +1,47 @@
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:cw_core/utils/proxy_socket/insecure.dart';
import 'package:cw_core/utils/proxy_socket/secure.dart';
import 'package:cw_core/utils/proxy_socket/socks.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:socks_socket/socks_socket.dart';
class ProxyAddress {
final String host;
final int port;
ProxyAddress({required this.host, required this.port});
}
abstract class ProxySocket {
static Future<ProxySocket> connect(bool sslEnabled, ProxyAddress address, {Duration? connectionTimeout}) async {
if (CakeTor.instance.started) {
var socksSocket = await SOCKSSocket.create(
proxyHost: InternetAddress.loopbackIPv4.address,
proxyPort: CakeTor.instance.port,
sslEnabled: sslEnabled,
);
await socksSocket.connect();
await socksSocket.connectTo(address.host, address.port);
return ProxySocketSocks(socksSocket);
}
if (sslEnabled == false) {
return ProxySocketInsecure(await Socket.connect(address.host, address.port, timeout: connectionTimeout));
} else {
return ProxySocketSecure(await SecureSocket.connect(
address.host,
address.port,
timeout: connectionTimeout,
onBadCertificate: (_) => true,
));
}
}
Future<void> close();
Future<void> destroy();
Future<void> write(String data);
StreamSubscription<List<int>> listen(Function(Uint8List event) onData, {Function (Object error)? onError, Function ()? onDone, bool cancelOnError = true});
ProxyAddress get address;
}

View file

@ -0,0 +1,34 @@
import 'package:cw_core/utils/proxy_socket/abstract.dart';
import 'dart:async';
import 'dart:typed_data';
import 'dart:io';
class ProxySocketInsecure implements ProxySocket {
final Socket socket;
ProxySocketInsecure(this.socket);
ProxyAddress get address => ProxyAddress(host: socket.remoteAddress.host, port: socket.remotePort);
@override
Future<void> close() => socket.close();
@override
Future<void> destroy() async => socket.destroy();
@override
Future<void> write(String data) async => socket.write(data);
@override
StreamSubscription<List<int>> listen(Function(Uint8List event) onData, {Function(Object error)? onError, Function()? onDone, bool cancelOnError = true}) {
return socket.listen(
(data) {
onData(Uint8List.fromList(data));
},
onError: onError,
onDone: onDone,
cancelOnError: cancelOnError,
);
}
}

View file

@ -0,0 +1,34 @@
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:cw_core/utils/proxy_socket/abstract.dart';
class ProxySocketSecure implements ProxySocket {
final SecureSocket socket;
ProxySocketSecure(this.socket);
ProxyAddress get address => ProxyAddress(host: socket.remoteAddress.host, port: socket.remotePort);
@override
Future<void> close() => socket.close();
@override
Future<void> destroy() async => socket.destroy();
@override
Future<void> write(String data) async => socket.write(data);
@override
StreamSubscription<List<int>> listen(Function(Uint8List event) onData, {Function(Object error)? onError, Function()? onDone, bool cancelOnError = true}) {
return socket.listen(
(data) {
onData(Uint8List.fromList(data));
},
onError: onError,
onDone: onDone,
cancelOnError: cancelOnError,
);
}
}

View file

@ -0,0 +1,36 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:cw_core/utils/proxy_socket/abstract.dart';
import 'package:socks_socket/socks_socket.dart';
class ProxySocketSocks implements ProxySocket {
final SOCKSSocket socket;
ProxySocketSocks(this.socket);
@override
ProxyAddress get address => ProxyAddress(host: socket.proxyHost, port: socket.proxyPort);
@override
Future<void> close() => socket.close();
@override
Future<void> destroy() => close();
@override
Future<void> write(String data) async => socket.write(data);
@override
StreamSubscription<List<int>> listen(Function(Uint8List event) onData, {Function(Object error)? onError, Function()? onDone, bool cancelOnError = true}) {
return socket.listen(
(data) {
onData(Uint8List.fromList(data));
},
onError: onError,
onDone: onDone,
cancelOnError: cancelOnError,
);
}
}

View file

@ -0,0 +1,447 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:cw_core/utils/proxy_logger/abstract.dart';
import 'package:cw_core/utils/proxy_socket/abstract.dart';
import 'package:cw_core/utils/tor/abstract.dart';
import 'package:cw_core/utils/tor/android.dart';
import 'package:cw_core/utils/tor/disabled.dart';
import 'package:http/http.dart';
import 'package:socks5_proxy/socks_client.dart';
import 'package:http/io_client.dart' as ioc;
class ProxyWrapper {
static final ProxyWrapper _proxyWrapper = ProxyWrapper._internal();
static ProxyLogger? logger;
factory ProxyWrapper() {
return _proxyWrapper;
}
ProxyWrapper._internal();
Future<ProxySocket> getSocksSocket(bool sslEnabled, String host, int port, {Duration? connectionTimeout}) async {
logger?.log(
uri: Uri(
scheme: sslEnabled ? "https" : "http",
host: host,
port: port,
),
method: RequestMethod.newProxySocket,
body: Uint8List(0),
response: null,
network: requestNetwork(),
error: null
);
return ProxySocket.connect(sslEnabled, ProxyAddress(host: host, port: port), connectionTimeout: connectionTimeout);
}
RequestNetwork requestNetwork() {
return CakeTor.instance.started ? RequestNetwork.tor : RequestNetwork.clearnet;
}
ioc.IOClient getHttpIOClient({int? portOverride, bool internal = false}) {
if (!internal) {
logger?.log(
uri: null,
method: RequestMethod.newHttpIOClient,
body: Uint8List(0),
response: null,
network: requestNetwork(),
error: null,
);
}
// ignore: deprecated_member_use_from_same_package
final httpClient = ProxyWrapper().getHttpClient(portOverride: portOverride, internal: true);
return ioc.IOClient(httpClient);
}
int getPort() => CakeTor.instance.port;
@Deprecated('Use ProxyWrapper().get/post/put methods instead, and provide proper clearnet and onion uri.')
HttpClient getHttpClient({int? portOverride, bool internal = false}) {
if (!internal) {
logger?.log(
uri: null,
method: RequestMethod.newProxySocket,
body: Uint8List(0),
response: null,
network: requestNetwork(),
error: null
);
}
if (CakeTor.instance.started) {
// Assign connection factory.
final client = HttpClient();
SocksTCPClient.assignToHttpClient(client, [
ProxySettings(
InternetAddress.loopbackIPv4,
CakeTor.instance.port,
password: null,
),
]);
return client;
} else {
return HttpClient();
}
}
Future<Response> _make({
required RequestMethod method,
required ioc.IOClient client,
required Uri uri,
required Map<String, String>? headers,
String? body,
}) async {
Object? error;
Response? resp;
try {
switch (method) {
case RequestMethod.get:
resp = await client. get(
uri,
headers: headers,
);
break;
case RequestMethod.delete:
resp = await client.delete(
uri,
headers: headers,
body: body,
);
break;
case RequestMethod.post:
resp = await client.post(
uri,
headers: headers,
body: body,
);
break;
case RequestMethod.put:
resp = await client.put(
uri,
headers: headers,
body: body,
);
break;
case RequestMethod.newHttpClient:
case RequestMethod.newHttpIOClient:
case RequestMethod.newProxySocket:
throw UnimplementedError();
}
return resp;
} catch (e) {
error = e;
rethrow;
} finally {
logger?.log(
uri: uri,
method: RequestMethod.get,
body: utf8.encode(body ?? ''),
response: resp,
network: requestNetwork(),
error: error?.toString(),
);
}
}
Future<Response> get({
Map<String, String>? headers,
int? portOverride,
Uri? clearnetUri,
Uri? onionUri,
}) async {
ioc.IOClient? torClient;
bool torEnabled = CakeTor.instance.started;
if (CakeTor.instance.started) {
torEnabled = true;
} else {
torEnabled = false;
}
// if tor is enabled, try to connect to the onion url first:
if (torEnabled) {
try {
// ignore: deprecated_member_use_from_same_package
torClient = await getHttpIOClient(portOverride: portOverride, internal: true);
} catch (_) {
rethrow;
}
if (onionUri != null) {
try {
return await _make(
method: RequestMethod.get,
client: torClient,
uri: onionUri,
headers: headers,
);
} catch (_) {
rethrow;
}
}
if (clearnetUri != null) {
try {
return await _make(
method: RequestMethod.get,
client: torClient,
uri: clearnetUri,
headers: headers,
);
} catch (_) {
rethrow;
}
}
}
if (clearnetUri != null) {
try {
return HttpOverrides.runZoned(
() async {
return await _make(
method: RequestMethod.get,
client: ioc.IOClient(),
uri: clearnetUri,
headers: headers,
);
},
);
} catch (_) {
// we weren't able to get a response:
rethrow;
}
}
throw Exception("Unable to connect to server");
}
Future<Response> post({
Map<String, String>? headers,
int? portOverride,
Uri? clearnetUri,
Uri? onionUri,
String? body,
bool allowMitmMoneroBypassSSLCheck = false,
}) async {
HttpClient? torHttpClient;
HttpClient cleatnetHttpClient = HttpClient();
if (allowMitmMoneroBypassSSLCheck) {
cleatnetHttpClient.badCertificateCallback =
((X509Certificate cert, String host, int port) => true);
}
ioc.IOClient clearnetClient = ioc.IOClient(cleatnetHttpClient);
bool torEnabled = CakeTor.instance.started;
if (torEnabled) {
try {
// ignore: deprecated_member_use_from_same_package
torHttpClient = await getHttpClient(portOverride: portOverride);
} catch (_) {
rethrow;
}
if (allowMitmMoneroBypassSSLCheck) {
torHttpClient.badCertificateCallback =
((X509Certificate cert, String host, int port) => true);
}
if (onionUri != null) {
try {
return await _make(
method: RequestMethod.post,
client: ioc.IOClient(torHttpClient),
uri: onionUri,
headers: headers,
body: body,
);
} catch (_) {
rethrow;
}
}
if (clearnetUri != null) {
try {
return await _make(
method: RequestMethod.post,
client: ioc.IOClient(torHttpClient),
uri: clearnetUri,
headers: headers,
body: body,
);
} catch (_) {
rethrow;
}
}
}
if (clearnetUri != null) {
try {
return HttpOverrides.runZoned(
() async {
return await _make(
method: RequestMethod.post,
client: clearnetClient,
uri: clearnetUri,
headers: headers,
body: body,
);
},
);
} catch (_) {
rethrow;
}
}
throw Exception("Unable to connect to server");
}
Future<Response> put({
Map<String, String>? headers,
int? portOverride,
Uri? clearnetUri,
Uri? onionUri,
String? body,
}) async {
ioc.IOClient? torClient;
bool torEnabled = CakeTor.instance.started;
if (torEnabled) {
try {
// ignore: deprecated_member_use_from_same_package
torClient = await getHttpIOClient(portOverride: portOverride, internal: true);
} catch (_) {}
if (onionUri != null) {
try {
return await _make(
method: RequestMethod.put,
client: torClient!,
uri: onionUri,
headers: headers,
body: body,
);
} catch (_) {
rethrow;
}
}
if (clearnetUri != null) {
try {
return await _make(
method: RequestMethod.put,
client: torClient!,
uri: clearnetUri,
headers: headers,
body: body,
);
} catch (_) {
rethrow;
}
}
}
if (clearnetUri != null) {
try {
return HttpOverrides.runZoned(
() async {
return await _make(
method: RequestMethod.put,
client: ioc.IOClient(),
uri: clearnetUri,
headers: headers,
body: body,
);
},
);
} catch (_) {
// we weren't able to get a response:
rethrow;
}
}
throw Exception("Unable to connect to server");
}
Future<Response> delete({
Map<String, String>? headers,
int? portOverride,
Uri? clearnetUri,
Uri? onionUri,
}) async {
ioc.IOClient? torClient;
bool torEnabled = CakeTor.instance.started;
if (CakeTor.instance.started) {
torEnabled = true;
} else {
torEnabled = false;
}
// if tor is enabled, try to connect to the onion url first:
if (torEnabled) {
try {
// ignore: deprecated_member_use_from_same_package
torClient = await getHttpIOClient(portOverride: portOverride, internal: true);
} catch (_) {
rethrow;
}
if (onionUri != null) {
try {
return await _make(
method: RequestMethod.delete,
client: torClient,
uri: onionUri,
headers: headers,
);
} catch (_) {
rethrow;
}
}
if (clearnetUri != null) {
try {
return await _make(
method: RequestMethod.delete,
client: torClient,
uri: clearnetUri,
headers: headers,
);
} catch (_) {
rethrow;
}
}
}
if (clearnetUri != null) {
try {
return HttpOverrides.runZoned(
() async {
return await _make(
method: RequestMethod.delete,
client: ioc.IOClient(),
uri: clearnetUri,
headers: headers,
);
},
);
} catch (_) {
// we weren't able to get a response:
rethrow;
}
}
throw Exception("Unable to connect to server");
}
}
class CakeTor {
static final CakeTorInstance instance = CakeTorInstance.getInstance();
}

View file

@ -0,0 +1,38 @@
import 'dart:io';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/utils/tor/android.dart';
import 'package:cw_core/utils/tor/disabled.dart';
import 'package:cw_core/utils/tor/tails.dart';
abstract class CakeTorInstance {
bool get started;
int get port => -1;
bool get enabled => false;
bool get bootstrapped => false;
Future<void> start();
Future<void> stop();
static CakeTorInstance getInstance() {
if (Platform.isAndroid) {
return CakeTorAndroid();
}
if (Platform.isLinux) {
try {
final os = File("/etc/os-release").readAsLinesSync();
for (var line in os) {
if (!line.startsWith("ID=")) continue;
if (!line.contains("tails")) continue;
return CakeTorTails();
}
} catch (e) {
printV("Failed to identify linux version - /etc/os-release missing");
}
}
return CakeTorDisabled();
}
}

View file

@ -0,0 +1,73 @@
import 'dart:convert';
import 'dart:io';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/utils/tor/abstract.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as p;
import 'package:tor_binary/tor_binary_platform_interface.dart';
class CakeTorAndroid implements CakeTorInstance {
@override
bool get bootstrapped => _proc != null;
@override
bool get enabled => _proc != null;
@override
int get port => 42142;
@override
Future<void> start() async {
await _runEmbeddedTor();
}
@override
bool get started => _proc != null;
@override
Future<void> stop() async {
_proc?.kill();
await _proc?.exitCode;
_proc = null;
}
static Process? _proc;
Future<void> _runEmbeddedTor() async {
final dir = await getApplicationCacheDirectory();
final torBinPath = p.join((await TorBinaryPlatform.instance.getBinaryPath())!, "libtor.so");
printV("torPath: $torBinPath");
if (started) {
printV("Proxy is running");
return;
}
printV("Starting embedded tor");
printV("app docs: $dir");
final torrc = """
SocksPort $port
Log notice file ${p.join(dir.path, "tor.log")}
RunAsDaemon 0
DataDirectory ${p.join(dir.path, "tor-data")}
""";
final torrcPath = p.join(dir.absolute.path, "torrc");
File(torrcPath).writeAsStringSync(torrc);
if (_proc != null) {
try {
_proc?.kill();
await _proc?.exitCode;
_proc = null;
} catch (e) {
printV(e);
}
}
printV("path: $torBinPath -f $torrcPath");
_proc = await Process.start(torBinPath, ["-f", torrcPath]);
_proc?.stdout.transform(utf8.decoder).forEach(printV);
_proc?.stderr.transform(utf8.decoder).forEach(printV);
}
}

View file

@ -0,0 +1,21 @@
import 'package:cw_core/utils/tor/abstract.dart';
class CakeTorDisabled implements CakeTorInstance {
@override
bool get bootstrapped => false;
@override
bool get enabled => false;
@override
int get port => -1;
@override
Future<void> start() => throw UnimplementedError();
@override
bool get started => false;
@override
Future<void> stop() => throw UnimplementedError();
}

View file

@ -0,0 +1,21 @@
import 'package:cw_core/utils/tor/abstract.dart';
class CakeTorTails implements CakeTorInstance {
@override
bool get bootstrapped => true;
@override
bool get enabled => true;
@override
int get port => 9150;
@override
Future<void> start() async {}
@override
bool get started => true;
@override
Future<void> stop() async {}
}

View file

@ -26,18 +26,18 @@ packages:
dependency: transitive
description:
name: args
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
url: "https://pub.dev"
source: hosted
version: "2.6.0"
version: "2.7.0"
asn1lib:
dependency: transitive
description:
name: asn1lib
sha256: "4bae5ae63e6d6dd17c4aac8086f3dec26c0236f6a0f03416c6c19d830c367cf5"
sha256: "1c296cd268f486cabcc3930e9b93a8133169305f18d722916e675959a88f6d2c"
url: "https://pub.dev"
source: hosted
version: "1.5.8"
version: "1.5.9"
async:
dependency: transitive
description:
@ -67,50 +67,50 @@ packages:
dependency: transitive
description:
name: build
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0
url: "https://pub.dev"
source: hosted
version: "2.4.1"
version: "2.4.2"
build_config:
dependency: transitive
description:
name: build_config
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
version: "1.1.2"
build_daemon:
dependency: transitive
description:
name: build_daemon
sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9"
sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
version: "4.0.4"
build_resolvers:
dependency: "direct dev"
description:
name: build_resolvers
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
url: "https://pub.dev"
source: hosted
version: "2.4.2"
version: "2.4.4"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
url: "https://pub.dev"
source: hosted
version: "2.4.13"
version: "2.4.15"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
url: "https://pub.dev"
source: hosted
version: "7.3.2"
version: "8.0.0"
built_collection:
dependency: transitive
description:
@ -123,10 +123,10 @@ packages:
dependency: transitive
description:
name: built_value
sha256: "8b158ab94ec6913e480dc3f752418348b5ae099eb75868b5f4775f0572999c61"
sha256: "082001b5c3dc495d4a42f1d5789990505df20d8547d42507c29050af6933ee27"
url: "https://pub.dev"
source: hosted
version: "8.9.4"
version: "8.10.1"
cake_backup:
dependency: "direct main"
description:
@ -212,10 +212,10 @@ packages:
dependency: transitive
description:
name: dart_style
sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab"
sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
url: "https://pub.dev"
source: hosted
version: "2.3.7"
version: "2.3.8"
decimal:
dependency: "direct main"
description:
@ -326,10 +326,10 @@ packages:
dependency: "direct main"
description:
name: http
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
url: "https://pub.dev"
source: hosted
version: "1.3.0"
version: "1.4.0"
http_multi_server:
dependency: transitive
description:
@ -342,10 +342,10 @@ packages:
dependency: transitive
description:
name: http_parser
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
version: "4.1.2"
intl:
dependency: "direct main"
description:
@ -462,10 +462,10 @@ packages:
dependency: "direct dev"
description:
name: mobx_codegen
sha256: "990da80722f7d7c0017dec92040b31545d625b15d40204c36a1e63d167c73cdc"
sha256: e0abbbc651a69550440f6b65c99ec222a1e2a4afd7baec8ba0f3088c7ca582a8
url: "https://pub.dev"
source: hosted
version: "2.7.0"
version: "2.7.1"
nested:
dependency: transitive
description:
@ -479,7 +479,7 @@ packages:
description:
path: "."
ref: cake-update-v2
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
resolved-ref: "096865a8c6b89c260beadfec04f7e184c40a3273"
url: "https://github.com/cake-tech/on_chain.git"
source: git
version: "3.7.0"
@ -487,10 +487,10 @@ packages:
dependency: transitive
description:
name: package_config
sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67"
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.2.0"
path:
dependency: transitive
description:
@ -511,10 +511,10 @@ packages:
dependency: transitive
description:
name: path_provider_android
sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2"
sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9
url: "https://pub.dev"
source: hosted
version: "2.2.15"
version: "2.2.17"
path_provider_foundation:
dependency: transitive
description:
@ -583,26 +583,26 @@ packages:
dependency: transitive
description:
name: provider
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84"
url: "https://pub.dev"
source: hosted
version: "6.1.2"
version: "6.1.5"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd"
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
url: "https://pub.dev"
source: hosted
version: "2.1.5"
version: "2.2.0"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
sha256: "81876843eb50dc2e1e5b151792c9a985c5ed2536914115ed04e9c8528f6647b0"
sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
url: "https://pub.dev"
source: hosted
version: "1.4.0"
version: "1.5.0"
rational:
dependency: transitive
description:
@ -615,18 +615,18 @@ packages:
dependency: transitive
description:
name: shelf
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
url: "https://pub.dev"
source: hosted
version: "1.4.1"
version: "1.4.2"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "3.0.0"
sky_engine:
dependency: transitive
description: flutter
@ -635,11 +635,21 @@ packages:
socks5_proxy:
dependency: "direct main"
description:
name: socks5_proxy
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
url: "https://pub.dev"
source: hosted
version: "1.0.6"
path: "."
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
url: "https://github.com/LacticWhale/socks_dart"
source: git
version: "2.1.0"
socks_socket:
dependency: "direct main"
description:
path: "."
ref: e6232c53c1595469931ababa878759a067c02e94
resolved-ref: e6232c53c1595469931ababa878759a067c02e94
url: "https://github.com/sneurlax/socks_socket"
source: git
version: "1.1.1"
source_gen:
dependency: transitive
description:
@ -716,10 +726,19 @@ packages:
dependency: transitive
description:
name: timing
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
version: "1.0.1"
tor_binary:
dependency: "direct main"
description:
path: "."
ref: cb811c610871a9517d47134b87c2f590c15c96c5
resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
url: "https://github.com/MrCyjaneK/flutter-tor_binary"
source: git
version: "4.7.14"
tuple:
dependency: transitive
description:
@ -780,18 +799,18 @@ packages:
dependency: transitive
description:
name: web_socket
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
url: "https://pub.dev"
source: hosted
version: "0.1.6"
version: "1.0.1"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5"
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
url: "https://pub.dev"
source: hosted
version: "3.0.2"
version: "3.0.3"
xdg_directories:
dependency: transitive
description:
@ -809,5 +828,5 @@ packages:
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.5.0 <4.0.0"
flutter: ">=3.24.0"
dart: ">=3.6.0 <4.0.0"
flutter: ">=3.27.0"

View file

@ -25,22 +25,29 @@ dependencies:
url: https://github.com/cake-tech/cake_backup.git
ref: main
version: 1.0.0
socks5_proxy: ^1.0.4
socks5_proxy:
git:
url: https://github.com/LacticWhale/socks_dart
ref: 27ad7c2efae8d7460325c74b90f660085cbd0685
unorm_dart: ^0.3.0
on_chain:
git:
url: https://github.com/cake-tech/on_chain.git
ref: cake-update-v2
# tor:
# git:
# url: https://github.com/cake-tech/tor.git
# ref: main
socks_socket:
git:
url: https://github.com/sneurlax/socks_socket
ref: e6232c53c1595469931ababa878759a067c02e94
tor_binary:
git:
url: https://github.com/MrCyjaneK/flutter-tor_binary
ref: cb811c610871a9517d47134b87c2f590c15c96c5
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.4.7
build_resolvers: ^2.0.9
build_runner: ^2.4.15
build_resolvers: ^2.4.4
mobx_codegen: ^2.0.7
hive_generator: ^2.0.1

View file

@ -5,6 +5,8 @@ import 'package:path/path.dart' as p;
import 'package:cw_core/exceptions.dart';
import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cw_decred/amount_format.dart';
import 'package:cw_decred/pending_transaction.dart';
import 'package:cw_decred/transaction_credentials.dart';
@ -307,9 +309,10 @@ abstract class DecredWalletBase
persistantPeer = addr;
await _libwallet.closeWallet(walletInfo.name);
final network = isTestnet ? "testnet" : "mainnet";
final dirPath = await pathForWalletDir(name: walletInfo.name, type: WalletType.decred);
final config = {
"name": walletInfo.name,
"datadir": walletInfo.dirPath,
"datadir": dirPath,
"net": network,
"unsyncedaddrs": true,
};

View file

@ -8,6 +8,7 @@ import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:path/path.dart';
import 'package:hive/hive.dart';
import 'package:collection/collection.dart';
import 'package:cw_core/unspent_coins_info.dart';
@ -57,42 +58,93 @@ class DecredWalletService extends WalletService<
@override
Future<DecredWallet> create(DecredNewWalletCredentials credentials, {bool? isTestnet}) async {
await this.init();
final dirPath = await pathForWalletDir(name: credentials.walletInfo!.name, type: getType());
final network = isTestnet == true ? testnet : mainnet;
final config = {
"name": credentials.walletInfo!.name,
"datadir": credentials.walletInfo!.dirPath,
"datadir": dirPath,
"pass": credentials.password!,
"net": isTestnet == true ? testnet : mainnet,
"net": network,
"unsyncedaddrs": true,
};
await libwallet!.createWallet(jsonEncode(config));
final di = DerivationInfo(
derivationPath: isTestnet == true ? seedRestorePathTestnet : seedRestorePath);
credentials.walletInfo!.derivationInfo = di;
credentials.walletInfo!.network = network;
// ios will move our wallet directory when updating. Since we must
// recalculate the new path every time we open the wallet, ensure this path
// is not used. An older wallet will have a directory here which is a
// condition for moving the wallet when opening, so this must be kept blank
// going forward.
credentials.walletInfo!.dirPath = "";
credentials.walletInfo!.path = "";
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
await wallet.init();
return wallet;
}
void copyDirectorySync(Directory source, Directory destination) {
/// create destination folder if not exist
if (!destination.existsSync()) {
destination.createSync(recursive: true);
}
/// get all files from source (recursive: false is important here)
source.listSync(recursive: false).forEach((entity) {
final newPath = destination.path + Platform.pathSeparator + basename(entity.path);
if (entity is File) {
entity.rename(newPath);
} else if (entity is Directory) {
copyDirectorySync(entity, Directory(newPath));
}
});
}
Future<void> moveWallet(String fromPath, String toPath) async {
final oldWalletDir = new Directory(fromPath);
final newWalletDir = new Directory(toPath);
copyDirectorySync(oldWalletDir, newWalletDir);
// It would be ideal to delete the old directory here, but ios will error
// sometimes with "OS Error: No such file or directory, errno = 2" even
// after checking if it exists.
}
@override
Future<DecredWallet> openWallet(String name, String password) async {
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
final network = walletInfo.derivationInfo?.derivationPath == seedRestorePathTestnet ||
if (walletInfo.network == null || walletInfo.network == "") {
walletInfo.network = walletInfo.derivationInfo?.derivationPath == seedRestorePathTestnet ||
walletInfo.derivationInfo?.derivationPath == pubkeyRestorePathTestnet
? testnet
: mainnet;
}
await this.init();
final walletDirExists = Directory(walletInfo.dirPath).existsSync();
if (!walletDirExists) {
walletInfo.dirPath = await pathForWalletDir(name: name, type: getType());
// Cake wallet version 4.27.0 and earlier gave a wallet dir that did not
// match the name. Move those to the correct place.
final dirPath = await pathForWalletDir(name: name, type: getType());
if (walletInfo.path != "") {
// On ios the stored dir no longer exists. We can only trust the basename.
// dirPath may already be updated and lost the basename, so look at path.
final randomBasename = basename(walletInfo.path);
final oldDir = await pathForWalletDir(name: randomBasename, type: getType());
if (oldDir != dirPath) {
await this.moveWallet(oldDir, dirPath);
}
// Clear the path so this does not trigger again.
walletInfo.dirPath = "";
walletInfo.path = "";
await walletInfo.save();
}
final config = {
"name": walletInfo.name,
"datadir": walletInfo.dirPath,
"net": network,
"name": name,
"datadir": dirPath,
"net": walletInfo.network,
"unsyncedaddrs": true,
};
await libwallet!.loadWallet(jsonEncode(config));
@ -127,12 +179,11 @@ class DecredWalletService extends WalletService<
await currentWallet.renameWalletFiles(newName);
final newDirPath = await pathForWalletDir(name: newName, type: getType());
final newWalletInfo = currentWalletInfo;
newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName;
newWalletInfo.dirPath = newDirPath;
newWalletInfo.network = network;
newWalletInfo.dirPath = "";
newWalletInfo.path = "";
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
}
@ -141,18 +192,23 @@ class DecredWalletService extends WalletService<
Future<DecredWallet> restoreFromSeed(DecredRestoreWalletFromSeedCredentials credentials,
{bool? isTestnet}) async {
await this.init();
final network = isTestnet == true ? testnet : mainnet;
final dirPath = await pathForWalletDir(name: credentials.walletInfo!.name, type: getType());
final config = {
"name": credentials.walletInfo!.name,
"datadir": credentials.walletInfo!.dirPath,
"datadir": dirPath,
"pass": credentials.password!,
"mnemonic": credentials.mnemonic,
"net": isTestnet == true ? testnet : mainnet,
"net": network,
"unsyncedaddrs": true,
};
await libwallet!.createWallet(jsonEncode(config));
final di = DerivationInfo(
derivationPath: isTestnet == true ? seedRestorePathTestnet : seedRestorePath);
credentials.walletInfo!.derivationInfo = di;
credentials.walletInfo!.network = network;
credentials.walletInfo!.dirPath = "";
credentials.walletInfo!.path = "";
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
await wallet.init();
@ -165,17 +221,22 @@ class DecredWalletService extends WalletService<
Future<DecredWallet> restoreFromKeys(DecredRestoreWalletFromPubkeyCredentials credentials,
{bool? isTestnet}) async {
await this.init();
final network = isTestnet == true ? testnet : mainnet;
final dirPath = await pathForWalletDir(name: credentials.walletInfo!.name, type: getType());
final config = {
"name": credentials.walletInfo!.name,
"datadir": credentials.walletInfo!.dirPath,
"datadir": dirPath,
"pubkey": credentials.pubkey,
"net": isTestnet == true ? testnet : mainnet,
"net": network,
"unsyncedaddrs": true,
};
await libwallet!.createWatchOnlyWallet(jsonEncode(config));
final di = DerivationInfo(
derivationPath: isTestnet == true ? pubkeyRestorePathTestnet : pubkeyRestorePath);
credentials.walletInfo!.derivationInfo = di;
credentials.walletInfo!.network = network;
credentials.walletInfo!.dirPath = "";
credentials.walletInfo!.path = "";
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
await wallet.init();

View file

@ -91,26 +91,26 @@ packages:
dependency: "direct dev"
description:
name: build_resolvers
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
url: "https://pub.dev"
source: hosted
version: "2.4.2"
version: "2.4.4"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
url: "https://pub.dev"
source: hosted
version: "2.4.13"
version: "2.4.15"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
url: "https://pub.dev"
source: hosted
version: "7.3.2"
version: "8.0.0"
built_collection:
dependency: transitive
description:
@ -666,11 +666,21 @@ packages:
socks5_proxy:
dependency: transitive
description:
name: socks5_proxy
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
url: "https://pub.dev"
source: hosted
version: "1.0.6"
path: "."
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
url: "https://github.com/LacticWhale/socks_dart"
source: git
version: "2.1.0"
socks_socket:
dependency: transitive
description:
path: "."
ref: e6232c53c1595469931ababa878759a067c02e94
resolved-ref: e6232c53c1595469931ababa878759a067c02e94
url: "https://github.com/sneurlax/socks_socket"
source: git
version: "1.1.1"
source_gen:
dependency: transitive
description:
@ -751,6 +761,15 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.2"
tor_binary:
dependency: transitive
description:
path: "."
ref: cb811c610871a9517d47134b87c2f590c15c96c5
resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
url: "https://github.com/MrCyjaneK/flutter-tor_binary"
source: git
version: "4.7.14"
tuple:
dependency: transitive
description:
@ -848,5 +867,5 @@ packages:
source: hosted
version: "2.2.2"
sdks:
dart: ">=3.5.0 <4.0.0"
dart: ">=3.6.0 <4.0.0"
flutter: ">=3.24.0"

View file

@ -19,8 +19,8 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.1.11
build_resolvers: ^2.0.9
build_runner: ^2.4.15
build_resolvers: ^2.4.4
mobx_codegen: ^2.0.7
hive_generator: ^2.0.1
ffigen: ^16.1.0

View file

@ -0,0 +1,170 @@
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 DeuroGasFeeException(
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(
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 DeuroGasFeeException(
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[17].encodeCall([amount, hexToBytes(frontendCode)]),
);
final sendTransaction = () => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
return PendingEVMChainTransaction(
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 DeuroGasFeeException(
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,
)) as PendingEVMChainTransaction;
} catch (e) {
if (e.toString().contains('insufficient funds for gas')) {
final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
throw DeuroGasFeeException(
currentBalance: ethBalance.getInWei,
);
}
rethrow;
}
}
}

File diff suppressed because one or more lines are too long

View file

@ -19,7 +19,7 @@ class EthereumClient extends EVMChainClient {
Future<List<EVMChainTransactionModel>> fetchTransactions(String address,
{String? contractAddress}) async {
try {
final response = await httpClient.get(Uri.https("api.etherscan.io", "/v2/api", {
final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
"chainid": "$chainId",
"module": "account",
"action": contractAddress != null ? "tokentx" : "txlist",
@ -51,7 +51,7 @@ class EthereumClient extends EVMChainClient {
@override
Future<List<EVMChainTransactionModel>> fetchInternalTransactions(String address) async {
try {
final response = await httpClient.get(Uri.https("api.etherscan.io", "/v2/api", {
final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
"chainid": "$chainId",
"module": "account",
"action": "txlistinternal",

View file

@ -6,7 +6,7 @@ author: Cake Wallet
homepage: https://cakewallet.com
environment:
sdk: '>=2.18.2 <3.0.0'
sdk: ^3.5.0
flutter: ">=1.17.0"
dependencies:
@ -29,7 +29,7 @@ dependency_overrides:
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.4.7
build_runner: ^2.4.15
flutter:
# assets:

View file

@ -5,6 +5,7 @@ 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';
@ -12,13 +13,12 @@ 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:http/http.dart';
import 'package:web3dart/web3dart.dart';
import 'contract/erc20.dart';
abstract class EVMChainClient {
final httpClient = Client();
late final client = ProxyWrapper().getHttpIOClient();
Web3Client? _client;
//! To be overridden by all child classes
@ -47,7 +47,7 @@ abstract class EVMChainClient {
}
_client =
Web3Client(isModifiedNodeUri ? rpcUri!.toString() : node.uri.toString(), httpClient);
Web3Client(isModifiedNodeUri ? rpcUri!.toString() : node.uri.toString(), client);
return true;
} catch (e) {
@ -101,6 +101,7 @@ abstract class EVMChainClient {
String? contractAddress,
EtherAmount? gasPrice,
EtherAmount? maxFeePerGas,
Uint8List? data,
}) async {
try {
if (contractAddress == null) {
@ -108,7 +109,6 @@ abstract class EVMChainClient {
sender: senderAddress,
to: toAddress,
value: value,
// maxFeePerGas: maxFeePerGas,
);
return estimatedGas.toInt();
@ -124,7 +124,7 @@ abstract class EVMChainClient {
final gasEstimate = await _client!.estimateGas(
sender: senderAddress,
to: EthereumAddress.fromHex(contractAddress),
data: transfer.encodeCall([
data: data ?? transfer.encodeCall([
toAddress,
value.getInWei,
]),
@ -137,6 +137,21 @@ abstract class EVMChainClient {
}
}
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,
@ -149,6 +164,7 @@ abstract class EVMChainClient {
required int exponent,
String? contractAddress,
String? data,
int? gasPrice,
}) async {
assert(currency == CryptoCurrency.eth ||
currency == CryptoCurrency.maticpoly ||
@ -164,6 +180,7 @@ abstract class EVMChainClient {
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;
@ -198,6 +215,52 @@ abstract class EVMChainClient {
);
}
Future<PendingEVMChainTransaction> signApprovalTransaction({
required Credentials privateKey,
required String spender,
required BigInt amount,
required BigInt gasFee,
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: EtherAmount.fromInt(EtherUnit.gwei, priority.tip),
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,
sendTransaction: () => sendTransaction(signedTransaction),
exponent: exponent,
isInfiniteApproval: amount.toRadixString(16) == 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
);
}
Transaction createTransaction({
required EthereumAddress from,
required EthereumAddress to,
@ -293,7 +356,7 @@ abstract class EVMChainClient {
},
);
final response = await httpClient.get(
final response = await client.get(
uri,
headers: {
"Accept": "application/json",

View file

@ -22,3 +22,32 @@ class EVMChainTransactionFeesException implements Exception {
@override
String toString() => exceptionMessage;
}
class DeuroGasFeeException implements Exception {
final String exceptionMessage;
final BigInt? requiredGasFee;
final BigInt? currentBalance;
DeuroGasFeeException({
this.requiredGasFee,
this.currentBalance,
}) : exceptionMessage = _buildMessage(requiredGasFee, currentBalance);
static String _buildMessage(BigInt? requiredGasFee, BigInt? currentBalance) {
const baseMessage = 'Insufficient ETH for gas fees.';
const addEthMessage = ' Please add ETH to your wallet to cover transaction fees.';
if (requiredGasFee != null) {
final requiredEth = (requiredGasFee / BigInt.from(10).pow(18)).toStringAsFixed(8);
final balanceInfo = currentBalance != null
? ', Available: ${(currentBalance / BigInt.from(10).pow(18)).toStringAsFixed(8)} ETH'
: '';
return '$baseMessage Required: ~$requiredEth ETH$balanceInfo.$addEthMessage';
}
return '$baseMessage$addEthMessage';
}
@override
String toString() => exceptionMessage;
}

View file

@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'package:bip32/bip32.dart' as bip32;
import 'package:bip39/bip39.dart' as bip39;
@ -197,8 +198,8 @@ abstract class EVMChainWalletBase
for (var token in erc20Currencies) {
bool isPotentialScam = false;
bool isWhitelisted =
getDefaultTokenContractAddresses.any((element) => element == token.contractAddress);
bool isWhitelisted = getDefaultTokenContractAddresses
.any((element) => element.toLowerCase() == token.contractAddress.toLowerCase());
final tokenSymbol = token.title.toUpperCase();
@ -213,6 +214,16 @@ abstract class EVMChainWalletBase
token.iconPath = null;
await token.save();
}
// For fixing wrongly classified tokens
if (!isPotentialScam && token.isPotentialScam) {
token.isPotentialScam = false;
final iconPath = CryptoCurrency.all
.firstWhere((element) => element.title.toUpperCase() == token.symbol.toUpperCase())
.iconPath;
token.iconPath = iconPath;
await token.save();
}
}
}
@ -255,36 +266,72 @@ abstract class EVMChainWalletBase
required String? contractAddress,
required String receivingAddressHex,
required TransactionPriority priority,
Uint8List? data,
}) async {
try {
if (priority is EVMChainTransactionPriority) {
final priorityFee = EtherAmount.fromInt(EtherUnit.gwei, priority.tip).getInWei.toInt();
int maxFeePerGas;
int adjustedGasPrice;
bool isPolygon = _client.chainId == 137;
if (gasBaseFee != null) {
// MaxFeePerGas with EIP1559;
maxFeePerGas = gasBaseFee! + priorityFee;
} else {
// MaxFeePerGas with gasPrice
maxFeePerGas = gasPrice;
maxFeePerGas = gasPrice + priorityFee;
}
adjustedGasPrice = maxFeePerGas;
// Polygon has a minimum priority fee of 25 gwei
if (isPolygon) {
int minPriorityFee = 25;
int minPriorityFeeWei =
EtherAmount.fromInt(EtherUnit.gwei, minPriorityFee).getInWei.toInt();
// Calculate user selected priority-based additional fee on top of minimum
int additionalPriorityFee = 0;
switch (priority) {
case EVMChainTransactionPriority.slow:
// We use minimum priority fee only
additionalPriorityFee = 0;
break;
case EVMChainTransactionPriority.medium:
// We add 15 gwei on top of minimum
additionalPriorityFee = EtherAmount.fromInt(EtherUnit.gwei, 15).getInWei.toInt();
break;
case EVMChainTransactionPriority.fast:
// We add 35 gwei on top of minimum
additionalPriorityFee = EtherAmount.fromInt(EtherUnit.gwei, 35).getInWei.toInt();
break;
}
int totalPriorityFee = minPriorityFeeWei + additionalPriorityFee;
adjustedGasPrice = gasPrice + totalPriorityFee;
maxFeePerGas = gasPrice + totalPriorityFee;
}
final estimatedGas = await _client.getEstimatedGasUnitsForTransaction(
contractAddress: contractAddress,
senderAddress: _evmChainPrivateKey.address,
value: EtherAmount.fromBigInt(EtherUnit.wei, amount!),
gasPrice: EtherAmount.fromInt(EtherUnit.wei, gasPrice),
gasPrice: EtherAmount.fromInt(EtherUnit.wei, adjustedGasPrice),
toAddress: EthereumAddress.fromHex(receivingAddressHex),
maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
data: data,
);
final totalGasFee = estimatedGas * maxFeePerGas;
final totalGasFee = estimatedGas * adjustedGasPrice;
return GasParamsHandler(
estimatedGasUnits: estimatedGas,
estimatedGasFee: totalGasFee,
maxFeePerGas: maxFeePerGas,
gasPrice: gasPrice,
gasPrice: adjustedGasPrice,
);
}
return GasParamsHandler.zero();
@ -473,11 +520,46 @@ abstract class EVMChainWalletBase
contractAddress:
transactionCurrency is Erc20Token ? transactionCurrency.contractAddress : null,
data: hexOpReturnMemo,
gasPrice: maxFeePerGasForTransaction,
);
return pendingEVMChainTransaction;
}
Future<PendingTransaction> createApprovalTransaction(BigInt amount, String spender,
CryptoCurrency token, EVMChainTransactionPriority priority) async {
final CryptoCurrency transactionCurrency =
balance.keys.firstWhere((element) => element.title == token.title);
assert(transactionCurrency is Erc20Token);
final data = _client.getEncodedDataForApprovalTransaction(
contractAddress: EthereumAddress.fromHex((transactionCurrency as Erc20Token).contractAddress),
value: EtherAmount.fromBigInt(EtherUnit.wei, amount),
toAddress: EthereumAddress.fromHex(spender),
);
final gasFeesModel = await calculateActualEstimatedFeeForCreateTransaction(
amount: amount,
receivingAddressHex: spender,
priority: priority,
contractAddress: transactionCurrency.contractAddress,
data: data,
);
return _client.signApprovalTransaction(
privateKey: _evmChainPrivateKey,
spender: spender,
amount: amount,
priority: priority,
gasFee: BigInt.from(gasFeesModel.estimatedGasFee),
maxFeePerGas: gasFeesModel.maxFeePerGas,
estimatedGasUnits: gasFeesModel.estimatedGasUnits,
exponent: transactionCurrency.decimal,
contractAddress: transactionCurrency.contractAddress,
gasPrice: gasFeesModel.gasPrice,
);
}
Future<void> _updateTransactions() async {
try {
if (_isTransactionUpdating) {

View file

@ -11,6 +11,7 @@ class PendingEVMChainTransaction with PendingTransaction {
final BigInt fee;
final String amount;
final int exponent;
final bool isInfiniteApproval;
PendingEVMChainTransaction({
required this.sendTransaction,
@ -18,10 +19,12 @@ class PendingEVMChainTransaction with PendingTransaction {
required this.fee,
required this.amount,
required this.exponent,
this.isInfiniteApproval = false,
});
@override
String get amountFormatted {
if (isInfiniteApproval) return "";
final _amount = (BigInt.parse(amount) / BigInt.from(pow(10, exponent))).toString();
return _amount.substring(0, min(10, _amount.length));
}

View file

@ -30,6 +30,7 @@ dependencies:
git:
url: https://github.com/cake-tech/ledger-flutter-plus-plugins
path: packages/ledger-ethereum
ref: f4761cd5171d4c1e2e42fd3298261650539fb2db
dependency_overrides:
web3dart:
@ -41,9 +42,9 @@ dependency_overrides:
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.4.7
build_runner: ^2.4.15
mobx_codegen: ^2.0.7
hive_generator: ^1.1.3
hive_generator: ^2.0.1
flutter_lints: ^2.0.0
flutter:

View file

@ -5,13 +5,11 @@ class PendingTransactionDescription {
required this.fee,
required this.hash,
required this.hex,
required this.txKey,
required this.pointerAddress});
final int amount;
final int fee;
final String hash;
final String hex;
final String txKey;
final int pointerAddress;
}

View file

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:ffi';
import 'dart:isolate';
@ -194,14 +195,12 @@ Future<PendingTransactionDescription> createTransactionSync(
final rFee = pendingTx.fee();
final rHash = pendingTx.txid('');
final rHex = pendingTx.hex('');
final rTxKey = rHash;
return PendingTransactionDescription(
amount: rAmt,
fee: rFee,
hash: rHash,
hex: rHex,
txKey: rTxKey,
pointerAddress: pendingTx.ffiAddress(),
);
}
@ -246,7 +245,6 @@ Future<PendingTransactionDescription> createTransactionMultDest(
fee: tx.fee(),
hash: tx.txid(''),
hex: tx.hex(''),
txKey: tx.txid(''),
pointerAddress: tx.ffiAddress(),
);
}
@ -263,6 +261,7 @@ Future<String?> commitTransaction({required Wallet2PendingTransaction tx, requir
filename: '',
overwrite: false,
);
return null;
});
String? error = (() {
@ -285,11 +284,12 @@ Future<String?> commitTransaction({required Wallet2PendingTransaction tx, requir
if (error != null && error != "no tx keys found for this txid") {
throw CreationTransactionException(message: error);
}
if (useUR) {
return Future.value(txCommit as String?);
} else {
return Future.value(null);
}
unawaited(() async {
storeSync(force: true);
await Future.delayed(Duration(seconds: 5));
storeSync(force: true);
}());
return Future.value(txCommit);
}
class Transaction {

View file

@ -116,12 +116,17 @@ String getSeedLegacy(String? language) {
}
String getPassphrase() {
return currentWallet!.getCacheAttribute(key: "cakewallet.passphrase");
return currentWallet?.getCacheAttribute(key: "cakewallet.passphrase") ?? "";
}
Map<int, Map<int, Map<int, String>>> addressCache = {};
String getAddress({int accountIndex = 0, int addressIndex = 0}) {
// this is a workaround for when we switch the wallet pointer,
// it should never reach UI but should be good enough to prevent gray screen
// or other errors because of forced null check.
if (currentWallet == null) return "<wallet not ready ($accountIndex:$addressIndex)>";
// printV("getaddress: ${accountIndex}/${addressIndex}: ${monero.Wallet_numSubaddresses(wptr!, accountIndex: accountIndex)}: ${monero.Wallet_address(wptr!, accountIndex: accountIndex, addressIndex: addressIndex)}");
// this could be a while loop, but I'm in favor of making it if to not cause freezes
if (currentWallet!.numSubaddresses(accountIndex: accountIndex)-1 < addressIndex) {
@ -146,7 +151,25 @@ int getUnlockedBalance({int accountIndex = 0}) =>
int getCurrentHeight() => currentWallet?.blockChainHeight() ?? 0;
int getNodeHeightSync() => currentWallet?.daemonBlockChainHeight() ?? 0;
int cachedNodeHeight = 0;
bool isHeightRefreshing = false;
int getNodeHeightSync() {
if (isHeightRefreshing == false) {
(() async {
try {
isHeightRefreshing = true;
final wptrAddress = currentWallet!.ffiAddress();
cachedNodeHeight = await Isolate.run(() async {
return monero.Wallet_daemonBlockChainHeight(Pointer.fromAddress(wptrAddress));
});
} finally {
isHeightRefreshing = false;
}
})();
}
return cachedNodeHeight;
}
bool isConnectedSync() => currentWallet?.connected() != 0;
@ -202,7 +225,6 @@ Future<bool> setupNodeSync(
}
void startRefreshSync() {
currentWallet!.refreshAsync();
currentWallet!.startRefresh();
}
@ -228,7 +250,7 @@ void storeSync({bool force = false}) async {
return monero.Wallet_synchronized(Pointer.fromAddress(addr));
});
if (lastStorePointer == addr &&
lastStoreHeight + 5000 > currentWallet!.blockChainHeight() &&
lastStoreHeight + 75000 > currentWallet!.blockChainHeight() &&
!synchronized &&
!force) {
return;
@ -255,13 +277,13 @@ void closeCurrentWallet() {
currentWallet!.stop();
}
String getSecretViewKey() => currentWallet!.secretViewKey();
String getSecretViewKey() => currentWallet?.secretViewKey() ?? "";
String getPublicViewKey() => currentWallet!.publicViewKey();
String getPublicViewKey() => currentWallet?.publicViewKey() ?? "";
String getSecretSpendKey() => currentWallet!.secretSpendKey();
String getSecretSpendKey() => currentWallet?.secretSpendKey() ?? "";
String getPublicSpendKey() => currentWallet!.publicSpendKey();
String getPublicSpendKey() => currentWallet?.publicSpendKey() ?? "";
class SyncListener {
SyncListener(this.onNewBlock, this.onNewTransaction)

View file

@ -17,6 +17,7 @@ import 'package:cw_core/pending_transaction.dart';
import 'package:cw_core/sync_status.dart';
import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
@ -207,6 +208,14 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
@override
Future<void> connectToNode({required Node node}) async {
String socksProxy = node.socksProxyAddress ?? '';
printV("bootstrapped: ${CakeTor.instance.bootstrapped}");
printV(" enabled: ${CakeTor.instance.enabled}");
printV(" port: ${CakeTor.instance.port}");
printV(" started: ${CakeTor.instance.started}");
if (CakeTor.instance.enabled) {
socksProxy = "127.0.0.1:${CakeTor.instance.port}";
}
try {
syncStatus = ConnectingSyncStatus();
await monero_wallet.setupNodeSync(
@ -216,7 +225,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
useSSL: node.isSSL,
isLightWallet: false,
// FIXME: hardcoded value
socksProxyAddress: node.socksProxyAddress);
socksProxyAddress: socksProxy);
await monero_wallet.setTrustedDaemon(node.trusted);
syncStatus = ConnectedSyncStatus();

View file

@ -31,8 +31,6 @@ class PendingMoneroTransaction with PendingTransaction {
@override
String get hex => pendingTransactionDescription.hex;
String get txKey => pendingTransactionDescription.txKey;
@override
String get amountFormatted => AmountConverter.amountIntToString(
CryptoCurrency.xmr, pendingTransactionDescription.amount);

View file

@ -66,11 +66,11 @@ packages:
dependency: transitive
description:
path: "."
ref: cake-update-v2
resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
ref: cake-update-v4
resolved-ref: "437dadd0bd9bf73ec6a551299577799341f6486a"
url: "https://github.com/cake-tech/blockchain_utils"
source: git
version: "3.3.0"
version: "4.3.0"
bluez:
dependency: transitive
description:
@ -131,18 +131,18 @@ packages:
dependency: "direct dev"
description:
name: build_runner
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
url: "https://pub.dev"
source: hosted
version: "2.4.13"
version: "2.4.15"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41"
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
url: "https://pub.dev"
source: hosted
version: "7.2.10"
version: "8.0.0"
built_collection:
dependency: transitive
description:
@ -599,10 +599,10 @@ packages:
description:
path: "."
ref: cake-update-v2
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
url: "https://github.com/cake-tech/on_chain.git"
source: git
version: "3.7.0"
version: "6.2.0"
package_config:
dependency: transitive
description:
@ -779,11 +779,21 @@ packages:
socks5_proxy:
dependency: transitive
description:
name: socks5_proxy
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
url: "https://pub.dev"
source: hosted
version: "1.0.6"
path: "."
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
url: "https://github.com/LacticWhale/socks_dart"
source: git
version: "2.1.0"
socks_socket:
dependency: transitive
description:
path: "."
ref: e6232c53c1595469931ababa878759a067c02e94
resolved-ref: e6232c53c1595469931ababa878759a067c02e94
url: "https://github.com/sneurlax/socks_socket"
source: git
version: "1.1.1"
source_gen:
dependency: transitive
description:
@ -860,10 +870,19 @@ packages:
dependency: transitive
description:
name: timing
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
version: "1.0.1"
tor_binary:
dependency: transitive
description:
path: "."
ref: cb811c610871a9517d47134b87c2f590c15c96c5
resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
url: "https://github.com/MrCyjaneK/flutter-tor_binary"
source: git
version: "4.7.14"
tuple:
dependency: transitive
description:

View file

@ -34,8 +34,8 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.4.7
build_resolvers: ^2.0.9
build_runner: ^2.4.15
build_resolvers: ^2.4.4
mobx_codegen: ^2.0.7
mockito: ^5.4.5
hive_generator: ^2.0.1

View file

@ -3,11 +3,11 @@ import 'dart:convert';
import 'package:cw_core/nano_account_info_response.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_nano/nano_block_info_response.dart';
import 'package:cw_core/n2_node.dart';
import 'package:cw_nano/nano_balance.dart';
import 'package:cw_nano/nano_transaction_model.dart';
import 'package:http/http.dart' as http;
import 'package:cw_core/node.dart';
import 'package:nanoutil/nanoutil.dart';
import 'package:shared_preferences/shared_preferences.dart';
@ -66,8 +66,8 @@ class NanoClient {
}
Future<NanoBalance> getBalance(String address) async {
final response = await http.post(
_node!.uri,
final response = await ProxyWrapper().post(
clearnetUri: _node!.uri,
headers: getHeaders(_node!.uri.host),
body: jsonEncode(
{
@ -76,7 +76,8 @@ class NanoClient {
},
),
);
final data = await jsonDecode(response.body);
final data = jsonDecode(response.body) as Map<String, dynamic>;
if (response.statusCode != 200 ||
data["error"] != null ||
data["balance"] == null ||
@ -93,8 +94,8 @@ class NanoClient {
Future<AccountInfoResponse?> getAccountInfo(String address) async {
try {
final response = await http.post(
_node!.uri,
final response = await ProxyWrapper().post(
clearnetUri: _node!.uri,
headers: getHeaders(_node!.uri.host),
body: jsonEncode(
{
@ -104,8 +105,9 @@ class NanoClient {
},
),
);
final data = await jsonDecode(response.body);
return AccountInfoResponse.fromJson(data as Map<String, dynamic>);
final data = jsonDecode(response.body) as Map<String, dynamic>;
return AccountInfoResponse.fromJson(data);
} catch (e) {
printV("error while getting account info $e");
return null;
@ -114,8 +116,8 @@ class NanoClient {
Future<BlockContentsResponse?> getBlockContents(String block) async {
try {
final response = await http.post(
_node!.uri,
final response = await ProxyWrapper().post(
clearnetUri: _node!.uri,
headers: getHeaders(_node!.uri.host),
body: jsonEncode(
{
@ -125,7 +127,8 @@ class NanoClient {
},
),
);
final data = await jsonDecode(response.body);
final data = jsonDecode(response.body) as Map<String, dynamic>;
return BlockContentsResponse.fromJson(data["contents"] as Map<String, dynamic>);
} catch (e) {
printV("error while getting block info $e");
@ -181,8 +184,8 @@ class NanoClient {
}
Future<String> requestWork(String hash) async {
final response = await http.post(
_powNode!.uri,
final response = await ProxyWrapper().post(
clearnetUri: _powNode!.uri,
headers: getHeaders(_powNode!.uri.host),
body: json.encode(
{
@ -191,8 +194,9 @@ class NanoClient {
},
),
);
if (response.statusCode == 200) {
final Map<String, dynamic> decoded = json.decode(response.body) as Map<String, dynamic>;
final decoded = jsonDecode(response.body) as Map<String, dynamic>;
if (decoded.containsKey("error")) {
throw Exception("Received error ${decoded["error"]}");
}
@ -224,13 +228,13 @@ class NanoClient {
"block": block,
});
final processResponse = await http.post(
_node!.uri,
final processResponse = await ProxyWrapper().post(
clearnetUri: _node!.uri,
headers: getHeaders(_node!.uri.host),
body: processBody,
);
final Map<String, dynamic> decoded = json.decode(processResponse.body) as Map<String, dynamic>;
final Map<String, dynamic> decoded = jsonDecode(processResponse.body) as Map<String, dynamic>;
if (decoded.containsKey("error")) {
throw Exception("Received error ${decoded["error"]}");
}
@ -423,12 +427,11 @@ class NanoClient {
"subtype": "receive",
"block": receiveBlock,
});
final processResponse = await http.post(
_node!.uri,
final processResponse = await ProxyWrapper().post(
clearnetUri: _node!.uri,
headers: getHeaders(_node!.uri.host),
body: processBody,
);
final Map<String, dynamic> decoded = json.decode(processResponse.body) as Map<String, dynamic>;
if (decoded.containsKey("error")) {
throw Exception("Received error ${decoded["error"]}");
@ -440,16 +443,17 @@ class NanoClient {
required String destinationAddress,
required String privateKey,
}) async {
final receivableResponse = await http.post(_node!.uri,
final receivableResponse = await ProxyWrapper().post(
clearnetUri: _node!.uri,
headers: getHeaders(_node!.uri.host),
body: jsonEncode({
"action": "receivable",
"account": destinationAddress,
"count": "-1",
"source": true,
}));
final receivableData = await jsonDecode(receivableResponse.body);
}),
);
final receivableData = jsonDecode(receivableResponse.body) as Map<String, dynamic>;
if (receivableData["blocks"] == "" || receivableData["blocks"] == null) {
return 0;
}
@ -492,15 +496,18 @@ class NanoClient {
Future<List<NanoTransactionModel>> fetchTransactions(String address) async {
try {
final response = await http.post(_node!.uri,
final response = await ProxyWrapper().post(
clearnetUri: _node!.uri,
headers: getHeaders(_node!.uri.host),
body: jsonEncode({
"action": "account_history",
"account": address,
"count": "100",
// "raw": true,
}));
final data = await jsonDecode(response.body);
}),
);
final data = jsonDecode(response.body) as Map<String, dynamic>;
final transactions = data["history"] is List ? data["history"] as List<dynamic> : [];
// Map the transactions list to NanoTransactionModel using the factory
@ -516,13 +523,14 @@ class NanoClient {
Future<List<N2Node>> getN2Reps() async {
final uri = Uri.parse(N2_REPS_ENDPOINT);
final response = await http.post(
uri,
final response = await ProxyWrapper().post(
clearnetUri: uri,
headers: getHeaders(uri.host),
body: jsonEncode({"action": "reps"}),
);
try {
final List<N2Node> nodes = (json.decode(response.body) as List<dynamic>)
final List<N2Node> nodes = (jsonDecode(response.body) as List<dynamic>)
.map((dynamic e) => N2Node.fromJson(e as Map<String, dynamic>))
.toList();
return nodes;
@ -533,8 +541,8 @@ class NanoClient {
Future<int> getRepScore(String rep) async {
final uri = Uri.parse(N2_REPS_ENDPOINT);
final response = await http.post(
uri,
final response = await ProxyWrapper().post(
clearnetUri: uri,
headers: getHeaders(uri.host),
body: jsonEncode({
"action": "rep_info",
@ -542,7 +550,8 @@ class NanoClient {
}),
);
try {
final N2Node node = N2Node.fromJson(json.decode(response.body) as Map<String, dynamic>);
final N2Node node = N2Node.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
return node.score ?? 100;
} catch (error) {
return 100;

View file

@ -5,34 +5,39 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8"
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
url: "https://pub.dev"
source: hosted
version: "47.0.0"
version: "76.0.0"
_macros:
dependency: transitive
description: dart
source: sdk
version: "0.3.3"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80"
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
url: "https://pub.dev"
source: hosted
version: "4.7.0"
version: "6.11.0"
args:
dependency: transitive
description:
name: args
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
url: "https://pub.dev"
source: hosted
version: "2.6.0"
version: "2.7.0"
asn1lib:
dependency: transitive
description:
name: asn1lib
sha256: "4bae5ae63e6d6dd17c4aac8086f3dec26c0236f6a0f03416c6c19d830c367cf5"
sha256: "1c296cd268f486cabcc3930e9b93a8133169305f18d722916e675959a88f6d2c"
url: "https://pub.dev"
source: hosted
version: "1.5.8"
version: "1.5.9"
async:
dependency: transitive
description:
@ -86,50 +91,50 @@ packages:
dependency: transitive
description:
name: build
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0
url: "https://pub.dev"
source: hosted
version: "2.4.1"
version: "2.4.2"
build_config:
dependency: transitive
description:
name: build_config
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
version: "1.1.2"
build_daemon:
dependency: transitive
description:
name: build_daemon
sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9"
sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
version: "4.0.4"
build_resolvers:
dependency: transitive
description:
name: build_resolvers
sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6"
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
url: "https://pub.dev"
source: hosted
version: "2.0.10"
version: "2.4.4"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
url: "https://pub.dev"
source: hosted
version: "2.4.13"
version: "2.4.15"
build_runner_core:
dependency: "direct overridden"
dependency: transitive
description:
name: build_runner_core
sha256: "0671ad4162ed510b70d0eb4ad6354c249f8429cab4ae7a4cec86bbc2886eb76e"
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
url: "https://pub.dev"
source: hosted
version: "7.2.7+1"
version: "8.0.0"
built_collection:
dependency: transitive
description:
@ -142,10 +147,10 @@ packages:
dependency: transitive
description:
name: built_value
sha256: "8b158ab94ec6913e480dc3f752418348b5ae099eb75868b5f4775f0572999c61"
sha256: "082001b5c3dc495d4a42f1d5789990505df20d8547d42507c29050af6933ee27"
url: "https://pub.dev"
source: hosted
version: "8.9.4"
version: "8.10.1"
cake_backup:
dependency: transitive
description:
@ -238,10 +243,10 @@ packages:
dependency: transitive
description:
name: dart_style
sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4"
sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
url: "https://pub.dev"
source: hosted
version: "2.2.4"
version: "2.3.8"
decimal:
dependency: "direct main"
description:
@ -373,18 +378,18 @@ packages:
dependency: "direct dev"
description:
name: hive_generator
sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938"
sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4"
url: "https://pub.dev"
source: hosted
version: "1.1.3"
version: "2.0.1"
http:
dependency: "direct main"
description:
name: http
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
url: "https://pub.dev"
source: hosted
version: "1.3.0"
version: "1.4.0"
http_multi_server:
dependency: transitive
description:
@ -397,10 +402,10 @@ packages:
dependency: transitive
description:
name: http_parser
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
version: "4.1.2"
intl:
dependency: transitive
description:
@ -473,6 +478,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
macros:
dependency: transitive
description:
name: macros
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
url: "https://pub.dev"
source: hosted
version: "0.1.3-main.0"
matcher:
dependency: transitive
description:
@ -517,10 +530,10 @@ packages:
dependency: "direct dev"
description:
name: mobx_codegen
sha256: d4beb9cea4b7b014321235f8fdc7c2193ee0fe1d1198e9da7403f8bc85c4407c
sha256: e0abbbc651a69550440f6b65c99ec222a1e2a4afd7baec8ba0f3088c7ca582a8
url: "https://pub.dev"
source: hosted
version: "2.3.0"
version: "2.7.1"
nanodart:
dependency: transitive
description:
@ -551,7 +564,7 @@ packages:
description:
path: "."
ref: cake-update-v2
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
resolved-ref: "01cbbacbb05d2113aafa8b7c4a2bb766f749d8d8"
url: "https://github.com/cake-tech/on_chain.git"
source: git
version: "3.7.0"
@ -559,10 +572,10 @@ packages:
dependency: transitive
description:
name: package_config
sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67"
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.2.0"
path:
dependency: transitive
description:
@ -583,10 +596,10 @@ packages:
dependency: transitive
description:
name: path_provider_android
sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2"
sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9
url: "https://pub.dev"
source: hosted
version: "2.2.15"
version: "2.2.17"
path_provider_foundation:
dependency: transitive
description:
@ -663,26 +676,26 @@ packages:
dependency: transitive
description:
name: provider
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84"
url: "https://pub.dev"
source: hosted
version: "6.1.2"
version: "6.1.5"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd"
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
url: "https://pub.dev"
source: hosted
version: "2.1.5"
version: "2.2.0"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
sha256: "81876843eb50dc2e1e5b151792c9a985c5ed2536914115ed04e9c8528f6647b0"
sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
url: "https://pub.dev"
source: hosted
version: "1.4.0"
version: "1.5.0"
rational:
dependency: transitive
description:
@ -695,18 +708,18 @@ packages:
dependency: "direct main"
description:
name: shared_preferences
sha256: "846849e3e9b68f3ef4b60c60cf4b3e02e9321bc7f4d8c4692cf87ffa82fc8a3a"
sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5"
url: "https://pub.dev"
source: hosted
version: "2.5.2"
version: "2.5.3"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: a768fc8ede5f0c8e6150476e14f38e2417c0864ca36bb4582be8e21925a03c22
sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac"
url: "https://pub.dev"
source: hosted
version: "2.4.6"
version: "2.4.10"
shared_preferences_foundation:
dependency: transitive
description:
@ -751,18 +764,18 @@ packages:
dependency: transitive
description:
name: shelf
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
url: "https://pub.dev"
source: hosted
version: "1.4.1"
version: "1.4.2"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "3.0.0"
sky_engine:
dependency: transitive
description: flutter
@ -771,27 +784,37 @@ packages:
socks5_proxy:
dependency: transitive
description:
name: socks5_proxy
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
url: "https://pub.dev"
source: hosted
version: "1.0.6"
path: "."
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
url: "https://github.com/LacticWhale/socks_dart"
source: git
version: "2.1.0"
socks_socket:
dependency: transitive
description:
path: "."
ref: e6232c53c1595469931ababa878759a067c02e94
resolved-ref: e6232c53c1595469931ababa878759a067c02e94
url: "https://github.com/sneurlax/socks_socket"
source: git
version: "1.1.1"
source_gen:
dependency: transitive
description:
name: source_gen
sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d"
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
url: "https://pub.dev"
source: hosted
version: "1.2.6"
version: "1.5.0"
source_helper:
dependency: transitive
description:
name: source_helper
sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f"
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
url: "https://pub.dev"
source: hosted
version: "1.3.3"
version: "1.3.5"
source_span:
dependency: transitive
description:
@ -852,10 +875,19 @@ packages:
dependency: transitive
description:
name: timing
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
version: "1.0.1"
tor_binary:
dependency: transitive
description:
path: "."
ref: cb811c610871a9517d47134b87c2f590c15c96c5
resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
url: "https://github.com/MrCyjaneK/flutter-tor_binary"
source: git
version: "4.7.14"
tuple:
dependency: transitive
description:
@ -916,18 +948,18 @@ packages:
dependency: transitive
description:
name: web_socket
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
url: "https://pub.dev"
source: hosted
version: "0.1.6"
version: "1.0.1"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5"
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
url: "https://pub.dev"
source: hosted
version: "3.0.2"
version: "3.0.3"
xdg_directories:
dependency: transitive
description:
@ -945,5 +977,5 @@ packages:
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.5.0 <4.0.0"
flutter: ">=3.24.0"
dart: ">=3.6.0 <4.0.0"
flutter: ">=3.27.0"

View file

@ -31,13 +31,12 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.4.7
build_runner: ^2.4.15
mobx_codegen: ^2.0.7
hive_generator: ^1.1.3
hive_generator: ^2.0.1
dependency_overrides:
watcher: ^1.1.0
build_runner_core: 7.2.7+1
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

View file

@ -84,6 +84,6 @@ class DefaultPolygonErc20Tokens {
.iconPath;
} catch (_) {}
return Erc20Token.copyWith(token, iconPath, 'POLY');
return Erc20Token.copyWith(token, iconPath, 'POL');
}).toList();
}

View file

@ -18,13 +18,20 @@ class PolygonClient extends EVMChainClient {
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,
data: data,
maxGas: maxGas,
// gasPrice: gasPrice,
gasPrice: finalGasPrice,
// maxFeePerGas: maxFeePerGas,
// maxPriorityFeePerGas: maxPriorityFeePerGas,
);
@ -40,7 +47,7 @@ class PolygonClient extends EVMChainClient {
Future<List<EVMChainTransactionModel>> fetchTransactions(String address,
{String? contractAddress}) async {
try {
final response = await httpClient.get(Uri.https("api.etherscan.io", "/v2/api", {
final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
"chainid": "$chainId",
"module": "account",
"action": contractAddress != null ? "tokentx" : "txlist",
@ -68,7 +75,7 @@ class PolygonClient extends EVMChainClient {
@override
Future<List<EVMChainTransactionModel>> fetchInternalTransactions(String address) async {
try {
final response = await httpClient.get(Uri.https("api.etherscan.io", "/v2/api", {
final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
"chainid": "$chainId",
"module": "account",
"action": "txlistinternal",

View file

@ -45,7 +45,12 @@ class PolygonWallet extends EVMChainWallet {
final initialErc20Tokens = DefaultPolygonErc20Tokens().initialPolygonErc20Tokens;
for (final token in initialErc20Tokens) {
if (!evmChainErc20TokensBox.containsKey(token.contractAddress)) {
if (evmChainErc20TokensBox.containsKey(token.contractAddress)) {
final existingToken = evmChainErc20TokensBox.get(token.contractAddress);
if (existingToken?.tag != token.tag) {
evmChainErc20TokensBox.put(token.contractAddress, token);
}
} else {
if (isMigration) token.enabled = false;
evmChainErc20TokensBox.put(token.contractAddress, token);
}

View file

@ -34,7 +34,7 @@ dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
build_runner: ^2.4.7
build_runner: ^2.4.15
# For information on the generic Dart part of this file, see the

View file

@ -2,8 +2,10 @@ import 'dart:async';
import 'dart:convert';
import 'dart:math' as math;
import 'package:blockchain_utils/blockchain_utils.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/node.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_core/solana_rpc_http_service.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_solana/pending_solana_transaction.dart';
@ -11,14 +13,17 @@ import 'package:cw_solana/solana_balance.dart';
import 'package:cw_solana/solana_exceptions.dart';
import 'package:cw_solana/solana_transaction_model.dart';
import 'package:cw_solana/spl_token.dart';
import 'package:http/http.dart' as http;
import 'package:on_chain/solana/solana.dart';
import 'package:on_chain/solana/src/instructions/associated_token_account/constant.dart';
import 'package:on_chain/solana/src/models/pda/pda.dart';
import 'package:blockchain_utils/blockchain_utils.dart';
import 'package:on_chain/solana/src/rpc/models/models/confirmed_transaction_meta.dart';
import '.secrets.g.dart' as secrets;
class SolanaWalletClient {
final httpClient = http.Client();
// Minimum amount in SOL to consider a transaction valid (to filter spam)
static const double minValidAmount = 0.00000003;
final httpClient = ProxyWrapper().getHttpClient();
SolanaRPC? _provider;
bool connect(Node node) {
@ -82,7 +87,8 @@ class SolanaWalletClient {
}
}
Future<SolanaBalance?> getSplTokenBalance(String mintAddress, String walletAddress) async {
Future<SolanaBalance?> getSplTokenBalance(
String mintAddress, String walletAddress) async {
// Fetch the token accounts (a token can have multiple accounts for various uses)
final tokenAccounts = await getSPLTokenAccounts(mintAddress, walletAddress);
@ -118,14 +124,16 @@ class SolanaWalletClient {
),
);
final fee = (feeForMessage?.toDouble() ?? 0.0) / SolanaUtils.lamportsPerSol;
final fee =
(feeForMessage?.toDouble() ?? 0.0) / SolanaUtils.lamportsPerSol;
return fee;
} catch (_) {
return 0.0;
}
}
Future<double> getEstimatedFee(SolanaPublicKey publicKey, Commitment commitment) async {
Future<double> getEstimatedFee(
SolanaPublicKey publicKey, Commitment commitment) async {
final message = await _getMessageForNativeTransaction(
publicKey: publicKey,
destinationAddress: publicKey.toAddress().address,
@ -155,105 +163,170 @@ class SolanaWalletClient {
if (meta == null || transaction == null) return null;
final int fee = meta.fee;
final feeInSol = fee / SolanaUtils.lamportsPerSol;
final message = transaction.message;
final instructions = message.compiledInstructions;
String sender = "";
String receiver = "";
String signature = (txResponse.transaction?.signatures.isEmpty ?? true)
? ""
: Base58Encoder.encode(txResponse.transaction!.signatures.first);
for (final instruction in instructions) {
final programId = message.accountKeys[instruction.programIdIndex];
if (programId == SystemProgramConst.programId) {
if (programId == SystemProgramConst.programId ||
programId == ComputeBudgetConst.programId) {
// For native solana transactions
if (txResponse.version == TransactionType.legacy) {
// For legacy transfers, the fee payer (index 0) is the sender.
sender = message.accountKeys[0].address;
final senderPreBalance = meta.preBalances[0];
final senderPostBalance = meta.postBalances[0];
final feeForTx = fee / SolanaUtils.lamportsPerSol;
// The loss on the sender's account would include both the transfer amount and the fee.
// So we would subtract the fee to calculate the actual amount that was transferred (in lamports).
final transferLamports = (senderPreBalance - senderPostBalance) - BigInt.from(fee);
// Next, we attempt to find the receiver by comparing the balance changes.
// (The index 0 is for the sender so we skip it.)
bool foundReceiver = false;
for (int i = 1; i < meta.preBalances.length; i++) {
// The increase in balance on the receiver account should correspond to the transfer amount we calculated earlieer.
final pre = meta.preBalances[i];
final post = meta.postBalances[i];
if ((post - pre) == transferLamports) {
receiver = message.accountKeys[i].address;
foundReceiver = true;
break;
}
}
if (!foundReceiver) {
// Optionally (and rarely), if no account shows the exact expected change,
// we set the receiver address to unknown.
receiver = "unknown";
}
final amount = transferLamports / BigInt.from(1e9);
return SolanaTransactionModel(
isOutgoingTx: sender == walletAddress,
from: sender,
to: receiver,
id: signature,
amount: amount.abs(),
programId: SystemProgramConst.programId.address,
tokenSymbol: 'SOL',
blockTimeInInt: blockTime?.toInt() ?? 0,
fee: feeForTx,
);
} else {
if (instruction.accounts.length < 2) continue;
final senderIndex = instruction.accounts[0];
final receiverIndex = instruction.accounts[1];
sender = message.accountKeys[senderIndex].address;
receiver = message.accountKeys[receiverIndex].address;
// Get the fee payer index based on transaction type
// For legacy transfers, the first account is usually the fee payer
// For versioned, the first account in instruction is usually the fee payer
final feePayerIndex =
txResponse.version == TransactionType.legacy ? 0 : instruction.accounts[0];
final feeForTx = fee / SolanaUtils.lamportsPerSol;
final preBalances = meta.preBalances;
final postBalances = meta.postBalances;
final amountInString =
(((preBalances[senderIndex] - postBalances[senderIndex]) / BigInt.from(1e9))
.toDouble() -
feeForTx)
.toStringAsFixed(6);
final amount = double.parse(amountInString);
return SolanaTransactionModel(
isOutgoingTx: sender == walletAddress,
from: sender,
to: receiver,
id: signature,
amount: amount.abs(),
programId: SystemProgramConst.programId.address,
tokenSymbol: 'SOL',
blockTimeInInt: blockTime?.toInt() ?? 0,
fee: feeForTx,
final transactionModel = await _parseNativeTransaction(
message: message,
meta: meta,
fee: fee,
feeInSol: feeInSol,
feePayerIndex: feePayerIndex,
walletAddress: walletAddress,
signature: signature,
blockTime: blockTime,
);
if (transactionModel != null) {
return transactionModel;
}
} else if (programId == SPLTokenProgramConst.tokenProgramId) {
// For SPL Token transactions
if (instruction.accounts.length < 2) continue;
final transactionModel = await _parseSPLTokenTransaction(
message: message,
meta: meta,
fee: fee,
feeInSol: feeInSol,
instruction: instruction,
walletAddress: walletAddress,
signature: signature,
blockTime: blockTime,
splTokenSymbol: splTokenSymbol,
);
if (transactionModel != null) {
return transactionModel;
}
} else if (programId == AssociatedTokenAccountProgramConst.associatedTokenProgramId) {
// For ATA program, we need to check if this is a create account transaction
// or if it's part of a normal token transfer
// We skip this transaction if this is the only instruction (this means that it's a create account transaction)
if (instructions.length == 1) {
return null;
}
// We look for a token transfer instruction in the same transaction
bool hasTokenTransfer = false;
for (final otherInstruction in instructions) {
final otherProgramId = message.accountKeys[otherInstruction.programIdIndex];
if (otherProgramId == SPLTokenProgramConst.tokenProgramId) {
hasTokenTransfer = true;
break;
}
}
// If there's no token transfer instruction, it means this is just an ATA creation transaction
if (!hasTokenTransfer) {
return null;
}
continue;
} else {
return null;
}
}
} catch (e, s) {
printV("Error parsing transaction: $e\n$s");
}
return null;
}
Future<SolanaTransactionModel?> _parseNativeTransaction({
required VersionedMessage message,
required ConfirmedTransactionMeta meta,
required int fee,
required double feeInSol,
required int feePayerIndex,
required String walletAddress,
required String signature,
required BigInt? blockTime,
}) async {
// Calculate total balance changes across all accounts
BigInt totalBalanceChange = BigInt.zero;
String? sender;
String? receiver;
for (int i = 0; i < meta.preBalances.length; i++) {
final preBalance = meta.preBalances[i];
final postBalance = meta.postBalances[i];
final balanceChange = preBalance - postBalance;
if (balanceChange > BigInt.zero) {
// This account sent funds
sender = message.accountKeys[i].address;
totalBalanceChange += balanceChange;
} else if (balanceChange < BigInt.zero) {
// This account received funds
receiver = message.accountKeys[i].address;
}
}
// We subtract the fee from total balance change if the fee payer is the sender
if (sender == message.accountKeys[feePayerIndex].address) {
totalBalanceChange -= BigInt.from(fee);
}
if (sender == null || receiver == null) {
return null;
}
final amount = totalBalanceChange / BigInt.from(1e9);
final amountInSol = amount.abs().toDouble();
// Skip transactions with very small amounts (likely spam)
if (amountInSol < minValidAmount) {
return null;
}
return SolanaTransactionModel(
isOutgoingTx: sender == walletAddress,
from: sender,
to: receiver,
id: signature,
amount: amountInSol,
programId: SystemProgramConst.programId.address,
tokenSymbol: 'SOL',
blockTimeInInt: blockTime?.toInt() ?? 0,
fee: feeInSol,
);
}
Future<SolanaTransactionModel?> _parseSPLTokenTransaction({
required VersionedMessage message,
required ConfirmedTransactionMeta meta,
required int fee,
required double feeInSol,
required CompiledInstruction instruction,
required String walletAddress,
required String signature,
required BigInt? blockTime,
String? splTokenSymbol,
}) async {
final preBalances = meta.preTokenBalances;
final postBalances = meta.postTokenBalances;
@ -317,17 +390,8 @@ class SolanaWalletClient {
programId: SPLTokenProgramConst.tokenProgramId.address,
blockTimeInInt: blockTime?.toInt() ?? 0,
tokenSymbol: tokenSymbol ?? '',
fee: fee / SolanaUtils.lamportsPerSol,
fee: feeInSol,
);
} else {
return null;
}
}
} catch (e, s) {
printV("Error parsing transaction: $e\n$s");
}
return null;
}
/// Load the Address's transactions into the account
@ -369,9 +433,11 @@ class SolanaWalletClient {
}
}));
final versionedBatchResponses = batchResponses.whereType<VersionedTransactionResponse>();
final versionedBatchResponses =
batchResponses.whereType<VersionedTransactionResponse>();
final parsedTransactionsFutures = versionedBatchResponses.map((tx) => parseTransaction(
final parsedTransactionsFutures =
versionedBatchResponses.map((tx) => parseTransaction(
txResponse: tx,
splTokenSymbol: splTokenSymbol,
walletAddress: walletAddress?.address ?? address.address,
@ -379,13 +445,16 @@ class SolanaWalletClient {
final parsedTransactions = await Future.wait(parsedTransactionsFutures);
transactions.addAll(parsedTransactions.whereType<SolanaTransactionModel>().toList());
transactions.addAll(
parsedTransactions.whereType<SolanaTransactionModel>().toList());
// Calling the callback after each batch is processed, therefore passing the current list of transactions.
// Only update UI if we have new valid transactions
if (parsedTransactions.isNotEmpty) {
onUpdate(List<SolanaTransactionModel>.from(transactions));
}
if (i + batchSize < signatures.length) {
await Future.delayed(const Duration(milliseconds: 500));
await Future.delayed(const Duration(milliseconds: 300));
}
}
@ -446,8 +515,8 @@ class SolanaWalletClient {
}
Future<SPLToken?> fetchSPLTokenInfo(String mintAddress) async {
final programAddress =
MetaplexTokenMetaDataProgramUtils.findMetadataPda(mint: SolAddress(mintAddress));
final programAddress = MetaplexTokenMetaDataProgramUtils.findMetadataPda(
mint: SolAddress(mintAddress));
final token = await _provider!.request(
SolanaRPCGetMetadataAccount(
@ -468,8 +537,9 @@ class SolanaWalletClient {
// iconPath = await _client.getIconImageFromTokenUri(metadata.uri);
// } catch (_) {}
String filteredTokenSymbol =
metadata.symbol.replaceFirst(RegExp('^\\\$'), '').replaceAll('\u0000', '');
String filteredTokenSymbol = metadata.symbol
.replaceFirst(RegExp('^\\\$'), '')
.replaceAll('\u0000', '');
return SPLToken.fromMetadata(
name: metadata.name,
@ -585,7 +655,8 @@ class SolanaWalletClient {
return message;
}
Future<double> _getFeeFromCompiledMessage(Message message, Commitment commitment) async {
Future<double> _getFeeFromCompiledMessage(
Message message, Commitment commitment) async {
final base64Message = base64Encode(message.serialize());
final fee = await getFeeForMessage(base64Message, commitment);
@ -724,7 +795,8 @@ class SolanaWalletClient {
required SolAddress mintAddress,
required bool shouldCreateATA,
}) async {
final associatedTokenAccount = AssociatedTokenAccountProgramUtils.associatedTokenAccount(
final associatedTokenAccount =
AssociatedTokenAccountProgramUtils.associatedTokenAccount(
mint: mintAddress,
owner: ownerAddress,
);
@ -732,19 +804,24 @@ class SolanaWalletClient {
SolanaAccountInfo? accountInfo;
try {
accountInfo = await _provider!.request(
SolanaRPCGetAccountInfo(account: associatedTokenAccount.address),
SolanaRPCGetAccountInfo(
account: associatedTokenAccount.address,
commitment: Commitment.confirmed,
),
);
} catch (e) {
accountInfo = null;
}
// If aacountInfo is null, signifies that the associatedTokenAccount has only been created locally and not been broadcasted to the blockchain.
// If account exists, we return the associated token account
if (accountInfo != null) return associatedTokenAccount;
if (!shouldCreateATA) return null;
final payerAddress = payerPrivateKey.publicKey().toAddress();
final createAssociatedTokenAccount = AssociatedTokenAccountProgram.associatedTokenAccount(
payer: payerPrivateKey.publicKey().toAddress(),
payer: payerAddress,
associatedToken: associatedTokenAccount.address,
owner: ownerAddress,
mint: mintAddress,
@ -753,19 +830,23 @@ class SolanaWalletClient {
final blockhash = await _getLatestBlockhash(Commitment.confirmed);
final transaction = SolanaTransaction(
payerKey: payerPrivateKey.publicKey().toAddress(),
payerKey: payerAddress,
instructions: [createAssociatedTokenAccount],
recentBlockhash: blockhash,
type: TransactionType.v0,
);
transaction.sign([payerPrivateKey]);
final serializedTransaction = await _signTransactionInternal(
ownerPrivateKey: payerPrivateKey,
transaction: transaction,
);
await sendTransaction(
serializedTransaction: transaction.serializeString(),
serializedTransaction: serializedTransaction,
commitment: Commitment.confirmed,
);
// Delay for propagation on the blockchain for newly created associated token addresses
// Wait for confirmation
await Future.delayed(const Duration(seconds: 2));
return associatedTokenAccount;
@ -786,7 +867,8 @@ class SolanaWalletClient {
final amount = (inputAmount * math.pow(10, tokenDecimals)).toInt();
ProgramDerivedAddress? associatedSenderAccount;
try {
associatedSenderAccount = AssociatedTokenAccountProgramUtils.associatedTokenAccount(
associatedSenderAccount =
AssociatedTokenAccountProgramUtils.associatedTokenAccount(
mint: mintAddress,
owner: ownerPrivateKey.publicKey().toAddress(),
);
@ -921,7 +1003,8 @@ class SolanaWalletClient {
if (uri.isEmpty || uri == '') return null;
try {
final response = await httpClient.get(Uri.parse(uri));
final client = ProxyWrapper().getHttpIOClient();
final response = await client.get(Uri.parse(uri));
final jsonResponse = json.decode(response.body) as Map<String, dynamic>;

View file

@ -33,9 +33,9 @@ dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
build_runner: ^2.4.7
build_runner: ^2.4.15
mobx_codegen: ^2.0.7
hive_generator: ^1.1.3
hive_generator: ^2.0.1
dependency_overrides:
watcher: ^1.1.0

View file

@ -5,6 +5,7 @@ import 'dart:developer';
import 'package:blockchain_utils/blockchain_utils.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/node.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_tron/pending_tron_transaction.dart';
import 'package:cw_tron/tron_abi.dart';
import 'package:cw_tron/tron_balance.dart';
@ -13,12 +14,12 @@ import 'package:cw_tron/tron_token.dart';
import 'package:cw_tron/tron_transaction_model.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart';
import '.secrets.g.dart' as secrets;
import 'package:on_chain/on_chain.dart';
class TronClient {
final httpClient = Client();
late final client = ProxyWrapper().getHttpIOClient();
TronProvider? _provider;
// This is an internal tracker, so we don't have to "refetch".
int _nativeTxEstimatedFee = 0;
@ -28,7 +29,7 @@ class TronClient {
Future<List<TronTransactionModel>> fetchTransactions(String address,
{String? contractAddress}) async {
try {
final response = await httpClient.get(
final response = await client.get(
Uri.https(
"api.trongrid.io",
"/v1/accounts/$address/transactions",
@ -61,7 +62,7 @@ class TronClient {
Future<List<TronTRC20TransactionModel>> fetchTrc20ExcludedTransactions(String address) async {
try {
final response = await httpClient.get(
final response = await client.get(
Uri.https(
"api.trongrid.io",
"/v1/accounts/$address/transactions/trc20",

View file

@ -1,22 +1,22 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:on_chain/tron/tron.dart';
import '.secrets.g.dart' as secrets;
class TronHTTPProvider implements TronServiceProvider {
TronHTTPProvider(
{required this.url,
http.Client? client,
this.defaultRequestTimeout = const Duration(seconds: 30)})
: client = client ?? http.Client();
this.defaultRequestTimeout = const Duration(seconds: 30)});
@override
final String url;
final http.Client client;
late final client = ProxyWrapper().getHttpIOClient();
final Duration defaultRequestTimeout;
@override
Future<Map<String, dynamic>> get(TronRequestDetails params, [Duration? timeout]) async {
Future<Map<String, dynamic>> get(TronRequestDetails params,
[Duration? timeout]) async {
final response = await client.get(Uri.parse(params.url(url)), headers: {
'Content-Type': 'application/json',
if (url.contains("trongrid")) 'TRON-PRO-API-KEY': secrets.tronGridApiKey,
@ -27,13 +27,16 @@ class TronHTTPProvider implements TronServiceProvider {
}
@override
Future<Map<String, dynamic>> post(TronRequestDetails params, [Duration? timeout]) async {
Future<Map<String, dynamic>> post(TronRequestDetails params,
[Duration? timeout]) async {
final response = await client
.post(Uri.parse(params.url(url)),
headers: {
'Content-Type': 'application/json',
if (url.contains("trongrid")) 'TRON-PRO-API-KEY': secrets.tronGridApiKey,
if (url.contains("nownodes")) 'api-key': secrets.tronNowNodesApiKey,
if (url.contains("trongrid"))
'TRON-PRO-API-KEY': secrets.tronGridApiKey,
if (url.contains("nownodes"))
'api-key': secrets.tronNowNodesApiKey,
},
body: params.toRequestBody())
.timeout(timeout ?? defaultRequestTimeout);

View file

@ -31,9 +31,9 @@ dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
build_runner: ^2.3.3
build_runner: ^2.4.15
mobx_codegen: ^2.1.1
hive_generator: ^1.1.3
hive_generator: ^2.0.1
flutter:
# assets:
# - images/a_dot_burr.jpeg

View file

@ -110,7 +110,16 @@ int getUnlockedBalance({int accountIndex = 0}) =>
int getCurrentHeight() => wownero.Wallet_blockChainHeight(wptr!);
int getNodeHeightSync() => wownero.Wallet_daemonBlockChainHeight(wptr!);
int cachedNodeHeight = 0;
int getNodeHeightSync() {
(() async {
final wptrAddress = wptr!.address;
cachedNodeHeight = await Isolate.run(() async {
return wownero.Wallet_daemonBlockChainHeight(Pointer.fromAddress(wptrAddress));
});
})();
return cachedNodeHeight;
}
bool isConnectedSync() => wownero.Wallet_connected(wptr!) != 0;
@ -154,7 +163,7 @@ Future<bool> setupNodeSync(
}
void startRefreshSync() {
wownero.Wallet_refreshAsync(wptr!);
// wownero.Wallet_refreshAsync(wptr!);
wownero.Wallet_startRefresh(wptr!);
}

View file

@ -16,6 +16,7 @@ import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wownero_amount_format.dart';
@ -183,6 +184,14 @@ abstract class WowneroWalletBase
@override
Future<void> connectToNode({required Node node}) async {
String socksProxy = node.socksProxyAddress ?? '';
printV("bootstrapped: ${CakeTor.instance.bootstrapped}");
printV(" enabled: ${CakeTor.instance.enabled}");
printV(" port: ${CakeTor.instance.port}");
printV(" started: ${CakeTor.instance.started}");
if (CakeTor.instance.enabled) {
socksProxy = "127.0.0.1:${CakeTor.instance.port}";
}
try {
syncStatus = ConnectingSyncStatus();
await wownero_wallet.setupNode(
@ -192,7 +201,7 @@ abstract class WowneroWalletBase
useSSL: node.isSSL,
isLightWallet: false,
// FIXME: hardcoded value
socksProxyAddress: node.socksProxyAddress);
socksProxyAddress: socksProxy);
wownero_wallet.setTrustedDaemon(node.trusted);
syncStatus = ConnectedSyncStatus();

View file

@ -5,18 +5,23 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8"
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
url: "https://pub.dev"
source: hosted
version: "47.0.0"
version: "76.0.0"
_macros:
dependency: transitive
description: dart
source: sdk
version: "0.3.3"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80"
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
url: "https://pub.dev"
source: hosted
version: "4.7.0"
version: "6.11.0"
args:
dependency: transitive
description:
@ -86,26 +91,26 @@ packages:
dependency: "direct dev"
description:
name: build_resolvers
sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6"
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
url: "https://pub.dev"
source: hosted
version: "2.0.10"
version: "2.4.4"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
url: "https://pub.dev"
source: hosted
version: "2.4.13"
version: "2.4.15"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41"
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
url: "https://pub.dev"
source: hosted
version: "7.2.10"
version: "8.0.0"
built_collection:
dependency: transitive
description:
@ -214,10 +219,10 @@ packages:
dependency: transitive
description:
name: dart_style
sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4"
sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
url: "https://pub.dev"
source: hosted
version: "2.2.4"
version: "2.3.8"
decimal:
dependency: transitive
description:
@ -336,10 +341,10 @@ packages:
dependency: "direct dev"
description:
name: hive_generator
sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938"
sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4"
url: "https://pub.dev"
source: hosted
version: "1.1.3"
version: "2.0.1"
http:
dependency: "direct main"
description:
@ -428,6 +433,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
macros:
dependency: transitive
description:
name: macros
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
url: "https://pub.dev"
source: hosted
version: "0.1.3-main.0"
matcher:
dependency: transitive
description:
@ -472,10 +485,10 @@ packages:
dependency: "direct dev"
description:
name: mobx_codegen
sha256: d4beb9cea4b7b014321235f8fdc7c2193ee0fe1d1198e9da7403f8bc85c4407c
sha256: e0abbbc651a69550440f6b65c99ec222a1e2a4afd7baec8ba0f3088c7ca582a8
url: "https://pub.dev"
source: hosted
version: "2.3.0"
version: "2.7.1"
monero:
dependency: "direct main"
description:
@ -670,27 +683,37 @@ packages:
socks5_proxy:
dependency: transitive
description:
name: socks5_proxy
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
url: "https://pub.dev"
source: hosted
version: "1.0.6"
path: "."
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
url: "https://github.com/LacticWhale/socks_dart"
source: git
version: "2.1.0"
socks_socket:
dependency: transitive
description:
path: "."
ref: e6232c53c1595469931ababa878759a067c02e94
resolved-ref: e6232c53c1595469931ababa878759a067c02e94
url: "https://github.com/sneurlax/socks_socket"
source: git
version: "1.1.1"
source_gen:
dependency: transitive
description:
name: source_gen
sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d"
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
url: "https://pub.dev"
source: hosted
version: "1.2.6"
version: "1.5.0"
source_helper:
dependency: transitive
description:
name: source_helper
sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f"
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
url: "https://pub.dev"
source: hosted
version: "1.3.3"
version: "1.3.5"
source_span:
dependency: transitive
description:
@ -751,10 +774,19 @@ packages:
dependency: transitive
description:
name: timing
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
version: "1.0.1"
tor_binary:
dependency: transitive
description:
path: "."
ref: cb811c610871a9517d47134b87c2f590c15c96c5
resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
url: "https://github.com/MrCyjaneK/flutter-tor_binary"
source: git
version: "4.7.14"
tuple:
dependency: transitive
description:
@ -844,5 +876,5 @@ packages:
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.5.0 <4.0.0"
dart: ">=3.6.0 <4.0.0"
flutter: ">=3.24.0"

View file

@ -31,10 +31,10 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.4.7
build_resolvers: ^2.0.9
build_runner: ^2.4.15
build_resolvers: ^2.4.4
mobx_codegen: ^2.0.7
hive_generator: ^1.1.3
hive_generator: ^2.0.1
dependency_overrides:
watcher: ^1.1.0

View file

@ -5,18 +5,23 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8"
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
url: "https://pub.dev"
source: hosted
version: "47.0.0"
version: "76.0.0"
_macros:
dependency: transitive
description: dart
source: sdk
version: "0.3.3"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80"
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
url: "https://pub.dev"
source: hosted
version: "4.7.0"
version: "6.11.0"
args:
dependency: transitive
description:
@ -86,26 +91,26 @@ packages:
dependency: "direct dev"
description:
name: build_resolvers
sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6"
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
url: "https://pub.dev"
source: hosted
version: "2.0.10"
version: "2.4.4"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
url: "https://pub.dev"
source: hosted
version: "2.4.13"
version: "2.4.15"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41"
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
url: "https://pub.dev"
source: hosted
version: "7.2.10"
version: "8.0.0"
built_collection:
dependency: transitive
description:
@ -214,10 +219,10 @@ packages:
dependency: transitive
description:
name: dart_style
sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4"
sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
url: "https://pub.dev"
source: hosted
version: "2.2.4"
version: "2.3.8"
decimal:
dependency: "direct main"
description:
@ -333,10 +338,10 @@ packages:
dependency: "direct dev"
description:
name: hive_generator
sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938"
sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4"
url: "https://pub.dev"
source: hosted
version: "1.1.3"
version: "2.0.1"
http:
dependency: "direct main"
description:
@ -433,6 +438,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
macros:
dependency: transitive
description:
name: macros
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
url: "https://pub.dev"
source: hosted
version: "0.1.3-main.0"
matcher:
dependency: transitive
description:
@ -477,10 +490,10 @@ packages:
dependency: "direct dev"
description:
name: mobx_codegen
sha256: d4beb9cea4b7b014321235f8fdc7c2193ee0fe1d1198e9da7403f8bc85c4407c
sha256: e0abbbc651a69550440f6b65c99ec222a1e2a4afd7baec8ba0f3088c7ca582a8
url: "https://pub.dev"
source: hosted
version: "2.3.0"
version: "2.7.1"
monero:
dependency: "direct main"
description:
@ -667,27 +680,37 @@ packages:
socks5_proxy:
dependency: transitive
description:
name: socks5_proxy
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
url: "https://pub.dev"
source: hosted
version: "1.0.6"
path: "."
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
url: "https://github.com/LacticWhale/socks_dart"
source: git
version: "2.1.0"
socks_socket:
dependency: transitive
description:
path: "."
ref: e6232c53c1595469931ababa878759a067c02e94
resolved-ref: e6232c53c1595469931ababa878759a067c02e94
url: "https://github.com/sneurlax/socks_socket"
source: git
version: "1.1.1"
source_gen:
dependency: transitive
description:
name: source_gen
sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d"
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
url: "https://pub.dev"
source: hosted
version: "1.2.6"
version: "1.5.0"
source_helper:
dependency: transitive
description:
name: source_helper
sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f"
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
url: "https://pub.dev"
source: hosted
version: "1.3.3"
version: "1.3.5"
source_span:
dependency: transitive
description:
@ -752,6 +775,15 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.2"
tor_binary:
dependency: transitive
description:
path: "."
ref: cb811c610871a9517d47134b87c2f590c15c96c5
resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
url: "https://github.com/MrCyjaneK/flutter-tor_binary"
source: git
version: "4.7.14"
tuple:
dependency: transitive
description:
@ -841,5 +873,5 @@ packages:
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.5.0 <4.0.0"
dart: ">=3.6.0 <4.0.0"
flutter: ">=3.24.0"

View file

@ -30,10 +30,10 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.4.7
build_runner: ^2.4.15
mobx_codegen: ^2.1.1
build_resolvers: ^2.0.9
hive_generator: ^1.1.3
build_resolvers: ^2.4.4
hive_generator: ^2.0.1
dependency_overrides:
watcher: ^1.1.0

View file

@ -18,8 +18,8 @@ In order to build the latest version of Cake Wallet, simply run the following:
git clone --branch main https://github.com/cake-tech/cake_wallet.git
# NOTE: Replace `main` with the latest release tag available at https://github.com/cake-tech/cake_wallet/releases/latest.
cd cake_wallet
# docker build -t ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.4-go1.24.1 . # Uncomment to build the docker image yourself instead of pulling it from the registry
docker run -v$(pwd):$(pwd) -w $(pwd) -i --rm ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.4-go1.24.1 bash -x << EOF
# docker build -t ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.0-go1.24.1-ruststablenightly . # Uncomment to build the docker image yourself instead of pulling it from the registry
docker run -v$(pwd):$(pwd) -w $(pwd) -i --rm ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.0-go1.24.1-ruststablenightly bash -x << EOF
set -x -e
pushd scripts/android
source ./app_env.sh cakewallet

View file

@ -7,7 +7,7 @@ The following are the system requirements to build Cake Wallet for your iOS devi
```txt
macOS 15.3.1
Xcode 16.2
Flutter 3.27.4
Flutter 3.27.0
```
NOTE: Newer versions of macOS and Xcode may also work, but have not been confirmed to work by the Cake team.
@ -43,9 +43,9 @@ To enable iOS build support for Xcode, perform the following:
### 3. Installing Flutter
Install Flutter, specifically version `3.27.4` by following the [official docs](https://docs.flutter.dev/get-started/install/macos/desktop?tab=download).
Install Flutter, specifically version `3.27.0` by following the [official docs](https://docs.flutter.dev/get-started/install/macos/desktop?tab=download).
NOTE: as `3.27.4` is not the latest version, you'll need to download it from <https://docs.flutter.dev/release/archive> instead of the link in the docs above.
NOTE: as `3.27.0` is not the latest version, you'll need to download it from <https://docs.flutter.dev/release/archive> instead of the link in the docs above.
### 4. Installing Rust
@ -65,7 +65,7 @@ The output of this command should appear like this, indicating successful instal
```zsh
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.27.4, on macOS 15.x.x)
[✓] Flutter (Channel stable, 3.27.0, on macOS 15.x.x)
[✓] Xcode - develop for iOS and macOS (Xcode 16.2)
```

View file

@ -20,8 +20,8 @@ In order to build the latest version of Cake Wallet, simply run the following:
git clone --branch main https://github.com/cake-tech/cake_wallet.git
# NOTE: Replace `main` with the latest release tag available at https://github.com/cake-tech/cake_wallet/releases/latest.
cd cake_wallet
# docker build -t ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.4-go1.24.1 . # Uncomment to build the docker image yourself instead of pulling it from the registry
docker run --privileged -v$(pwd):$(pwd) -w $(pwd) -i --rm ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.4-go1.24.1 bash -x << EOF
# docker build -t ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.0-go1.24.1-ruststablenightly . # Uncomment to build the docker image yourself instead of pulling it from the registry
docker run --privileged -v$(pwd):$(pwd) -w $(pwd) -i --rm ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.0-go1.24.1-ruststablenightly bash -x << EOF
set -x -e
pushd scripts
./gen_android_manifest.sh

View file

@ -7,7 +7,7 @@ The following are the system requirements to build Cake Wallet for your macOS de
```txt
macOS 15.3.1
Xcode 16.2
Flutter 3.27.4
Flutter 3.27.0
```
### 1. Installing dependencies
@ -34,9 +34,9 @@ sudo xcodebuild -runFirstLaunch
### 3. Installing Flutter
Install Flutter, specifically version `3.27.4` by following the [official docs](https://docs.flutter.dev/get-started/install/macos/desktop?tab=download).
Install Flutter, specifically version `3.27.0` by following the [official docs](https://docs.flutter.dev/get-started/install/macos/desktop?tab=download).
NOTE: as `3.27.4` is not the latest version, you'll need to download it from <https://docs.flutter.dev/release/archive> instead of the link in the docs above.
NOTE: as `3.27.0` is not the latest version, you'll need to download it from <https://docs.flutter.dev/release/archive> instead of the link in the docs above.
### 4. Installing Rust
@ -56,7 +56,7 @@ The output of this command should appear like this, indicating successful instal
```zsh
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.27.4, on macOS 15.x.x)
[✓] Flutter (Channel stable, 3.27.0, on macOS 15.x.x)
...
[✓] Xcode - develop for iOS and macOS (Xcode 16.2)
...

View file

@ -6,18 +6,18 @@ The following are the system requirements to build Cake Wallet for your Windows
```txt
Windows 10 or later (64-bit), x86-64 based
Flutter 3.27.4
Flutter 3.27.0
```
### 1. Installing Flutter
Install Flutter, specifically version `3.27.4` by following the [official docs](https://docs.flutter.dev/get-started/install/windows).
Install Flutter, specifically version `3.27.0` by following the [official docs](https://docs.flutter.dev/get-started/install/windows).
In order for Flutter to function, you'll also need to enable Developer Mode:
Start Menu > search for "Run" > type `ms-settings:developers`, and turn on Developer Mode.
NOTE: as `3.27.4` is not the latest version, you'll need to download it from <https://docs.flutter.dev/release/archive> instead of the link in the docs above.
NOTE: as `3.27.0` is not the latest version, you'll need to download it from <https://docs.flutter.dev/release/archive> instead of the link in the docs above.
### 2. Install Development Tools

View file

@ -70,7 +70,10 @@ class WalletKeysAndSeedPageRobot {
if (walletType == WalletType.bitcoin ||
walletType == WalletType.litecoin ||
walletType == WalletType.bitcoinCash) {
commonTestCases.hasText(appStore.wallet!.seed!);
final seedWords = appStore.wallet!.seed!.split(" ");
for (var seedWord in seedWords) {
commonTestCases.hasTextAtLestOnce(seedWord);
}
tester.printToConsole('$walletName wallet has seeds properly displayed');
}
@ -78,10 +81,14 @@ class WalletKeysAndSeedPageRobot {
walletType == WalletType.solana ||
walletType == WalletType.tron) {
if (hasSeed) {
commonTestCases.hasText(appStore.wallet!.seed!);
final seedWords = appStore.wallet!.seed!.split(" ");
for (var seedWord in seedWords) {
commonTestCases.hasTextAtLestOnce(seedWord);
}
tester.printToConsole('$walletName wallet has seeds properly displayed');
}
if (hasPrivateKey) {
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
commonTestCases.hasText(appStore.wallet!.privateKey!);
tester.printToConsole('$walletName wallet has private key properly displayed');
}
@ -89,14 +96,19 @@ class WalletKeysAndSeedPageRobot {
if (walletType == WalletType.nano || walletType == WalletType.banano) {
if (hasSeed) {
commonTestCases.hasText(appStore.wallet!.seed!);
final seedWords = appStore.wallet!.seed!.split(" ");
for (var seedWord in seedWords) {
commonTestCases.hasTextAtLestOnce(seedWord);
}
tester.printToConsole('$walletName wallet has seeds properly displayed');
}
if (hasHexSeed) {
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
commonTestCases.hasText(appStore.wallet!.hexSeed!);
tester.printToConsole('$walletName wallet has hexSeed properly displayed');
}
if (hasPrivateKey) {
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
commonTestCases.hasText(appStore.wallet!.privateKey!);
tester.printToConsole('$walletName wallet has private key properly displayed');
}
@ -129,35 +141,39 @@ class WalletKeysAndSeedPageRobot {
final hasSeedLegacy = Polyseed.isValidSeed(seed);
if (hasPublicSpendKey) {
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
commonTestCases.hasText(keys.publicSpendKey);
tester.printToConsole('$walletName wallet has public spend key properly displayed');
}
if (hasPrivateSpendKey) {
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
commonTestCases.hasText(keys.privateSpendKey);
tester.printToConsole('$walletName wallet has private spend key properly displayed');
}
if (hasPublicViewKey) {
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
commonTestCases.hasText(keys.publicViewKey);
tester.printToConsole('$walletName wallet has public view key properly displayed');
}
if (hasPrivateViewKey) {
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
commonTestCases.hasText(keys.privateViewKey);
tester.printToConsole('$walletName wallet has private view key properly displayed');
}
if (hasSeeds) {
await commonTestCases.dragUntilVisible(
'${walletName}_wallet_seed_item_key',
'wallet_keys_page_credentials_list_view_key',
);
commonTestCases.hasText(seed);
await commonTestCases.tapItemByKey('wallet_keys_page_seed');
final seedWords = seed.split(" ");
for (var seedWord in seedWords) {
commonTestCases.hasTextAtLestOnce(seedWord);
}
tester.printToConsole('$walletName wallet has seeds properly displayed');
}
if (hasSeedLegacy) {
await commonTestCases.dragUntilVisible(
'${walletName}_wallet_seed_legacy_item_key',
'wallet_keys_page_credentials_list_view_key',
);
commonTestCases.hasText(legacySeed);
await commonTestCases.tapItemByKey('wallet_keys_page_seed_legacy');
final seedWords = legacySeed.split(" ");
for (var seedWord in seedWords) {
commonTestCases.hasTextAtLestOnce(seedWord);
}
tester.printToConsole('$walletName wallet has legacy seeds properly displayed');
}
}

View file

@ -76,7 +76,7 @@ PODS:
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- payjoin_flutter (0.20.0)
- payjoin_flutter (0.23.0)
- permission_handler_apple (9.3.0):
- Flutter
- reown_yttrium (0.0.1):
@ -235,7 +235,7 @@ SPEC CHECKSUMS:
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
payjoin_flutter: 6397d7b698cdad6453be4949ab6aca1863f6c5e5
payjoin_flutter: d9d4c8aa16bd5dfedb9b21d0edc8199e0187d96e
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
reown_yttrium: c0e87e5965fa60a3559564cc35cffbba22976089
SDWebImage: 73c6079366fea25fa4bb9640d5fb58f0893facd8

View file

@ -6,8 +6,9 @@ import 'package:cake_wallet/anonpay/anonpay_status_response.dart';
import 'package:cake_wallet/core/fiat_conversion_service.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/exchange/limits.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:http/http.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
@ -20,17 +21,21 @@ class AnonPayApi {
final WalletBase wallet;
static const anonpayRef = secrets.anonPayReferralCode;
static const onionApiAuthority = 'tqzngtf2hybjbexznel6dhgsvbynjzezoybvtv6iofomx7gchqfssgqd.onion';
static const clearNetAuthority = 'trocador.app';
// static const onionApiAuthority = 'tqzngtf2hybjbexznel6dhgsvbynjzezoybvtv6iofomx7gchqfssgqd.onion';
static const onionApiAuthority = clearNetAuthority;
static const markup = secrets.trocadorExchangeMarkup;
static const anonPayPath = '/anonpay';
static const anonPayStatus = '/anonpay/status';
static const coinPath = 'api/coin';
static const apiKey = secrets.trocadorApiKey;
static final apiKey = isMoneroOnly ? secrets.trocadorMoneroApiKey : secrets.trocadorApiKey;
Future<AnonpayStatusResponse> paymentStatus(String id) async {
final authority = await _getAuthority();
final response = await get(Uri.https(authority, "$anonPayStatus/$id"));
final response = await ProxyWrapper().get(
clearnetUri: Uri.https(clearNetAuthority, "$anonPayStatus/$id"),
onionUri: Uri.https(onionApiAuthority, "$anonPayStatus/$id"),
);
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final status = responseJSON['Status'] as String;
final fiatAmount = responseJSON['Fiat_Amount'] as double?;
@ -69,9 +74,10 @@ class AnonPayApi {
if (request.fiatEquivalent != null) {
body['fiat_equiv'] = request.fiatEquivalent;
}
final authority = await _getAuthority();
final response = await get(Uri.https(authority, anonPayPath, body));
final response = await ProxyWrapper().get(
clearnetUri: Uri.https(clearNetAuthority, anonPayPath, body),
onionUri: Uri.https(onionApiAuthority, anonPayPath, body),
);
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final id = responseJSON['ID'] as String;
@ -146,17 +152,16 @@ class AnonPayApi {
'name': cryptoCurrency.name,
};
final String apiAuthority = await _getAuthority();
final uri = Uri.https(apiAuthority, coinPath, params);
final response = await get(uri);
final response = await ProxyWrapper().get(
clearnetUri: Uri.https(clearNetAuthority, coinPath, params),
onionUri: Uri.https(onionApiAuthority, coinPath, params),
);
final responseJSON = json.decode(response.body) as List<dynamic>;
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
}
final responseJSON = json.decode(response.body) as List<dynamic>;
if (responseJSON.isEmpty) {
throw Exception('No data');
}
@ -197,17 +202,4 @@ class AnonPayApi {
return tag.toLowerCase();
}
}
Future<String> _getAuthority() async {
try {
if (useTorOnly) {
return onionApiAuthority;
}
final uri = Uri.https(onionApiAuthority, '/anonpay');
await get(uri);
return onionApiAuthority;
} catch (e) {
return clearNetAuthority;
}
}
}

View file

@ -1,8 +1,8 @@
import 'dart:convert';
import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
import 'package:cake_wallet/utils/exception_handler.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cake_wallet/anypay/any_pay_payment.dart';
import 'package:cake_wallet/anypay/any_pay_trasnaction.dart';
@ -53,7 +53,11 @@ class AnyPayApi {
final body = <String, dynamic>{
'chain': chainByScheme(scheme),
'currency': currencyByScheme(scheme).title};
final response = await post(url, headers: headers, body: utf8.encode(json.encode(body)));
final response = await ProxyWrapper().post(
clearnetUri: url,
headers: headers,
body: json.encode(body),
);
if (response.statusCode != 200) {
await ExceptionHandler.onError(FlutterErrorDetails(exception: response));
@ -79,7 +83,12 @@ class AnyPayApi {
'chain': chain,
'currency': currency,
'transactions': transactions.map((tx) => {'tx': tx.tx, 'tx_hash': tx.id, 'tx_key': tx.key}).toList()};
final response = await post(Uri.parse(uri), headers: headers, body: utf8.encode(json.encode(body)));
final response = await ProxyWrapper().post(
clearnetUri: Uri.parse(uri),
headers: headers,
body: json.encode(body),
);
if (response.statusCode == 400) {
final decodedBody = json.decode(response.body) as Map<String, dynamic>;
throw Exception(decodedBody['message'] as String? ?? 'Unexpected response\nError code: 400');

View file

@ -747,5 +747,6 @@ class CWBitcoin extends Bitcoin {
final _wallet = wallet as ElectrumWallet;
(_wallet.walletAddresses as BitcoinWalletAddresses).payjoinManager.cleanupSessions();
(_wallet.walletAddresses as BitcoinWalletAddresses).currentPayjoinReceiver = null;
(_wallet.walletAddresses as BitcoinWalletAddresses).payjoinEndpoint = null;
}
}

View file

@ -10,6 +10,7 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
import 'package:cw_core/crypto_currency.dart';
@ -17,7 +18,6 @@ import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
class DFXBuyProvider extends BuyProvider {
@ -100,12 +100,13 @@ class DFXBuyProvider extends BuyProvider {
});
final uri = Uri.https(_baseUrl, _authPath);
var response = await http.post(
uri,
final response = await ProxyWrapper().post(
clearnetUri: uri,
headers: {'Content-Type': 'application/json'},
body: requestBody,
);
if (response.statusCode == 201) {
final responseBody = jsonDecode(response.body);
return responseBody['accessToken'] as String;
@ -137,7 +138,9 @@ class DFXBuyProvider extends BuyProvider {
final url = Uri.https(_baseUrl, '/v1/fiat');
try {
final response = await http.get(url, headers: {'accept': 'application/json'});
final response = await ProxyWrapper().get(
clearnetUri: url,
headers: {'accept': 'application/json'});
if (response.statusCode == 200) {
final data = jsonDecode(response.body) as List<dynamic>;
@ -160,7 +163,7 @@ class DFXBuyProvider extends BuyProvider {
final url = Uri.https(_baseUrl, '/v1/asset', {'blockchains': blockchain});
try {
final response = await http.get(url, headers: {'accept': 'application/json'});
final response = await ProxyWrapper().get(clearnetUri: url, headers: {'accept': 'application/json'});
if (response.statusCode == 200) {
final responseData = jsonDecode(response.body);
@ -271,7 +274,12 @@ class DFXBuyProvider extends BuyProvider {
});
try {
final response = await http.put(url, headers: headers, body: body);
final response = await ProxyWrapper().put(
clearnetUri: url,
headers: headers,
body: body,
);
final responseData = jsonDecode(response.body);
if (response.statusCode == 200) {

View file

@ -9,10 +9,10 @@ import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:flutter/material.dart';
import 'dart:developer';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
class KryptonimBuyProvider extends BuyProvider {
@ -74,9 +74,14 @@ class KryptonimBuyProvider extends BuyProvider {
});
try {
final response = await http.post(url, headers: headers, body: body);
final response = await ProxyWrapper().post(
clearnetUri: url,
headers: headers,
body: body,
);
if (response.statusCode == 200 || response.statusCode == 201 || response.statusCode == 401) {
return jsonDecode(response.body) as Map<String, dynamic>;
} else {
return {};

View file

@ -8,13 +8,13 @@ import 'package:cake_wallet/buy/payment_method.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:flutter/material.dart';
import 'dart:developer';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
class MeldBuyProvider extends BuyProvider {
@ -71,8 +71,8 @@ class MeldBuyProvider extends BuyProvider {
final url = Uri.https(_baseUrl, path, params);
try {
final response = await http.get(
url,
final response = await ProxyWrapper().get(
clearnetUri: url,
headers: {
'Authorization': _isProduction ? '' : _testApiKey,
'Meld-Version': '2023-12-19',
@ -81,6 +81,7 @@ class MeldBuyProvider extends BuyProvider {
},
);
if (response.statusCode == 200) {
final data = jsonDecode(response.body) as List<dynamic>;
final paymentMethods =
@ -130,7 +131,12 @@ class MeldBuyProvider extends BuyProvider {
});
try {
final response = await http.post(url, headers: headers, body: body);
final response = await ProxyWrapper().post(
clearnetUri: url,
headers: headers,
body: body,
);
if (response.statusCode == 200) {
final data = jsonDecode(response.body) as Map<String, dynamic>;

View file

@ -16,12 +16,12 @@ import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/themes/core/material_base_theme.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart';
import 'package:url_launcher/url_launcher.dart';
class MoonPayProvider extends BuyProvider {
@ -98,9 +98,12 @@ class MoonPayProvider extends BuyProvider {
Future<String> getMoonpaySignature(String query) async {
final uri = Uri.https(_cIdBaseUrl, "/api/moonpay");
final response = await post(uri,
final response = await ProxyWrapper().post(
clearnetUri: uri,
headers: {'Content-Type': 'application/json', 'x-api-key': _exchangeHelperApiKey},
body: json.encode({'query': query}));
body: json.encode({'query': query}),
);
if (response.statusCode == 200) {
return (jsonDecode(response.body) as Map<String, dynamic>)['signature'] as String;
@ -120,7 +123,11 @@ class MoonPayProvider extends BuyProvider {
final url = Uri.https(_baseUrl, path, params);
try {
final response = await get(url, headers: {'accept': 'application/json'});
final response = await ProxyWrapper().get(
clearnetUri: url,
headers: {'accept': 'application/json'},
);
if (response.statusCode == 200) {
return jsonDecode(response.body) as Map<String, dynamic>;
} else {
@ -191,7 +198,7 @@ class MoonPayProvider extends BuyProvider {
final path = '$_currenciesPath/$formattedCryptoCurrency$quotePath';
final url = Uri.https(_baseUrl, path, params);
try {
final response = await get(url);
final response = await ProxyWrapper().get(clearnetUri: url);
if (response.statusCode == 200) {
final data = jsonDecode(response.body) as Map<String, dynamic>;
@ -306,7 +313,8 @@ class MoonPayProvider extends BuyProvider {
Future<Order> findOrderById(String id) async {
final url = _apiUrl + _transactionsSuffix + '/$id' + '?apiKey=' + _apiKey;
final uri = Uri.parse(url);
final response = await get(uri);
final response = await ProxyWrapper().get(clearnetUri: uri);
if (response.statusCode != 200) {
throw BuyException(title: providerDescription, content: 'Transaction $id is not found!');

View file

@ -9,11 +9,12 @@ import 'package:cake_wallet/buy/payment_method.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/themes/core/theme_store.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
class OnRamperBuyProvider extends BuyProvider {
@ -66,8 +67,10 @@ class OnRamperBuyProvider extends BuyProvider {
final url = Uri.https(_baseApiUrl, '$supported$defaultsAll', params);
try {
final response =
await http.get(url, headers: {'Authorization': _apiKey, 'accept': 'application/json'});
final response = await ProxyWrapper().get(
clearnetUri: url,
headers: {'Authorization': _apiKey, 'accept': 'application/json'},
);
if (response.statusCode == 200) {
final Map<String, dynamic> data = jsonDecode(response.body) as Map<String, dynamic>;
@ -102,7 +105,7 @@ class OnRamperBuyProvider extends BuyProvider {
try {
final response =
await http.get(url, headers: {'Authorization': _apiKey, 'accept': 'application/json'});
await ProxyWrapper().get(clearnetUri: url, headers: {'Authorization': _apiKey, 'accept': 'application/json'});
if (response.statusCode == 200) {
final Map<String, dynamic> data = jsonDecode(response.body) as Map<String, dynamic>;
@ -132,7 +135,8 @@ class OnRamperBuyProvider extends BuyProvider {
try {
final response =
await http.get(url, headers: {'Authorization': _apiKey, 'accept': 'application/json'});
await ProxyWrapper().get(clearnetUri: url, headers: {'Authorization': _apiKey, 'accept': 'application/json'});
if (response.statusCode == 200) {
final Map<String, dynamic> data = jsonDecode(response.body) as Map<String, dynamic>;
@ -195,7 +199,7 @@ class OnRamperBuyProvider extends BuyProvider {
final headers = {'Authorization': _apiKey, 'accept': 'application/json'};
try {
final response = await http.get(url, headers: headers);
final response = await ProxyWrapper().get(clearnetUri: url, headers: headers);
if (response.statusCode == 200) {
final data = jsonDecode(response.body) as List<dynamic>;

View file

@ -11,6 +11,7 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
import 'package:cw_core/crypto_currency.dart';
@ -18,7 +19,6 @@ import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
class RobinhoodBuyProvider extends BuyProvider {
@ -69,7 +69,8 @@ class RobinhoodBuyProvider extends BuyProvider {
final uri = Uri.https(_apiBaseUrl, '$_assetsPath', {'applicationId': _applicationId});
try {
final response = await http.get(uri, headers: {'accept': 'application/json'});
final response = await ProxyWrapper().get(clearnetUri: uri, headers: {'accept': 'application/json'});
if (response.statusCode == 200) {
final responseData = jsonDecode(response.body) as Map<String, dynamic>;
@ -122,12 +123,14 @@ class RobinhoodBuyProvider extends BuyProvider {
final uri = Uri.https(_cIdBaseUrl, "/api/robinhood");
var response = await http.post(uri,
var response = await ProxyWrapper().post(
clearnetUri: uri,
headers: {'Content-Type': 'application/json'},
body: json
.encode({'valid_until': valid_until, 'wallet': walletAddress, 'signature': signature}));
body: json.encode({'valid_until': valid_until, 'wallet': walletAddress, 'signature': signature}),
);
if (response.statusCode == 200) {
return (jsonDecode(response.body) as Map<String, dynamic>)['connectId'] as String;
} else {
throw Exception('Provider currently unavailable. Status: ${response.statusCode}');
@ -219,7 +222,8 @@ class RobinhoodBuyProvider extends BuyProvider {
Uri.https('api.robinhood.com', '/catpay/v1/${cryptoCurrency.title}/quote/', queryParams);
try {
final response = await http.get(uri, headers: {'accept': 'application/json'});
final response = await ProxyWrapper().get(clearnetUri: uri, headers: {'accept': 'application/json'});
final responseData = jsonDecode(response.body) as Map<String, dynamic>;
if (response.statusCode == 200) {

View file

@ -3,7 +3,7 @@ import 'package:cake_wallet/buy/buy_exception.dart';
import 'package:cake_wallet/buy/pairs_utils.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:http/http.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cake_wallet/buy/buy_amount.dart';
import 'package:cake_wallet/buy/buy_provider.dart';
import 'package:cake_wallet/buy/buy_provider_description.dart';
@ -73,18 +73,21 @@ class WyreBuyProvider extends BuyProvider {
'referrerAccountId': _accountId,
'lockFields': ['amount', 'sourceCurrency', 'destCurrency', 'dest']
};
final response = await post(uri,
final response = await ProxyWrapper().post(
clearnetUri: uri,
headers: {
'Authorization': 'Bearer $_secretKey',
'Content-Type': 'application/json',
'cache-control': 'no-cache'
},
body: json.encode(body));
body: json.encode(body),
);
if (response.statusCode != 200) {
throw BuyException(title: providerDescription, content: 'Url $url is not found!');
}
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final urlFromResponse = responseJSON['url'] as String;
return urlFromResponse;
@ -101,18 +104,21 @@ class WyreBuyProvider extends BuyProvider {
'country': _countryCode
};
final uri = Uri.parse(quoteUrl);
final response = await post(uri,
final response = await ProxyWrapper().post(
clearnetUri: uri,
headers: {
'Authorization': 'Bearer $_secretKey',
'Content-Type': 'application/json',
'cache-control': 'no-cache'
},
body: json.encode(body));
body: json.encode(body),
);
if (response.statusCode != 200) {
throw BuyException(title: providerDescription, content: 'Quote is not found!');
}
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final sourceAmount = responseJSON['sourceAmount'] as double;
final destAmount = responseJSON['destAmount'] as double;
@ -125,8 +131,7 @@ class WyreBuyProvider extends BuyProvider {
Future<Order> findOrderById(String id) async {
final orderUrl = baseApiUrl + _ordersSuffix + '/$id';
final orderUri = Uri.parse(orderUrl);
final orderResponse = await get(orderUri);
final orderResponse = await ProxyWrapper().get(clearnetUri: orderUri);
if (orderResponse.statusCode != 200) {
throw BuyException(title: providerDescription, content: 'Order $id is not found!');
}
@ -142,8 +147,7 @@ class WyreBuyProvider extends BuyProvider {
final transferUrl = baseApiUrl + _transferSuffix + transferId + _trackSuffix;
final transferUri = Uri.parse(transferUrl);
final transferResponse = await get(transferUri);
final transferResponse = await ProxyWrapper().get(clearnetUri: transferUri);
if (transferResponse.statusCode != 200) {
throw BuyException(title: providerDescription, content: 'Transfer $transferId is not found!');
}

View file

@ -3,9 +3,9 @@ import 'dart:convert';
import 'package:cake_wallet/cake_pay/cake_pay_order.dart';
import 'package:cake_wallet/cake_pay/cake_pay_user_credentials.dart';
import 'package:cake_wallet/cake_pay/cake_pay_vendor.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cake_wallet/entities/country.dart';
import 'package:http/http.dart' as http;
class CakePayApi {
static const testBaseUri = false;
@ -32,12 +32,17 @@ class CakePayApi {
'Content-Type': 'application/json',
'Authorization': 'Api-Key $apiKey',
};
final response = await http.post(uri, headers: headers, body: json.encode({'email': email}));
final response = await ProxyWrapper().post(
clearnetUri: uri,
headers: headers,
body: json.encode({'email': email}),
);
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
}
final bodyJson = json.decode(response.body) as Map<String, dynamic>;
if (bodyJson.containsKey('user') && bodyJson['user']['email'] != null) {
@ -64,12 +69,17 @@ class CakePayApi {
};
final query = <String, String>{'email': email, 'otp': code};
final response = await http.post(uri, headers: headers, body: json.encode(query));
final response = await ProxyWrapper().post(
clearnetUri: uri,
headers: headers,
body: json.encode(query),
);
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
}
final bodyJson = json.decode(response.body) as Map<String, dynamic>;
if (bodyJson.containsKey('error')) {
@ -116,9 +126,14 @@ class CakePayApi {
};
try {
final response = await http.post(uri, headers: headers, body: json.encode(query));
final response = await ProxyWrapper().post(
clearnetUri: uri,
headers: headers,
body: json.encode(query),
);
if (response.statusCode != 201) {
final responseBody = json.decode(response.body);
if (responseBody is List) {
throw '${responseBody[0]}';
@ -127,6 +142,7 @@ class CakePayApi {
}
}
final bodyJson = json.decode(response.body) as Map<String, dynamic>;
return CakePayOrder.fromMap(bodyJson);
} catch (e) {
@ -145,7 +161,8 @@ class CakePayApi {
'X-CSRFToken': CSRFToken,
};
final response = await http.get(uri, headers: headers);
final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers);
printV('Response: ${response.statusCode}');
@ -168,7 +185,11 @@ class CakePayApi {
};
try {
final response = await http.post(uri, headers: headers, body: json.encode({'email': email}));
final response = await ProxyWrapper().post(
clearnetUri: uri,
headers: headers,
body: json.encode({'email': email}),
);
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
@ -187,7 +208,7 @@ class CakePayApi {
'Authorization': 'Api-Key $apiKey',
};
final response = await http.get(uri, headers: headers);
final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers);
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
@ -234,14 +255,15 @@ class CakePayApi {
'Authorization': 'Api-Key $apiKey',
};
var response = await http.get(uri, headers: headers);
var response = await ProxyWrapper().get(clearnetUri: uri, headers: headers);
if (response.statusCode != 200) {
throw Exception(
'Failed to fetch vendors: statusCode - ${response.statusCode}, queryParams -$queryParams, response - ${response.body}');
}
final bodyJson = json.decode(utf8.decode(response.bodyBytes));
final bodyJson = json.decode(response.body);
if (bodyJson is List<dynamic> && bodyJson.isEmpty) {
return [];

View file

@ -38,9 +38,9 @@ class AddressValidator extends TextValidator {
'|[0-9a-zA-Z]{105}|addr1[0-9a-zA-Z]{98}';
case CryptoCurrency.btc:
pattern =
'${P2pkhAddress.regex.pattern}|${P2shAddress.regex.pattern}|${RegExp(r'(bc|tb)1q[ac-hj-np-z02-9]{25,39}}').pattern}|${P2trAddress.regex.pattern}|${P2wshAddress.regex.pattern}|${SilentPaymentAddress.regex.pattern}';
'${P2pkhAddress.regex.pattern}|${P2shAddress.regex.pattern}|${P2wpkhAddress.regex.pattern}|${P2trAddress.regex.pattern}|${P2wshAddress.regex.pattern}|${SilentPaymentAddress.regex.pattern}';
case CryptoCurrency.ltc:
pattern = '^${RegExp(r'ltc1q[ac-hj-np-z02-9]{25,39}').pattern}\$|^${MwebAddress.regex.pattern}\$';
pattern = '${P2wpkhAddress.regex.pattern}|${MwebAddress.regex.pattern}';
case CryptoCurrency.nano:
pattern = '[0-9a-zA-Z_]+';
case CryptoCurrency.banano:
@ -335,10 +335,6 @@ class AddressValidator extends TextValidator {
}
}
if (pattern != null) {
return "$BEFORE_REGEX($pattern)$AFTER_REGEX";
}
return null;
return pattern != null ? "($pattern)" : null;
}
}

View file

@ -7,6 +7,7 @@ import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/utils/feature_flag.dart';
import 'package:cake_wallet/utils/tor.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
import 'package:cw_core/sync_status.dart';
@ -15,6 +16,7 @@ import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter/foundation.dart';
class BackgroundSync {
final FlutterLocalNotificationsPlugin _notificationsPlugin = FlutterLocalNotificationsPlugin();
@ -90,6 +92,11 @@ class BackgroundSync {
}
Future<void> sync() async {
final settingsStore = getIt.get<SettingsStore>();
if (settingsStore.currentBuiltinTor) {
printV("Starting Tor");
await ensureTorStarted(context: null);
}
printV("Background sync started");
await _syncWallets();
printV("Background sync completed");
@ -100,7 +107,6 @@ class BackgroundSync {
final walletListViewModel = getIt.get<WalletListViewModel>();
final settingsStore = getIt.get<SettingsStore>();
final List<WalletListItem> moneroWallets = walletListViewModel.wallets
.where((element) => !element.isHardware)
.where((element) => ![WalletType.haven, WalletType.decred].contains(element.type))

Some files were not shown because too many files have changed in this diff Show more