mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-25 05:58:42 +01:00
+4








084fd6bb1e
* refactor node form UI to use new ListItem model * add new list item row types and widgets * add style wrapper * remove print [skip ci] * Integrate new design mockup with ViewModel (#2653) * feat: add Lightning Network support for Bitcoin wallets * refactor: rename `fiatConvertationStore` to `fiatConversionStore` for consistency and update related occurrences across codebase * feat: enhance address validation with Lightning Network invoice support for BTC & refactor wallet type/token checks in view model * feat: add support for Lightning invoice detection, refactor MWEB deposit/withdraw actions, and integrate Lightning transaction creation with updated priority handling * feat: add method to retrieve unused Spark deposit address for Bitcoin wallets * feat: add Breez API key support and update secrets handling for Bitcoin Lightning wallet integration in workflows * chore: update Breez SDK dependency to version 0.3.4 in pubspec files * Add bitcoin secrets config [skip ci] * feat: extend Lightning wallet functionality with transaction history fetching * feat: add LNURL-pay address detection and support in address parsing flow for Bitcoin Lightning integration * refactor: simplify `ReceivePageOption` logic * refactor: centralize `PaymentURI` generation logic across wallet types * feat: enhance `PaymentURI` handling with asynchronous support and Lightning-specific functionality * refactor: streamline `PaymentURI` logic and remove redundant URI implementations across wallet types * refactor: remove redundant debug print statement from `bitcoin_wallet_addresses.dart` * refactor: improve consistency in widget styling and centralized label logic, add Bitcoin Lightning deposit/withdraw support * feat: reload balance and tx history after sending a lightning transaction * feat: improve address formatting for human-readable addresses and update the default LNURL domain * fix: merge conflicts * feat: add error handling for LightningWallet initialization and adjust transaction direction logic * integrate homepage from new ui mockup * fix import * feat: add Lightning Network support for Bitcoin wallets * refactor: rename `fiatConvertationStore` to `fiatConversionStore` for consistency and update related occurrences across codebase * feat: enhance address validation with Lightning Network invoice support for BTC & refactor wallet type/token checks in view model * feat: add support for Lightning invoice detection, refactor MWEB deposit/withdraw actions, and integrate Lightning transaction creation with updated priority handling * feat: add method to retrieve unused Spark deposit address for Bitcoin wallets * feat: add Breez API key support and update secrets handling for Bitcoin Lightning wallet integration in workflows * chore: update Breez SDK dependency to version 0.3.4 in pubspec files * Add bitcoin secrets config [skip ci] * feat: extend Lightning wallet functionality with transaction history fetching * feat: add LNURL-pay address detection and support in address parsing flow for Bitcoin Lightning integration * refactor: simplify `ReceivePageOption` logic * refactor: centralize `PaymentURI` generation logic across wallet types * feat: enhance `PaymentURI` handling with asynchronous support and Lightning-specific functionality * refactor: streamline `PaymentURI` logic and remove redundant URI implementations across wallet types * refactor: remove redundant debug print statement from `bitcoin_wallet_addresses.dart` * refactor: improve consistency in widget styling and centralized label logic, add Bitcoin Lightning deposit/withdraw support * feat: reload balance and tx history after sending a lightning transaction * feat: improve address formatting for human-readable addresses and update the default LNURL domain * fix: merge conflicts * feat: add error handling for LightningWallet initialization and adjust transaction direction logic * integrate homepage from new ui mockup * fix import * add new-ui dir to pubspec_base * minor layout fixes * cleanup navbar logic * Modify navbar behaviour * smooth color change when selecting navbar options * open old ui pages with new ui action buttons * feat: working navbar in new ui * fix: minor sizing tweaks * CW-1266-integrate-bitcoin-lightning-through-spark-sdk (#2623) * feat: add Lightning Network support for Bitcoin wallets * refactor: rename `fiatConvertationStore` to `fiatConversionStore` for consistency and update related occurrences across codebase * feat: enhance address validation with Lightning Network invoice support for BTC & refactor wallet type/token checks in view model * feat: add support for Lightning invoice detection, refactor MWEB deposit/withdraw actions, and integrate Lightning transaction creation with updated priority handling * feat: add method to retrieve unused Spark deposit address for Bitcoin wallets * feat: add Breez API key support and update secrets handling for Bitcoin Lightning wallet integration in workflows * chore: update Breez SDK dependency to version 0.3.4 in pubspec files * Add bitcoin secrets config [skip ci] * feat: extend Lightning wallet functionality with transaction history fetching * feat: add LNURL-pay address detection and support in address parsing flow for Bitcoin Lightning integration * refactor: simplify `ReceivePageOption` logic * refactor: centralize `PaymentURI` generation logic across wallet types * feat: enhance `PaymentURI` handling with asynchronous support and Lightning-specific functionality * refactor: streamline `PaymentURI` logic and remove redundant URI implementations across wallet types * refactor: remove redundant debug print statement from `bitcoin_wallet_addresses.dart` * refactor: improve consistency in widget styling and centralized label logic, add Bitcoin Lightning deposit/withdraw support * feat: reload balance and tx history after sending a lightning transaction * feat: improve address formatting for human-readable addresses and update the default LNURL domain * fix: merge conflicts * feat: add error handling for LightningWallet initialization and adjust transaction direction logic * feat: enable private transactions by default in LightningWallet and update Breez SDK version to 0.4.2 * minor fixes [skip ci] * chore: fix some minor issues in comments (#2654) Signed-off-by: black5box <black5box@outlook.com> * fix: handle send-all functionality for LightningWallet transactions and adjust amount calculation logic * fix-german (#2659) * chore: update German localization strings for consistency and accuracy * chore: update German localization strings for consistency and accuracy [skip-ci] * feat: add LNURL support for address validation and LightningWallet compatibility, enhance error handling for OpenCryptoPay * fix: adjust LightningWallet amount parsing from 9 to 8 decimal places * feat: add LNURL support in LightningPaymentRequest and LightningWallet * Fix navigation gradient (#2657) * Fix navigation gradient * Fix CONFIG_ARGS formatting in app_config.sh (#2660) * fix: block wrongly parsed addresses (#2656) * fix: block wrongly parsed addresses * fix: move parsed address check into handlePaymentFlow method * fix: Handle QR URLs separately for pay anything flow * feat: add electrum seed support for Lightning * refactor: improve `parseFixed` logic and add comprehensive unit tests for edge cases (#2661) * Update cw_bitcoin/lib/bitcoin_wallet.dart [skip ci] * resolve conflict issue --------- Signed-off-by: black5box <black5box@outlook.com> Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> Co-authored-by: black5box <black5box@outlook.com> Co-authored-by: tuxsudo <tuxsudo@tux.pizza> Co-authored-by: cyan <cyjan@mrcyjanek.net> Co-authored-by: David Adegoke <64401859+Blazebrain@users.noreply.github.com> * remove old code [skip ci] * fix: page widgets rebuilt every time * chore: formatting * remove unused navbar * fix: lightning balance on balance card in lightning mode * fix: return if uri == null * feat: trade history in new ui * feat: enhance balance model with secondary asset handling * feat: unique card colors and icons for coins * wip: card customization * fix: merge conflict * feat: customizable balance card * Fix some spacings and test sizing * Fix formatting * feat: add Lightning Network support for send flow and fee handling adjustments * refactor: remove unnecessary debug print statement in send flow for Lightning Network balance * feat: integrate OpenCryptoPay QR handling in send flow and improve scan action handling logic * feat: add maxDepositClaimFee parameter for Lightning wallet configuration * Switch balance card to RoundedSuperellipse and set color list to Wrap * Fix * Switch to scroll view * refactor: update navigation route from `receive` to `addressPage` in coin action row * feat: bloc-based viewmodel for card customizer * feat: mobx-independent balance card customizer vm * minor fixes * copyWith constructor for cleaner state changes * refactor: wrap `HistorySection` with `Observer` for reactive state updates, update navigation for "Buy" action, and fix incorrect balance property * feat: enhance Bitcoin receive flow with Lightning and SegWit options, update address page registration, and localize coin action labels * feat: add helper methods for Lightning and SegWit receive page options in Bitcoin configuration * feat: add Lightning transaction type handling and enable navigation to transaction details from history * Add more special card types * feat: new ui settings page * feat: add Lightning actions to balance cards, refactor card rendering logic * refactor: update settings page localization, hide unused settings items * feat: add secrets generation for cw_bitcoin, including breezApiKey * Lightning switcher animation (#2725) * Properly animate bitcoin/lightning switcher * Cleanup and fix sizing * Final cleanup * Fix action button colors (#2732) * feat: add hardware wallet type handling and improve parsed address reset flow * Refactor settings page to use generic row items (#2745) * fix android back button in settings on android, force iphone-style transitions on all platforms * move settings page to generic row impl * fix imports * Balance card fixes (#2729) * Update balance card sizing * Fix spacing * Initial font changes (#2731) * Initial font changes * More font changes * feat: integrate Lightning transaction support and improve transaction history UX - Enhanced dashboard to compute confirmations using the first and last transactions. - Updated `BitcoinWallet` to subscribe for and manage Lightning transactions. - Improved `HistoryTile` layout and introduced new leading icons for Lightning transactions. * feat: add unclaimed deposit actions and enhance Lightning event handling * release changes [skip ci] * Enhanced card customizer (#2744) * enhanced card customizer * Fix conflicts * Add missing icons + adjust spacing --------- Co-authored-by: tuxsudo <tuxsudo@tux.pizza> * feat: enhance Lightning invoice handling and address validation - Added support for identifying non-zero amount Lightning invoices. - Refined invoice matching with updated regex patterns. - Improved UI to conditionally display addresses in sending confirmations. - Adjusted logic to handle zero-value Lightning invoices. * feat: add `isPayjoinAvailable` computed property and update Payjoin visibility logic * feat: improve Lightning wallet handling and block explorer logic * update patch version [skip ci] --------- Signed-off-by: black5box <black5box@outlook.com> Co-authored-by: Konstantin Ullrich <konstantinullrich12@gmail.com> Co-authored-by: OmarHatem <omarh.ismail1@gmail.com> Co-authored-by: tuxsudo <tuxsudo@tux.pizza> Co-authored-by: black5box <black5box@outlook.com> Co-authored-by: cyan <cyjan@mrcyjanek.net> Co-authored-by: David Adegoke <64401859+Blazebrain@users.noreply.github.com> * minor new ui fixes * padding for new row separators * remove debug prints * New design improve nav bar (#2763) * tweak navigation bar touch targets and sizing * feat: add tests for identifying zero and non-zero amount Lightning invoices (#2772) * feat: add tests for identifying zero and non-zero amount Lightning invoices - Added unit tests to validate recognition of Bolt11 zero-amount and non-zero-amount invoices. - Enhanced `isBolt11ZeroInvoice` function to handle `lightning:` prefixed invoices. * fix: correct currency selection logic for Lightning wallets in Send ViewModel * feat: improve Lightning wallet initialization and address generation - Updated `init` method in `LightningWallet` to return a boolean for initialization success, adding error handling for failed setups. - Integrated dynamic name generation using `generateName` for Lightning wallet addresses. * refactor: relocate `generate_name` to `cw_core` and update imports * chore: update breez sdk (#2789) * Wire tokens and NFTs to new Home page (#2804) * wiring for assets on home page * fix imports * wire nft page in new ui * fix nft display in new ui * fix totalWidth calc * add tokens button + bugfixes * add tokens button + bugfixes * fix late initialization * fix history section exception * fixes * Add haptic feedback (#2813) * Add haptic feedback * Add haptic feedback to correct ActionButton * Return bottom gradient (#2808) * Add IgnorePointer to bottom nav gradient (#2822) * New design send page (#2791) * new ui send page (wip) * coin control page improvements * fee settings, new send confirmation modal * minor aesthetic fixes * load network name without cw_bitcoin import * recipient dot cleanup * minor logic fixes * ui polish * - add aliaspay support - fix wording for max button - fix back button action in modals * Sizing changes * dismiss transaction on modal close * sizing fix * - fix remove recipient icon - properly round amounts - fix coin control page spacing * - fix remove recipient icon - properly round amounts - fix coin control page spacing * - fix button sizing - add description for fees page * Update lib/view_model/unspent_coins/unspent_coins_list_view_model.dart [skip ci] * Update lib/view_model/send/send_view_model.dart [skip ci] --------- Co-authored-by: tuxsudo <tuxsudo@tux.pizza> Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * Fix conflicts with dev * Fix conflicts with dev * Add zcash receive options to new design flow * wire in missing settings in new ui * up migration version * formatting * translations * lint.sh * New design receive page (#2834) * feat: viewmodel on new receive page (wip) * feat: address type selector for new ui receive page * fix: minor fixes to touch targets and tap anims * minor ui fix * wip receive page changes * receive page + address selection page * receive page + address selection page * add font * add svg * minor ui fixes * receive page fixes/improvements * change receive page modal design * further ui polish * icons for infobox * fix restricted import * fix closing label modal * - fix balance card appearance on address page - fix search bar position - fix mweb position in address type selector * receive page fixes * fix anonpay * remove restricted import * fixes * fix receive on zec * simplify chain id * lint fix * fix migration numbering Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * fix migration version Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * fix settings * readability * fix formatting * move dismiss infobox to vm * disable addr rotation on zcash * remove _setEffects * translations --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * New UI Sync bar (#2831) * syncbar * small styling fixes * modify duration * lint.sh * syncHeavy wallet types Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * update translations, add pull-down refresh, separate top bar components to files --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * alphabetize * New design balance card icons (#2824) * Add the other crypto card icons * Update Zano, add Zcash and Arbitrum * Update default card style and available gradients * Fix broken preview * Fix animation * Color combinations fix * Minor updates * Add decred icon * if(!mounted) return; * New design minor fixes (#2842) * More minor fixes * Update modal sheet behaviour * Update missing currency image links * List row and navigation bar tweaks * Minor navbar fix * New UI Swap page (#2829) * new ui send page (wip) * coin control page improvements * fee settings, new send confirmation modal * minor aesthetic fixes * load network name without cw_bitcoin import * recipient dot cleanup * minor logic fixes * ui polish * - add aliaspay support - fix wording for max button - fix back button action in modals * Sizing changes * dismiss transaction on modal close * sizing fix * - fix remove recipient icon - properly round amounts - fix coin control page spacing * - fix remove recipient icon - properly round amounts - fix coin control page spacing * - fix button sizing - add description for fees page * swap page * fixes * further fixes * merge * png -> svg * minor condition changes * lint.sh * fix swapsxyz isCentralized Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * translations, readability improvements, remove debug rows * cleanup * alphabetize strings * small bugfixes --------- Co-authored-by: tuxsudo <tuxsudo@tux.pizza> Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * fix new ui coin control page layout (#2843) * New design lightning flows (#2821) * new ui send page (wip) * coin control page improvements * fee settings, new send confirmation modal * minor aesthetic fixes * load network name without cw_bitcoin import * recipient dot cleanup * minor logic fixes * ui polish * - add aliaspay support - fix wording for max button - fix back button action in modals * Sizing changes * dismiss transaction on modal close * sizing fix * - fix remove recipient icon - properly round amounts - fix coin control page spacing * - fix remove recipient icon - properly round amounts - fix coin control page spacing * - fix button sizing - add description for fees page * lightning flow wip * lightning flow wip #2 * lightning withdraw flow * lightning deposit flow (wip, currently works but really janky) * send from external + bugfixes * merge fixes * merge fixes * fixes * Update lib/new-ui/widgets/send_page/l2_action_wallet_selector.dart --------- Co-authored-by: tuxsudo <tuxsudo@tux.pizza> Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * fix covered create wallet button * fix: close modal after transaction commitment in Send ViewModel (#2858) * New design tweaks (#2847) * Add more haptic feedback * Fix background gradients and modify SafeArea in home_page.dart * Fix history padding, fix divider color, disable Charts, update base_page.dart appbar style, fix nav bar border + height on iOS * Lightning Username setup flow for new UI (#2845) * lightning username edit/create flow * fix button color * fix layout * fixes * remove character limitation for internal builds * remove dependency on cw_bitcoin * fix username requirements * fixes * condition fix * Update lib/new-ui/pages/lightning_username_page.dart --------- Co-authored-by: OmarHatem <omarh.ismail1@gmail.com> * keyboard hide overlay for iphone * New design UI tweaks (#2870) * Fix nav height on Android * Fix blue gradient * Update transactions view and minor UI tweaks * CW1011-Allow-users-to-view-bitcoin-amounts-in-sats (#2678) * feat: introduce `formatFixed` integration and sats display preference * feat: enable display of Bitcoin amounts in satoshis and refactor amount formatting logic * refactor: consolidate fee formatting logic and add support for MWEB availability check * feat: add support for satoshi-based amount display and enhance fiat conversion logic * feat: add support for satoshi-based amount parsing, formatting, and display preferences across buy/sell flow and UI adjustments * feat: implement `AmountParsingProxy` with unit tests for parsing and formatting crypto amounts in various display modes * feat: replace `preferBalanceInSats` with `displayAmountsInSatoshi` and introduce `AmountParsingProxy` for unified crypto amount handling across the app * refactor: streamline amount formatting logic and enhance unspent coins handling with `AmountParsingProxy` integration for unified crypto processing * refactor: replace direct bitcoin amount formatting with `AmountParsingProxy` and remove debug print in `exchange_view_model` * refactor: replace `getIt<AmountParsingProxy>` with `_appStore.amountParsingProxy`, remove redundant dependencies across view models and adjust related imports * refactor: remove redundant `AmountParsingProxy` registration in DI setup to simplify dependency management [skip ci] * feat: add getCryptoSymbol to simplify the Symbol selection between Sats and Bitcoin * fix: merge conflict * Revert "fix: merge conflict" This reverts commit 6debdaa0466747d6fcc85d6bd0be115e6fb4d856. * fix import [skip ci] * feat: remove number formatter from exchangeVM * refactor: replace `SettingsStore` with `AppStore` across view models, integrate `AmountParsingProxy` for amount formatting, and clean up redundant imports * refactor: remove debug print statements from `getCryptoOutputAmount` in `AmountParsingProxy` * feat: enhance fee and deposit amount formatting with crypto symbols and improved parsing * feat: integrate `AmountParsingProxy` for improved Bitcoin amount parsing in `PaymentURI` and fiat-to-crypto conversion * feat: append crypto symbols to Bitcoin transaction amounts in dashboard view * feat: improve crypto amount parsing in SendCard widget and Output view model using `AmountParsingProxy` * feat: conditionally show Bitcoin amount display setting for Bitcoin wallets and refactor `DisplaySettingsViewModel` to use `AppStore` * refactor: clean up redundant imports in `bitcoin_wallet.dart` and `electrum_wallet.dart` * feat: append crypto symbols to fee values in send and cake pay card widgets for improved clarity * refactor: replace `currencyTitle` with `currency` across widgets and integrate `AmountParsingProxy` for improved crypto amount formatting and parsing * refactor: improve validation logic and amount parsing in SendCard and Output widgets * refactor: standardize amount parsing and formatting logic with `AmountParsingProxy` across ViewModels and widgets * refactor: `AppStore` remove debug prints * refactor: rename amount parsing/formatting methods for better clarity in tests [skip ci] * fix: AmountParsingProxy and optimize `cryptoAmount` handling in BuySellViewModel * fix: adjust balance calculation for Monero and Wownero to exclude unlocked amount from full balance * fix: correct `_appStore` usage and format adjustments in `SendViewModel` * feat: enhance satoshi on new-ui * feat: add computed property for `amountParsingProxy` in AppStore * feat: localize Bitcoin amount display option and update asset history UI * feat: improve exchange amount processing and refactor fiat conversion values --------- Co-authored-by: OmarHatem <omarh.ismail1@gmail.com> * New design unconfirmed balance widget (#2861) * unconfirmed balance display widget for new ui * unconfirmed balance display widget for new ui * merge * alphabetize translations * New UI fixes (#2860) * fix asset top bar * improve wordmark in large qr mode on receive * receive option fixes * exchange -> swap * send page fixes * update pubspec base * go to home on wallet change * nicer card customizer * don't show account name for single-account wallets * special card design for lightning * fiat amount bar improvement * update translations * add sending indicator * fix non-number input to receive amount * de-jank fiat input * nicer balance card animation when switching btc-ln * nicer balance card animation when switching btc-ln * fix backup password input * display time+date in history * fix error on buy * fix incorrect wallet showing in wallets tab * fix wallet list layout * only show coin control option on hasCoinControl * better error on send page * fiat value in coin control * add zano to isSyncHeeavy * fix keys in settings * fix assets condition * fix import * remove BasePage dependency in RescanPage * add missing settings * show send/receive in swap confirm sheet * fix receive background on ios * fix assets spacing * fix trade tile sizing * fix padding * fix icon in wallet select modal * change text on swap initial modal * spacing * l2 send external modal styling * fix null on swap confirm sheet * change infobox text * add chain icon to asset widgets * add chain icon to asset widgets * disable swap page reset * fix wallet list page layout * revert merge conflict issue [skip ci] --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * Account Customizer for new UI Monero (#2859) * new ui monero account management (wip) * account reordering wip * account reordering wip * account reordering (works) * cards view compact mode * format * translation * name generation * fix for non-xmr wallets * } * allow reset order, fallback on improperly saved order * translation * fix for pre-reorder wallet loading * fix loading * - save cards on modal slide-down - always load active account as first * fix card customizer close anim * fixes * fix condition * merge * fix migration versions * use updated flutter rust bridge in lightning and payjoin * mvp fixes * new ui mweb mask/unmask * fallback for balance card amount format * New UI tweaks (#2872) * Update lightning transaction history view * Add BNB card design * Color, padding, and radius fixes * Fix wallet list bottom section & gradient * Update Apps page * reformat * nicer sat picker * fix keyboard * chain badges * enhance styling for card customizer * mweb settings icon * mweb chain badge * update wording in settings * bnb chain icon * always round send amount * always round all amount * token placeholder * Fix ModernButton colors (#2888) * Fix ModernButton colors * Minor * guard for unconfirmed balance widget * fix error on wallet switch * New design mvp fixes (#2890) * mvp fixes * fallback for balance card amount format * reformat * nicer sat picker * fix keyboard * chain badges * enhance styling for card customizer * bnb chain icon * always round send amount * always round all amount * guard for unconfirmed balance widget * fix error on wallet switch * Update lib/new-ui/pages/account_customizer.dart Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * fixes --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * Install Protoc in reusable build workflow Added step to install Protoc before building. * Fix protobuf-compiler installation step Update package list before installing protobuf-compiler. * Modify Protoc installation command to handle update errors Change the install command for Protoc to avoid failure on update. * Streamline Lightning wallet balance management and crypto handling (#2893) * refactor: streamline crypto amount handling and improve Lightning balance management - Removed redundancy in balance calculations for Lightning wallets. - Centralized crypto symbol creation with `selectedCryptoCurrencySymbol`. - Enhanced crypto amount handling and formatting with `AmountParsingProxy`. - Simplified logic for displaying Lightning balances across UI components. * refactor: remove sats display logic and unused formatting from BalanceCard widget * chore: update breez-sdk-spark-flutter to latest commit * feat: enhance crypto amount formatting with localized separators and max decimals * fixes * fix: handle null balance records in Lightning wallet cards * New design add validators (#2894) * add validation, fix node form * Add validator to send amount input field Added a validator to the amount input field. --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * Disable 'addresses' button on receive screen for BTC LN (#2896) * feat: Disable addresses button on receive screen for BTC LN * chore: remove debug print in uri computation * feat: introduce swap action toggle and UI updates * feat: conditionally display Zcash card setting for Zcash wallets in Display Settings * Fix leading button color (#2898) * fix amount format * fix loadCards * pre-beta fixes * cyjan: fix: order being null for certain index * pre-beta fixes v2 (#2905) * minor context check [skip ci] * persist fee priority for zcash (#2907) * Add support for Zcash address detection and amount logic refactor (#2906) * refactor: consolidate crypto amount handling with `displayAmount` and `cryptoCurrencySymbol`, improve fiat conversion logic, and optimize token currency management * refactor: simplify `displayAmount` logic and remove unused `_rawAmount` property * feat: add support for Zcash transparent address detection and simplify amount conversion logic * Improve Lightning invoice handling and crypto formatting (#2908) * fix: update Lightning address regex to support additional formats and prefixes * fix: conditionally render advanced settings dropdown based on fees or coin control availability * feat: enhance address detection, Lightning invoice handling, and crypto formatting * feat: improve Lightning invoice parsing and crypto amount formatting on send page * chore: remove unnecessary exception printing in `getBolt11Amount` function * Fix lightning swap * import fix (hide Mac;) * parse -> tryParse * Transaction Commited -> Transaction Sent! * feat: enable clipboard paste for deposit and receive addresses in swap page (#2909) * fix: resolving LNURLS (#2911) * new UI pre-beta fixes (#2910) * fixes * parse -> tryParse * change sat display settings style * minor fix until hive is removed [skip ci] * Remove redundant logic and improve send page features (#2913) * fix: update crypto amount parsing logic in send confirmation sheet * feat: Added checkbox to toggle Litecoin MWEB coins on the send page. fix: LNUrlPayRecord * fix: remove redundant `.withLocalSeperator()` from fiat balance formatting logic * fix: update Bitcoin amount display titles to align with consistent formatting standards * force boolean to update (#2916) * force boolean to update * fix bnb balance * fixes #4 (#2917) * only display sats for btc wallets in card customizer * fix: resolve async handling of Lightning transaction history in `fetchTransactions` method (#2919) * New design pre beta fixes 5 (#2918) * improve l2 deposit/withdraw flow layouts * add copied indicator * SafeArea * fix addr rotation * fix styling of lightning switcher * no raster graphics in new ui please * optimization for assets/history section * remove unused bloc provider * optimize dashboard + fix duplicated name * disable lightning mode on wallet change * remove broken animation * safearea around nodes page * fix rescan page layout * fix exception on really fast switching * remove broken blur * card customizer autosave * remove debug print * add better fallbacks for currency titles * revert due to broken animation * fix send page crash * fix chain title for arb * fix cupertino date picker * Update lib/new-ui/widgets/receive_page/receive_bottom_buttons.dart [skip ci] --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * feat: add focus management and external address validation to send page forms - Introduced `focusNode` to `NewSendAddressInput` for better input handling. - Enhanced address validation by synchronizing with parsed external addresses. - Updated logic for non-MWEB unspent coin type in send actions. - Suppressed unnecessary exception logs in Bitcoin wallet transactions. * New design swap page fixes (#2923) * swap page fixes * add scroll controller * comment * tweak refresh pulldown (#2924) * add copy button to l2 send external modal (#2925) * account customizer padding * feat: lightning transaction fetching and Lightning wallet integration (#2927) - Allow nullable `height` in `ElectrumTransactionInfo`. - Add `fromDate` support for filtered Lightning transaction history retrieval. - Simplify Lightning address handling logic in wallet addresses. - Optimize Lightning wallet transaction syncing based on the last Lightning transaction date. - Clean up imports in `electrum_transaction_history.dart`. * New balance handeling (#2931) * feat: lightning transaction fetching and Lightning wallet integration - Allow nullable `height` in `ElectrumTransactionInfo`. - Add `fromDate` support for filtered Lightning transaction history retrieval. - Simplify Lightning address handling logic in wallet addresses. - Optimize Lightning wallet transaction syncing based on the last Lightning transaction date. - Clean up imports in `electrum_transaction_history.dart`. * feat: add support for Lightning balance in wallet snapshot and initialization * Disable Lightning switcher for hardware wallets (#2932) * fix: disable Lightning switcher for hardware wallets * fix: update wallet type check to use `isSoftwareWallet` for Lightning support * New design pre beta fixes 6 (#2929) * fix swaps from ln * prevent crash if ln balance isn't loaded yet * fix currency icons disappearing on swap confirm sheet * fix swap fiat validation * display ln at top on swap currency selector in btc wallets * update icon for card options * display default fiat and crypto at top of currency picker * fix fiat amount bar formatting * add modal scroll controller to provider logs * revert premature optimization (sizing issue) * New design pre beta fixes 7 (#2935) * fix swaps from ln * prevent crash if ln balance isn't loaded yet * fix currency icons disappearing on swap confirm sheet * fix swap fiat validation * display ln at top on swap currency selector in btc wallets * update icon for card options * display default fiat and crypto at top of currency picker * fix fiat amount bar formatting * add modal scroll controller to provider logs * revert premature optimization (sizing issue) * copyable trade id * disable swaptrade on ln * set isFixedRate properly * update color * display trocador provider on swap confirmation sheet * add "exchange rate is fixed" text --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * updated l2 modal (#2934) * display names for send addr input (#2936) * fix: MWEB toggle logic on send page (#2938) * feat: add retry logic to `getAddress` in Lightning wallet (#2939) * Enhance crypto address handling and add Lightning invoice support (#2937) * feat: enhance crypto address handling and add Lightning invoice support - Added `_overrideFromCryptoCurrency` and `_overrideToCryptoCurrency` for Zcash unified and shielded addresses. - Improved Lightning invoice generation with `getLightningInvoice` for Bitcoin wallets. - Updated error handling in exchange provider to accommodate API changes. - Introduced Lightning invoice fallback for deposit and receive addresses in ExchangeViewModel. * feat: Added `_overrideFromCryptoCurrency` and `_overrideToCryptoCurrency` for Zcash unified and shielded addresses. * fix pay anything on paste button (#2940) * Prevent multiple paste triggering any pay flow * fixup fiat input (#2941) * New design small fixes (#2920) * Fix optical symmetry for swipe to send text * Update nav bar icons * Top bar and card tweaks * Update paddings --------- Co-authored-by: malik1004x <malikowskirobert@gmail.com> * fix erc20 balance display (#2944) * fix erc20 balance display * route through cw_evm * New design beta fixes (#2943) * fix asset tile layout * fix card customizer keyboard jumping * fix cardsview exception * fix swap modal height * remove syncbar percentage * add copy icon to trade id * fix trade confirmation appearing twice * remove useless mweb setting * add tag to contact currency selector text * prettify picker for ln * fix receive crash * always route to NewSendPage * handle ln amount on paste * handle ln amount on payment request creation * prettify ln errors * readd "blocks" word * register deeplinks * Enhance bitcoin error message [skip ci] * fix support chat page (#2946) * new-design-fix-confirm-sheet-parsing-error (#2950) * feat: use bigint instead of doubles * feat: use bigint instead of doubles * feat: use bigint instead of doubles * fix: also show sats in confirm sheet * fix: also show sats in confirm sheet * feat: localize amounts and cap amount in tx history (#2951) * fix: linux CI * feat: localize token balances (#2954) * fix: send amount hww (#2955) * chore: use latest breez sdk (#2953) * fix addAddressWord * fixes (#2957) * receive fixes (#2959) * fix: stabilize android build by increasing jvm heap size (#2958) * fix: lighting by having the new SDK Save into a new location (#2961) * New design rc fixes (#2960) * additional safeguards for isSyncHeavy check * fix account name getting reset * update base icon * fix units in swap * readd memos to zec * New design minor UI (#2956) * Wrap text for list item widgets * Update lightning address and QR icons * Update near intents icon * Fix share issue * Fix (#2963) * fix tx amount formatting * rename currency * change wording for zec memo * fix base icon * fix app icon script [skip ci] * New design rc fixes 2 (#2965) * fix account name changes * fix spacing on addresses page * add payjoin copy modal * improve memo ui * potential fix for ci * Set default address for zcash and bitcoin for buy flow (#2979) * new monero.com icons (#2980) * parse and validate deposit amount from api (#2967) * generic fixes * minor context fix [skip ci] * Generic fixes (#2982) - solana send all bug - pasting SOL address triggering payanything flow - add more address types for payanything detection * rescan fix * about page (#2974) Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * update settings icons/wording (#2973) * Changelog modal (New UI) (#2971) * add new changelog modal * typo * print -> printV * Update lib/new-ui/widgets/changelog_modal.dart [skip ci] --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * fix node form (#2970) * new-design-add-breez-logging (#2984) * feat: add Lightning log export functionality for Bitcoin wallets * feat: add multilingual support for Lightning log export strings * feat: add Lightning support toggle in settings and translations for multiple languages * refactor: simplify `setUseLightning` by removing redundant store update * Update cw_bitcoin/lib/electrum_wallet.dart [skip ci] --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * minor fixes [skip ci] * minor context fixe [skip ci] * fix: make walletinfoid unique to prevent duplicates (#2969) * fix: walletconnect not working in new design builds (#2993) * new-design-fix-mweb-coin-enabling (#2995) * feat: add unspent coin type handling to asset details modal send button * fix: improve German translations for wallet, seed, and Lightning-related strings * fix: update Dutch translations to correct terminology and formatting #2992 (#2997) * fix: drop gn (#3000) * vulnerable seeds popup on new ui (#3004) * update mac os monero.com icons (#3003) * Add Payjoin log export and improve logging behavior (#2999) * fix: suppress exception logging in `getAddress` method of Lightning wallet - Added a try-catch block to avoid unnecessary exception logs during Lightning address retries. * fix: update fallback message for missing Lightning address detection * feat: add Payjoin log export and logging enhancements * fix: localize "Export Payjoin Logs" string in settings page * fix: litecoin mweb balance being included in the second asset (#2998) * Minor fixes (#2994) * Minor fixes * Update lib/new-ui/new_dashboard.dart [skip ci] --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * New design post release fixes (#3002) * fix: only throw on deserialize if kDebugMode is true (#2976) This closes #2972, in debug mode we will still catch these issues but if somehow that happens on production it will fallback to safe default * fix: update docs and dockerfile (#2975) * fix: missing steps for linux build [skip ci] (#2985) * move balance to bigint * move balance to bigint * fix electrum issue * fix unconfirmed balance * fixes * Fix minor issue in Spanish translation (#2991) * Fix minor issue in French translation (#2987) * Fix minor issues in German translation (#2986) * update constructor calls in configure.dart * fixes * remove expnded * V6.0.1 Early Release Candidate --------- Co-authored-by: cyan <cyjan@mrcyjanek.net> Co-authored-by: Edwin den Boer <woordenboer@planet.nl> Co-authored-by: OmarHatem <omarh.ismail1@gmail.com> * Swaps xyz swap enhancements (#2615) * refactor token cache and limit handling * Improve SwapsXYZ external send logic * Improve SwapsXYZ fixed-rate handling * disable fixedRate for SwapsXyz * SwapsXYZ contract call * Add track url for swaps xyz * add arbitrum support * add check for unsupported source tokens * enable SwapsXyzExchangeProvider in provider list * refactor SwapsXYZ provider and trade flow handling * Update exchange_trade_view_model.dart * validate evm call-data * Revert "validate evm call-data" * fix msg value for ERC-20 transfers * fix flag for swapsXYZ send check * Update send_view_model.dart * handle Swaps.xyz method selectors & approvals * fix evm approvals * Improve EVM token approval & receipt handling * merge split token transfers in evm tx * fix token allowance check for approvals * add sourceToken validation to EVM tx creation * prioritizing incoming transactions over fetched outgoing ones * compute net flow when merging transactions * add truncateTrailingText to list items and widgets --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> Co-authored-by: Serhii-Borodenko <17529954+Serhii-Borodenko@users.noreply.github.com> * fix wownero balance (#3007) * fix keyboard (#3006) * fix android build * check if stream is closed [skip ci] * [skip ci] lint * fix: populate amount field (#3009) * fix address rotation exception (#3011) * copy uri with amount from recieve page (#3010) * add "transaction sent!" to swap confirmation sheet (#3012) * fix wallet change on token swaps (#3013) * Fix fee estimation and recipient logic in Lightning Network (#3014) * fix: update `hasMultiRecipient` logic to include non-Lightning coin type check * fix: Lightning Network fee estimation and parsing logic * fix: adjust `sumByBigInt` logic to handle `sendAll` flag in send page * fix: move sqlite migrations to sqlite.dart (#3008) * fix: move sqlite migrations to sqlite.dart fix: backup restore not initializing SQL properly * Update cw_core/lib/db/sqlite.dart --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * feat: update currency symbol handling and extend Electrum transaction details (#3016) * Use isCentralized for truncation trade id [skip ci] * fix: Incorrect token amount for tokens and filter out new spam transactions after swaps (#3015) * New design release swap fixes (#3018) * fix deserialize ln * fix get wallet type for bnb tokens * show extraId for swaps * chore: translate using gpt-5.2 (#3021) * add NEAR send-all validation * tiny ui fixes (#3023) * use double parsing for amount comparison[skip ci] * adjust lightning address error message * feat: add BTC LN support to available methods and payment data handling * feat: add cached Lightning address handling for improved performance (#3025) - Introduced `cachedAddress` field to store and reuse the last fetched Lightning address. - Updated `getLightningAddress` method to fallback on `cachedAddress` if no new address is fetched. - Extended wallet constructors and snapshots to include `cachedLightningAddress` for persistence. * Revert transaction history color * Minor * Apply suggestion from @malik1004x * new-design-post-release-fixes-3 (#3028) * fix unnecessary account switching * fix exception on bad payment amount * fallback for currencies on external swap modal * increase balance card touch target * don't use lightning addresses for exchange * increase max length for ln encoding * don't show infobox on ln receive * fix showing old tx details (null tx on createTransaction) * remove unused import * refactor: relocate `_logStream` declaration in `LightningWallet` for better code organization (#3029) * monero build fix update build numbers fix best rate null [skip ci] * hide swap on mweb asset * minor context fix [skip ci] * Fix Silent payment case * new-ui: fix changelog typo (#3036) Fix typo in changelog for account management. * Modify date format to Month DD YYYY * feat: docker based ci * Fix 2FA continue button location (#3043) * fix: Desktop wallet bugs (#2968) * fix: Desktop wallet bugs - repeated wallets post migration - newly created wallets not showing up - extra large components on desktop, needed maxwidth * chore: Remove duplicate logic * chore: remove formatting * refactor: streamline updateList method and handle multiple calls with a future based mutex * refactor: enhance swipe to confirm with dynamic width handling and improved drag threshold logic * Improve error handling and duplicate invoice handling in Lightning wallet (#3047) * fix: handle duplicate Lightning invoice payments gracefully * fix: improved error handling in Lightning wallet * General improvements (#3030) * General Improvements - Make borderRadius for CTAs uniform 18px - Identify hardware wallets in title of Seeds/Keys page - Make entire wallet tile clickable in wallet list * chore: Simplify check * Update lib/view_model/wallet_keys_view_model.dart [skip ci] --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * homescreen improvements (#3034) * Homescreen improvements - Increase touch area for card editing icon - Tapping any card in the background should move it ino the foreground without closing the account tab - make top left chain icon primary color with full opacity when syncing and 20% when done - add tor indicator to top bar when enabled * fix: remove unneeded color filter * refactor: Adjust structure for tor and sync indicators * minor fixes (#3045) * Enhance LNURL payment integration (#3041) * feat: enhance LNURL support and Lightning payment handling - Improve LNURL decoding and encoding with expanded prefix handling. - Add support for parsing and validating LNURL payment invoices. - Introduce logic for fetching LNURL payment request amounts. - Update payment flow to integrate LNURL and Lightning-specific conditions. - Refactor BOLT11 prefix validation and amount extraction logic. * fix: simplify LNURL invoice validation by removing redundant BOLT11 check --------- Signed-off-by: black5box <black5box@outlook.com> Co-authored-by: Serhii <borodenko.sv@gmail.com> Co-authored-by: malik1004x <malikowskirobert@gmail.com> Co-authored-by: Konstantin Ullrich <konstantinullrich12@gmail.com> Co-authored-by: tuxsudo <tuxsudo@tux.pizza> Co-authored-by: black5box <black5box@outlook.com> Co-authored-by: cyan <cyjan@mrcyjanek.net> Co-authored-by: David Adegoke <64401859+Blazebrain@users.noreply.github.com> Co-authored-by: Konstantin Ullrich <konstantin@cakewallet.com> Co-authored-by: Edwin den Boer <woordenboer@planet.nl> Co-authored-by: Serhii <17529954+serhii-bor@users.noreply.github.com> Co-authored-by: Serhii-Borodenko <17529954+Serhii-Borodenko@users.noreply.github.com> Co-authored-by: rottenwheel <92872541+rottenwheel@users.noreply.github.com>
1636 lines
53 KiB
Dart
1636 lines
53 KiB
Dart
import 'dart:async';
|
||
import 'dart:collection';
|
||
import 'dart:convert';
|
||
import 'dart:developer';
|
||
|
||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||
import 'package:cake_wallet/core/amount_parsing_proxy.dart';
|
||
import 'package:cake_wallet/core/create_trade_result.dart';
|
||
import 'package:cake_wallet/core/fiat_conversion_service.dart';
|
||
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
|
||
import 'package:cake_wallet/entities/calculate_fiat_amount.dart';
|
||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||
import 'package:cake_wallet/entities/fiat_api_mode.dart';
|
||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||
import 'package:cake_wallet/entities/preferences_key.dart';
|
||
import 'package:cake_wallet/entities/wallet_contact.dart';
|
||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||
import 'package:cake_wallet/exchange/exchange_template.dart';
|
||
import 'package:cake_wallet/exchange/exchange_trade_state.dart';
|
||
import 'package:cake_wallet/exchange/limits.dart';
|
||
import 'package:cake_wallet/exchange/limits_state.dart';
|
||
import 'package:cake_wallet/exchange/provider/chainflip_exchange_provider.dart';
|
||
import 'package:cake_wallet/exchange/provider/jupiter_exchange_provider.dart';
|
||
import 'package:cake_wallet/exchange/provider/letsexchange_exchange_provider.dart';
|
||
import 'package:cake_wallet/exchange/provider/changenow_exchange_provider.dart';
|
||
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
|
||
import 'package:cake_wallet/exchange/provider/exolix_exchange_provider.dart';
|
||
import 'package:cake_wallet/exchange/provider/near_Intents_exchange_provider.dart';
|
||
import 'package:cake_wallet/exchange/provider/stealth_ex_exchange_provider.dart';
|
||
import 'package:cake_wallet/exchange/provider/swapsxyz_exchange_provider.dart';
|
||
import 'package:cake_wallet/exchange/provider/swaptrade_exchange_provider.dart';
|
||
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
|
||
import 'package:cake_wallet/exchange/provider/xoswap_exchange_provider.dart';
|
||
import 'package:cake_wallet/exchange/trade.dart';
|
||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||
import 'package:cake_wallet/generated/i18n.dart';
|
||
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
|
||
import 'package:cake_wallet/store/app_store.dart';
|
||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||
import 'package:cw_core/currency.dart';
|
||
import "package:cw_core/wallet_info.dart";
|
||
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||
import 'package:cake_wallet/store/dashboard/trades_store.dart';
|
||
import 'package:cake_wallet/store/settings_store.dart';
|
||
import 'package:cake_wallet/store/templates/exchange_template_store.dart';
|
||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||
import 'package:cake_wallet/utils/feature_flag.dart';
|
||
import 'package:cake_wallet/utils/token_utilities.dart';
|
||
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
|
||
import 'package:cake_wallet/view_model/send/fees_view_model.dart';
|
||
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart';
|
||
import 'package:cw_core/crypto_amount_format.dart';
|
||
import 'package:cw_core/crypto_currency.dart';
|
||
import 'package:cw_core/erc20_token.dart';
|
||
import 'package:cw_core/spl_token.dart';
|
||
import 'package:cw_core/sync_status.dart';
|
||
import 'package:cw_core/transaction_priority.dart';
|
||
import 'package:cw_core/tron_token.dart';
|
||
import 'package:cw_core/unspent_coin_type.dart';
|
||
import 'package:cw_core/utils/print_verbose.dart';
|
||
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||
import 'package:cw_core/wallet_type.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:hive/hive.dart';
|
||
import 'package:mobx/mobx.dart';
|
||
import 'package:shared_preferences/shared_preferences.dart';
|
||
|
||
part 'exchange_view_model.g.dart';
|
||
|
||
class ExchangeViewModel = ExchangeViewModelBase with _$ExchangeViewModel;
|
||
|
||
abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with Store {
|
||
@override
|
||
void onWalletChange(wallet) {
|
||
// FIXME this caused a bug when switching wallet, i can't figure out why it's here?
|
||
// receiveCurrency = wallet.currency;
|
||
// depositCurrency = wallet.currency;
|
||
}
|
||
|
||
ExchangeViewModelBase(
|
||
this._appStore,
|
||
this.trades,
|
||
this._exchangeTemplateStore,
|
||
this.tradesStore,
|
||
this.sharedPreferences,
|
||
this.contactListViewModel,
|
||
this.unspentCoinsListViewModel,
|
||
this.feesViewModel,
|
||
this.fiatConversionStore,
|
||
) : isSendAllEnabled = false,
|
||
isFixedRateMode = false,
|
||
isReceiveAmountEntered = false,
|
||
_depositAmount = '',
|
||
_receiveAmount = '',
|
||
receiveAddress = '',
|
||
depositAddress = '',
|
||
isDepositAddressEnabled = false,
|
||
isReceiveAmountEditable = false,
|
||
_useTorOnly = false,
|
||
receiveCurrencies = ObservableList<CryptoCurrency>(),
|
||
depositCurrencies = ObservableList<CryptoCurrency>(),
|
||
limits = Limits(min: 0, max: 0),
|
||
tradeState = ExchangeTradeStateInitial(),
|
||
limitsState = LimitsInitialState(),
|
||
receiveCurrency = _appStore.wallet!.currency,
|
||
depositCurrency = _appStore.wallet!.currency,
|
||
providerList = [],
|
||
selectedProviders = ObservableList<ExchangeProvider>(),
|
||
super(appStore: _appStore) {
|
||
_useTorOnly = _settingsStore.exchangeStatus == ExchangeApiMode.torOnly;
|
||
_setProviders();
|
||
const excludeDepositCurrencies = [CryptoCurrency.btt];
|
||
const excludeReceiveCurrencies = [
|
||
CryptoCurrency.xlm,
|
||
CryptoCurrency.xrp,
|
||
CryptoCurrency.bnb,
|
||
CryptoCurrency.btt
|
||
];
|
||
_initialPairBasedOnWallet();
|
||
|
||
unspentCoinsListViewModel.initialSetup().then((_) {
|
||
unspentCoinsListViewModel.resetUnspentCoinsInfoSelections();
|
||
});
|
||
|
||
final Map<String, dynamic> exchangeProvidersSelection =
|
||
json.decode(sharedPreferences.getString(PreferencesKey.exchangeProvidersSelection) ?? "{}")
|
||
as Map<String, dynamic>;
|
||
|
||
/// if the provider is not in the user settings (user's first time or newly added provider)
|
||
/// then use its default value decided by us
|
||
selectedProviders = ObservableList.of(providerList
|
||
.where((element) =>
|
||
(!forceDecentralizedExchanges || !element.description.isCentralized) &&
|
||
(exchangeProvidersSelection[element.title] == null
|
||
? element.isEnabled
|
||
: (exchangeProvidersSelection[element.title] as bool)))
|
||
.toList());
|
||
|
||
_setAvailableProviders();
|
||
|
||
autorun((_) {
|
||
if (selectedProviders.any((provider) => provider is TrocadorExchangeProvider)) {
|
||
final trocadorProvider =
|
||
selectedProviders.firstWhere((provider) => provider is TrocadorExchangeProvider)
|
||
as TrocadorExchangeProvider;
|
||
|
||
updateAllTrocadorProviderStates(trocadorProvider);
|
||
}
|
||
});
|
||
|
||
bestRateSync = Timer.periodic(Duration(seconds: 10), (timer) {
|
||
if (tradeState is! TradeIsCreating) {
|
||
calculateBestRate();
|
||
}
|
||
});
|
||
|
||
isDepositAddressEnabled = !useSameWalletAddress(depositCurrency);
|
||
_depositAmount = '';
|
||
_receiveAmount = '';
|
||
receiveAddress = '';
|
||
depositAddress =
|
||
useSameWalletAddress(depositCurrency) ? wallet.walletAddresses.addressForExchange : '';
|
||
|
||
reaction((_) => receiveAddress, (_) {
|
||
if(!(tradeState is TradeIsCreatedSuccessfully)) {
|
||
receiveAddressDisplayName = null;
|
||
}
|
||
});
|
||
|
||
provider = providerList.firstOrNull;
|
||
final initialProvider = provider;
|
||
provider!.checkIsAvailable().then((bool isAvailable) {
|
||
if (!isAvailable && provider == initialProvider) {
|
||
provider = providerList.firstWhere((provider) => provider is ChangeNowExchangeProvider,
|
||
orElse: () => providerList.last);
|
||
_onPairChange();
|
||
}
|
||
});
|
||
|
||
// providerDisplay is read by ui to display auto-selected provider.
|
||
// it's on a delay so it doesn't flicker.
|
||
reaction((_)=>bestRateProvider,(val) {
|
||
providerDisplay = val;
|
||
},delay: 300);
|
||
|
||
receiveCurrencies = CryptoCurrency.all
|
||
.where((cryptoCurrency) => !excludeReceiveCurrencies.contains(cryptoCurrency))
|
||
.toList()
|
||
.asObservable();
|
||
depositCurrencies = CryptoCurrency.all
|
||
.where((cryptoCurrency) => !excludeDepositCurrencies.contains(cryptoCurrency))
|
||
.toList()
|
||
.asObservable();
|
||
|
||
_injectUserEthTokensIntoCurrencyLists();
|
||
_injectUserSplTokensIntoCurrencyLists();
|
||
_injectUserTronTokensIntoCurrencyLists();
|
||
_defineIsReceiveAmountEditable();
|
||
loadLimits();
|
||
reaction((_) => isFixedRateMode, (Object _) {
|
||
bestRateProvider = null;
|
||
bestRate = 0.0;
|
||
loadLimits();
|
||
});
|
||
|
||
reaction((_) => forceDecentralizedExchanges, (val) {
|
||
if (val && (bestRateProvider?.description.isCentralized ?? false)) {
|
||
bestRateProvider = null;
|
||
bestRate = 0.0;
|
||
loadLimits();
|
||
}
|
||
});
|
||
|
||
if (isElectrumWallet) {
|
||
bitcoin!.updateFeeRates(wallet);
|
||
}
|
||
fetchFiatPrice(receiveCurrency);
|
||
|
||
updateDepositAvailableAmount();
|
||
}
|
||
|
||
Future<void> updateDepositAvailableAmount() async {
|
||
if (depositCurrency == CryptoCurrency.btcln) {
|
||
final currency = depositCurrency;
|
||
Future.doWhile(() async {
|
||
await Future.delayed(const Duration(milliseconds: 400));
|
||
if (depositCurrency != currency) {
|
||
return false;
|
||
}
|
||
final balance = wallet.balance[depositCurrency];
|
||
if (balance != null) {
|
||
depositAvailableAmount = _appStore.amountParsingProxy
|
||
.getDisplayCryptoStringFromBigInt(balance.fullAvailableBalance, depositCurrency);
|
||
return false;
|
||
}
|
||
return true;
|
||
});
|
||
} else {
|
||
final currency = depositCurrency;
|
||
final amount = _appStore.amountParsingProxy.getDisplayCryptoString(
|
||
(await unspentCoinsListViewModel.getSendingBalance(UnspentCoinType.any)), depositCurrency);
|
||
if(depositCurrency == currency) {
|
||
depositAvailableAmount = amount;
|
||
}
|
||
}
|
||
}
|
||
|
||
void showFiatCurrencyPicker(BuildContext context) {
|
||
showPopUp<void>(
|
||
context: context,
|
||
builder: (_) => CurrencyPicker(
|
||
key: ValueKey('send_page_currency_picker_dialog_button_key'),
|
||
selectedAtIndex: FiatCurrency.all.indexOf(fiat),
|
||
items: FiatCurrency.all,
|
||
hintText: S.of(context).search_currency,
|
||
onItemSelected: (Currency cur) async {
|
||
_settingsStore.fiatCurrency = (cur as FiatCurrency);
|
||
},
|
||
),
|
||
);
|
||
}
|
||
|
||
bool useSameWalletAddress(CryptoCurrency currency) =>
|
||
currency == wallet.currency ||
|
||
(currency == CryptoCurrency.btcln && wallet.currency == CryptoCurrency.btc && wallet.isSoftwareWallet) ||
|
||
(currency.tag != null && currency.tag == wallet.currency.tag) ||
|
||
currency.tag == wallet.currency.title;
|
||
|
||
bool get isElectrumWallet => [
|
||
WalletType.bitcoin,
|
||
WalletType.litecoin,
|
||
WalletType.bitcoinCash,
|
||
WalletType.dogecoin
|
||
].contains(wallet.type);
|
||
|
||
bool get hideAddressAfterExchange =>
|
||
[WalletType.monero, WalletType.wownero, WalletType.zcash].contains(wallet.type);
|
||
|
||
bool _useTorOnly;
|
||
final Box<Trade> trades;
|
||
final ExchangeTemplateStore _exchangeTemplateStore;
|
||
final TradesStore tradesStore;
|
||
final SharedPreferences sharedPreferences;
|
||
|
||
List<ExchangeProvider> get _allProviders => [
|
||
ChangeNowExchangeProvider(settingsStore: _settingsStore),
|
||
// SideShiftExchangeProvider(),
|
||
ChainflipExchangeProvider(tradesStore: trades),
|
||
if (FeatureFlag.isExolixEnabled) ExolixExchangeProvider(),
|
||
SwapTradeExchangeProvider(),
|
||
LetsExchangeExchangeProvider(),
|
||
StealthExExchangeProvider(),
|
||
XOSwapExchangeProvider(),
|
||
SwapsXyzExchangeProvider(),
|
||
JupiterExchangeProvider(),
|
||
NearIntentsExchangeProvider(),
|
||
TrocadorExchangeProvider(
|
||
useTorOnly: _useTorOnly, providerStates: _settingsStore.trocadorProviderStates),
|
||
];
|
||
|
||
@observable
|
||
ExchangeProvider? provider;
|
||
|
||
@observable
|
||
ExchangeProvider? providerDisplay;
|
||
|
||
/// Maps in dart are not sorted by default
|
||
/// SplayTreeMap is a map sorted by keys
|
||
/// will use it to sort available providers
|
||
/// based on the rate they yield for the current trade
|
||
///
|
||
///
|
||
/// initialize with descending comparator
|
||
/// since we want largest rate first
|
||
final SplayTreeMap<double, ExchangeProvider> _sortedAvailableProviders =
|
||
SplayTreeMap<double, ExchangeProvider>((double a, double b) => b.compareTo(a));
|
||
|
||
final List<ExchangeProvider> _tradeAvailableProviders = [];
|
||
|
||
Map<ExchangeProvider, Limits> _providerLimits = {};
|
||
|
||
@observable
|
||
ObservableList<ExchangeProvider> selectedProviders;
|
||
|
||
@observable
|
||
List<ExchangeProvider> providerList;
|
||
|
||
@observable
|
||
CryptoCurrency depositCurrency;
|
||
|
||
@observable
|
||
CryptoCurrency receiveCurrency;
|
||
|
||
@observable
|
||
LimitsState limitsState;
|
||
|
||
@observable
|
||
ExchangeTradeState tradeState;
|
||
|
||
@observable
|
||
String _depositAmount;
|
||
|
||
@computed
|
||
String get depositAmount =>
|
||
_depositAmount == "..."? _depositAmount:amountParsingProxy.getDisplayCryptoAmount(_depositAmount, depositCurrency);
|
||
|
||
@computed
|
||
String get depositAmountCanonical => _depositAmount;
|
||
|
||
@observable
|
||
String _receiveAmount;
|
||
|
||
@computed
|
||
String get receiveAmount =>
|
||
_receiveAmount == "..." ? _receiveAmount:amountParsingProxy.getDisplayCryptoAmount(_receiveAmount, receiveCurrency);
|
||
|
||
@action
|
||
// only set canonical formated amounts here;
|
||
void setCanonicalReceiveAmount(String value) => _receiveAmount = value;
|
||
|
||
@observable
|
||
String depositAddress;
|
||
|
||
@observable
|
||
String receiveAddress;
|
||
|
||
@observable
|
||
String? receiveAddressDisplayName;
|
||
|
||
@observable
|
||
bool isDepositAddressEnabled;
|
||
|
||
@observable
|
||
bool isReceiveAmountEntered;
|
||
|
||
@observable
|
||
bool isReceiveAmountEditable;
|
||
|
||
@observable
|
||
bool isFixedRateMode;
|
||
|
||
@observable
|
||
bool isSendAllEnabled;
|
||
|
||
@observable
|
||
Limits limits;
|
||
|
||
@observable
|
||
bool tradeStarted = false;
|
||
|
||
@observable
|
||
bool isSendFromExternal = false;
|
||
|
||
@computed
|
||
SyncStatus get status => wallet.syncStatus;
|
||
|
||
@computed
|
||
ObservableList<ExchangeTemplate> get templates => _exchangeTemplateStore.templates;
|
||
|
||
@computed
|
||
List<WalletContact> get walletContactsToShow => contactListViewModel.walletContacts
|
||
.where((element) => element.type == receiveCurrency)
|
||
.toList();
|
||
|
||
@computed
|
||
List<WalletContact> get refundWalletContactsToShow => contactListViewModel.walletContacts
|
||
.where((element) => element.type == depositCurrency)
|
||
.toList();
|
||
|
||
@computed
|
||
Future<List<WalletInfo>> get receiveWallets async {
|
||
WalletType? type;
|
||
type = cryptoCurrencyOrTokenToWalletType(receiveCurrency);
|
||
if (type == null) {
|
||
type = cryptoCurrencyOrTokenToWalletType(CryptoCurrency.fromString(receiveCurrency.tag ?? ""));
|
||
}
|
||
|
||
return await WalletInfo.selectList("type = ?", [type!.index]);
|
||
}
|
||
|
||
Future<List<WalletInfoAddressInfo>> addressesForWallet(WalletInfo wallet) async {
|
||
final List<WalletInfoAddressInfo> ret = [];
|
||
final addresses = await wallet.getAddressInfos();
|
||
for (var list in addresses.values) {
|
||
// we only want the "primary" account addresses - those that contain account names.
|
||
ret.addAll(list.where((item) => item.label.split(" ").length > 1));
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
@computed
|
||
Future<List<WalletInfo>> get depositWallets async {
|
||
WalletType? type;
|
||
type = cryptoCurrencyOrTokenToWalletType(depositCurrency);
|
||
if (type == null) {
|
||
try {
|
||
type = cryptoCurrencyOrTokenToWalletType(CryptoCurrency.fromString(depositCurrency.tag ?? ""));
|
||
} catch (_) {}
|
||
}
|
||
|
||
if (type != null) {
|
||
return await WalletInfo.selectList("type = ?", [type.index]);
|
||
}
|
||
|
||
return await WalletInfo.getAll();
|
||
}
|
||
|
||
@action
|
||
bool checkIfWalletIsAnInternalWallet(String address) {
|
||
final walletContactList =
|
||
walletContactsToShow.where((element) => element.address == address).toList();
|
||
|
||
return walletContactList.isNotEmpty;
|
||
}
|
||
|
||
@computed
|
||
bool get shouldDisplayTOTP2FAForExchangesToInternalWallet =>
|
||
_settingsStore.shouldRequireTOTP2FAForExchangesToInternalWallets;
|
||
|
||
@computed
|
||
bool get shouldDisplayTOTP2FAForExchangesToExternalWallet =>
|
||
_settingsStore.shouldRequireTOTP2FAForExchangesToExternalWallets;
|
||
|
||
@computed
|
||
String? get balanceDisplay {
|
||
final bal = wallet.balance[depositCurrency]?.fullAvailableBalance;
|
||
if(bal == null) return null;
|
||
return amountParsingProxy.getDisplayCryptoStringFromBigInt(bal, depositCurrency);
|
||
}
|
||
|
||
//* Still open to further optimize these checks
|
||
//* It works but can be made better
|
||
@action
|
||
bool shouldDisplayTOTP() {
|
||
final isInternalWallet = checkIfWalletIsAnInternalWallet(receiveAddress);
|
||
|
||
if (isInternalWallet) {
|
||
return shouldDisplayTOTP2FAForExchangesToInternalWallet;
|
||
} else {
|
||
return shouldDisplayTOTP2FAForExchangesToExternalWallet;
|
||
}
|
||
}
|
||
|
||
@computed
|
||
bool get decentralizedExchangesPromptDismissed => _settingsStore.decentralizedExchangesPromptDismissed;
|
||
|
||
@action
|
||
void dismissDecentralizedExchangesPrompt() {
|
||
_settingsStore.decentralizedExchangesPromptDismissed = true;
|
||
}
|
||
|
||
@computed
|
||
TransactionPriority get transactionPriority {
|
||
final priority = _settingsStore.getPriority(wallet.type, chainId: wallet.chainId);
|
||
|
||
if (priority == null) {
|
||
throw Exception('Unexpected type ${wallet.type.toString()}');
|
||
}
|
||
|
||
return priority;
|
||
}
|
||
|
||
bool get hasAllAmount =>
|
||
[
|
||
WalletType.monero,
|
||
WalletType.bitcoin,
|
||
WalletType.litecoin,
|
||
WalletType.bitcoinCash,
|
||
WalletType.dogecoin,
|
||
].contains(wallet.type) &&
|
||
(depositCurrency == wallet.currency);
|
||
|
||
bool get isMoneroWallet => wallet.type == WalletType.monero;
|
||
|
||
@observable
|
||
ObservableList<CryptoCurrency> receiveCurrencies;
|
||
|
||
@observable
|
||
ObservableList<CryptoCurrency> depositCurrencies;
|
||
|
||
final AppStore _appStore;
|
||
SettingsStore get _settingsStore => _appStore.settingsStore;
|
||
|
||
final ContactListViewModel contactListViewModel;
|
||
|
||
final UnspentCoinsListViewModel unspentCoinsListViewModel;
|
||
|
||
final FeesViewModel feesViewModel;
|
||
|
||
@observable
|
||
double bestRate = 0.0;
|
||
|
||
@computed
|
||
bool get useSatoshiDeposit => _appStore.amountParsingProxy.useSatoshi(depositCurrency);
|
||
|
||
@computed
|
||
bool get useSatoshisReceive => _appStore.amountParsingProxy.useSatoshi(receiveCurrency);
|
||
|
||
@computed
|
||
AmountParsingProxy get amountParsingProxy => _appStore.amountParsingProxy;
|
||
|
||
@observable
|
||
ExchangeProvider? bestRateProvider;
|
||
|
||
@observable
|
||
ExchangeProvider? forcedProvider;
|
||
|
||
@action
|
||
void setForcedProvider(ExchangeProvider? provider) {
|
||
forcedProvider = provider;
|
||
forcedProviderRate = 0.0;
|
||
calculateForcedProviderRate();
|
||
}
|
||
|
||
@observable
|
||
double forcedProviderRate = 0.0;
|
||
|
||
late Timer bestRateSync;
|
||
|
||
final FiatConversionStore fiatConversionStore;
|
||
|
||
FiatCurrency get fiat => _settingsStore.fiatCurrency;
|
||
|
||
@computed
|
||
bool get isFiatDisabled => feesViewModel.isFiatDisabled;
|
||
|
||
@action
|
||
Future<void> fetchFiatPrice(CryptoCurrency currency) async {
|
||
if (fiatConversionStore.prices[currency] != null) {
|
||
return;
|
||
}
|
||
|
||
fiatConversionStore.prices[currency] = await FiatConversionService.fetchPrice(
|
||
crypto: currency,
|
||
fiat: fiat,
|
||
torOnly: _settingsStore.fiatApiMode == FiatApiMode.torOnly,
|
||
);
|
||
}
|
||
|
||
@computed
|
||
String get receiveAmountFiatFormatted {
|
||
var amount = '0.00';
|
||
try {
|
||
if (_receiveAmount.isNotEmpty) {
|
||
if (fiatConversionStore.prices[receiveCurrency] == null) return '';
|
||
|
||
amount = calculateFiatAmount(
|
||
price: fiatConversionStore.prices[receiveCurrency]!,
|
||
cryptoAmount: _receiveAmount,
|
||
);
|
||
}
|
||
} catch (_) {
|
||
log('Error calculating receive amount fiat formatted: $_');
|
||
}
|
||
return isFiatDisabled ? '' : '$amount';
|
||
}
|
||
|
||
@computed
|
||
String get depositAmountFiatFormatted {
|
||
var amount = '0.00';
|
||
try {
|
||
if (_depositAmount.isNotEmpty) {
|
||
if (fiatConversionStore.prices[depositCurrency] == null) return '';
|
||
|
||
amount = calculateFiatAmount(
|
||
price: fiatConversionStore.prices[depositCurrency]!,
|
||
cryptoAmount: _depositAmount,
|
||
);
|
||
}
|
||
} catch (_) {
|
||
log('Error calculating deposit amount fiat formatted: $_');
|
||
}
|
||
return isFiatDisabled ? '' : '$amount';
|
||
}
|
||
|
||
@computed
|
||
String get receiveAmountFiat {
|
||
var amount = '';
|
||
try {
|
||
if (_receiveAmount.isNotEmpty) {
|
||
if (fiatConversionStore.prices[receiveCurrency] == null) return '';
|
||
|
||
amount = calculateFiatAmount(
|
||
price: fiatConversionStore.prices[receiveCurrency]!,
|
||
cryptoAmount: _receiveAmount,
|
||
raw: true);
|
||
}
|
||
} catch (_) {
|
||
log('Error calculating receive amount fiat formatted: $_');
|
||
}
|
||
return amount;
|
||
}
|
||
|
||
@computed
|
||
String get depositAmountFiat {
|
||
var amount = '';
|
||
try {
|
||
if (_depositAmount.isNotEmpty) {
|
||
if (fiatConversionStore.prices[depositCurrency] == null) return '';
|
||
|
||
amount = calculateFiatAmount(
|
||
price: fiatConversionStore.prices[depositCurrency]!,
|
||
cryptoAmount: _depositAmount,
|
||
raw: true);
|
||
}
|
||
} catch (_) {
|
||
log('Error calculating deposit amount fiat formatted: $_');
|
||
}
|
||
return amount;
|
||
}
|
||
|
||
String roundedDepositAmount(int digits) {
|
||
if (depositAmount.split(".").last.length <= digits) {
|
||
return depositAmount;
|
||
}
|
||
try {
|
||
return double.parse(depositAmount).toStringAsPrecision(digits);
|
||
} catch(e) {
|
||
return "0";
|
||
}
|
||
}
|
||
|
||
String roundedReceiveAmount(int digits) {
|
||
if (receiveAmount.split(".").last.length <= digits) {
|
||
return receiveAmount;
|
||
}
|
||
try {
|
||
return double.parse(receiveAmount).toStringAsPrecision(digits);
|
||
} catch(e) {
|
||
return "0";
|
||
}
|
||
}
|
||
|
||
String roundedReceiveAmountFiat(int digits) {
|
||
if (receiveAmountFiat.split(".").last.length <= digits) {
|
||
return receiveAmountFiat;
|
||
}
|
||
return double.parse(receiveAmountFiat).toStringAsPrecision(digits);
|
||
}
|
||
|
||
String roundedDepositAmountFiat(int digits) {
|
||
if (depositAmountFiat.split(".").last.length <= digits) {
|
||
return depositAmountFiat;
|
||
}
|
||
return double.parse(depositAmountFiat).toStringAsPrecision(digits);
|
||
}
|
||
|
||
@action
|
||
void changeDepositCurrency({required CryptoCurrency currency}) {
|
||
depositCurrency = currency;
|
||
isFixedRateMode = false;
|
||
_onPairChange();
|
||
isDepositAddressEnabled = !useSameWalletAddress(depositCurrency);
|
||
fetchFiatPrice(currency);
|
||
updateDepositAvailableAmount();
|
||
}
|
||
|
||
@action
|
||
void changeReceiveCurrency({required CryptoCurrency currency}) {
|
||
if (!(currency.tag == receiveCurrency.tag ||
|
||
currency.tag == receiveCurrency.title ||
|
||
currency.title == receiveCurrency.tag ||
|
||
currency == receiveCurrency)) {
|
||
receiveAddress = "";
|
||
}
|
||
receiveCurrency = currency;
|
||
isFixedRateMode = false;
|
||
_onPairChange();
|
||
isDepositAddressEnabled = !useSameWalletAddress(depositCurrency);
|
||
fetchFiatPrice(currency);
|
||
}
|
||
|
||
@action
|
||
Future<void> changeReceiveAmount({required String amount}) async {
|
||
amount = amountParsingProxy.getCanonicalCryptoAmount(amount, receiveCurrency);
|
||
|
||
_receiveAmount = amount;
|
||
|
||
if (amount.isEmpty) {
|
||
_depositAmount = '';
|
||
_receiveAmount = '';
|
||
return;
|
||
}
|
||
|
||
final _enteredAmount = double.tryParse(_receiveAmount.replaceAll(',', '.')) ?? 0;
|
||
|
||
if (bestRate == 0) {
|
||
_depositAmount = "...";
|
||
|
||
await calculateBestRate();
|
||
}
|
||
|
||
final amount_ = _enteredAmount / (forcedProvider == null ? bestRate : forcedProviderRate);
|
||
_depositAmount = amount_.toString().withMaxDecimals(depositCurrency.decimals);
|
||
}
|
||
|
||
@action
|
||
void setReceiveAmountFromFiat({required String fiatAmount}) {
|
||
final _enteredAmount = double.tryParse(fiatAmount.replaceAll(',', '.')) ?? 0.0;
|
||
final price = fiatConversionStore.prices[receiveCurrency];
|
||
if (price == null || price == 0.0) return;
|
||
|
||
final crypto = _enteredAmount / price;
|
||
final _receiveAmountTmp = crypto.toString().withMaxDecimals(receiveCurrency.decimals);
|
||
if (_receiveAmount != _receiveAmountTmp) {
|
||
changeReceiveAmount(amount: _receiveAmountTmp);
|
||
}
|
||
}
|
||
|
||
@action
|
||
void setDepositAmountFromFiat({required String fiatAmount}) {
|
||
final _enteredAmount = double.tryParse(fiatAmount.replaceAll(',', '.')) ?? 0.0;
|
||
final price = fiatConversionStore.prices[depositCurrency];
|
||
if (price == null || price == 0.0) return;
|
||
|
||
final crypto = _enteredAmount / price;
|
||
final depositAmountTmp = crypto.toString().withMaxDecimals(receiveCurrency.decimals);
|
||
if (_depositAmount != depositAmountTmp) {
|
||
changeDepositAmount(amount: depositAmountTmp, isCanonical: true);
|
||
}
|
||
}
|
||
|
||
@action
|
||
Future<void> changeDepositAmount({required String amount, bool isCanonical = false}) async {
|
||
_depositAmount = isCanonical ? amount.replaceAll(',', '.') : _appStore.amountParsingProxy
|
||
.getCanonicalCryptoAmount(amount.replaceAll(',', '.'), depositCurrency);
|
||
|
||
if (amount.isEmpty) {
|
||
_depositAmount = '';
|
||
_receiveAmount = '';
|
||
return;
|
||
}
|
||
|
||
/// For fixed-rate transactions, we don't want to recalculate receive amount
|
||
/// as it should remain exactly what the user set
|
||
if (isFixedRateMode) return;
|
||
|
||
final _enteredAmount = double.tryParse(_depositAmount) ?? 0;
|
||
|
||
/// in case the best rate was not calculated yet
|
||
if (bestRate == 0) {
|
||
_receiveAmount = "...";
|
||
|
||
await calculateBestRate();
|
||
}
|
||
|
||
final amount_ = _enteredAmount * (forcedProvider == null ? bestRate : forcedProviderRate);
|
||
_receiveAmount = amount_.toString().withMaxDecimals(receiveCurrency.decimals);
|
||
}
|
||
|
||
bool checkIfInputMeetsMinOrMaxCondition(String input) {
|
||
final _enteredAmount = double.tryParse(input.replaceAll(',', '.')) ?? 0;
|
||
double minLimit = limits.min ?? 0;
|
||
double? maxLimit = limits.max;
|
||
|
||
if (_enteredAmount < minLimit) return false;
|
||
|
||
if (maxLimit != null && _enteredAmount > maxLimit) return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
Future<void> calculateForcedProviderRate() async {
|
||
if (forcedProvider == null || depositCurrency == receiveCurrency) {
|
||
forcedProviderRate = 0.0;
|
||
return;
|
||
}
|
||
|
||
final amount = double.tryParse(isFixedRateMode ? _receiveAmount : _depositAmount) ??
|
||
initialAmountByAssets(isFixedRateMode ? receiveCurrency : depositCurrency);
|
||
|
||
forcedProviderRate = await forcedProvider!.fetchRate(
|
||
from: depositCurrency,
|
||
to: receiveCurrency,
|
||
amount: amount,
|
||
isFixedRateMode: isFixedRateMode,
|
||
isReceiveAmount: isFixedRateMode);
|
||
}
|
||
|
||
Future<void> calculateBestRate() async {
|
||
if (depositCurrency == receiveCurrency) {
|
||
bestRate = 0.0;
|
||
bestRateProvider = null;
|
||
return;
|
||
}
|
||
final amount = double.tryParse(isFixedRateMode ? _receiveAmount : _depositAmount) ??
|
||
initialAmountByAssets(isFixedRateMode ? receiveCurrency : depositCurrency);
|
||
|
||
final validProvidersForAmount = _tradeAvailableProviders.where((provider) {
|
||
final limits = _providerLimits[provider];
|
||
|
||
if (limits == null) return false;
|
||
if (limits.min != null && amount < limits.min!) return false;
|
||
if (limits.max != null && amount > limits.max!) return false;
|
||
|
||
return true;
|
||
}).toList();
|
||
|
||
final _providers = validProvidersForAmount
|
||
.where((element) => !isFixedRateMode || element.supportsFixedRate)
|
||
.toList();
|
||
|
||
final result = await Future.wait<double>(
|
||
_providers.map(
|
||
(element) => element
|
||
.fetchRate(
|
||
from: depositCurrency,
|
||
to: receiveCurrency,
|
||
amount: amount,
|
||
isFixedRateMode: isFixedRateMode,
|
||
isReceiveAmount: isFixedRateMode)
|
||
.timeout(
|
||
Duration(seconds: 7),
|
||
onTimeout: () => 0.0,
|
||
),
|
||
),
|
||
);
|
||
|
||
// We'll use a new SplayTreeMap to avoid concurrent modification issues
|
||
final newSortedProviders =
|
||
SplayTreeMap<double, ExchangeProvider>((double a, double b) => b.compareTo(a));
|
||
|
||
for (int i = 0; i < result.length; i++) {
|
||
if (result[i] != 0) {
|
||
/// add this provider as its valid for this trade
|
||
try {
|
||
newSortedProviders[result[i]] = _providers[i];
|
||
} catch (e) {
|
||
// will throw "Concurrent modification during iteration" error if modified at the same
|
||
// time [createTrade] is called, as this is not a normal map, but a sorted map
|
||
}
|
||
}
|
||
}
|
||
|
||
// Replace the old map with the new one
|
||
_sortedAvailableProviders.clear();
|
||
_sortedAvailableProviders.addAll(newSortedProviders);
|
||
|
||
if (_sortedAvailableProviders.isNotEmpty) {
|
||
bestRate = _sortedAvailableProviders.keys.first;
|
||
bestRateProvider = _sortedAvailableProviders.values.first;
|
||
}
|
||
}
|
||
|
||
@action
|
||
Future<void> loadLimits() async {
|
||
if (depositCurrency == receiveCurrency) {
|
||
limitsState = LimitsLoadedSuccessfully(limits: Limits(min: 0, max: 0));
|
||
return;
|
||
}
|
||
if (selectedProviders.isEmpty) return;
|
||
|
||
limitsState = LimitsIsLoading();
|
||
|
||
final from = isFixedRateMode ? receiveCurrency : depositCurrency;
|
||
final to = isFixedRateMode ? depositCurrency : receiveCurrency;
|
||
|
||
double? lowestMin = double.maxFinite;
|
||
double? highestMax = 0.0;
|
||
|
||
try {
|
||
final futures = selectedProviders
|
||
.where((provider) => providerList.contains(provider))
|
||
.map((provider) async {
|
||
final limits = await provider
|
||
.fetchLimits(
|
||
from: from,
|
||
to: to,
|
||
isFixedRateMode: isFixedRateMode,
|
||
)
|
||
.onError((error, stackTrace) => Limits(max: 0.0, min: double.maxFinite))
|
||
.timeout(
|
||
Duration(seconds: 7),
|
||
onTimeout: () => Limits(max: 0.0, min: double.maxFinite),
|
||
);
|
||
return MapEntry(provider, limits);
|
||
}).toList();
|
||
|
||
final entries = await Future.wait(futures);
|
||
_providerLimits = Map.fromEntries(entries);
|
||
|
||
_providerLimits.values.forEach((tempLimits) {
|
||
if (lowestMin != null && (tempLimits.min ?? -1) < lowestMin!) {
|
||
lowestMin = tempLimits.min;
|
||
}
|
||
|
||
if (highestMax != null && (tempLimits.max ?? double.maxFinite) > highestMax!) {
|
||
highestMax = tempLimits.max;
|
||
}
|
||
});
|
||
} on ConcurrentModificationError {
|
||
/// if user changed the selected providers while fetching limits
|
||
/// then delay the fetching limits a bit and try again
|
||
///
|
||
/// this is because the limitation of collections that
|
||
/// you can't modify it while iterating through it
|
||
Future.delayed(Duration(milliseconds: 200), loadLimits);
|
||
}
|
||
|
||
if (lowestMin != double.maxFinite) {
|
||
limits = Limits(min: lowestMin, max: highestMax);
|
||
|
||
limitsState = LimitsLoadedSuccessfully(limits: limits);
|
||
} else {
|
||
limitsState = LimitsLoadedFailure(error: 'Limits loading failed');
|
||
}
|
||
|
||
calculateBestRate();
|
||
}
|
||
|
||
@action
|
||
Future<void> createTrade() async {
|
||
if (isSendAllEnabled) {
|
||
await calculateDepositAllAmount();
|
||
final amount = double.tryParse(_depositAmount);
|
||
|
||
if (limits.min != null && amount != null && amount < limits.min!) {
|
||
tradeState = TradeIsCreatedFailure(
|
||
title: S.current.trade_not_created,
|
||
error: S.current.amount_is_below_minimum_limit(limits.min!.toString()));
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (depositCurrency == CryptoCurrency.btcln &&
|
||
depositAddress == wallet.walletAddresses.addressForExchange) {
|
||
final invoice = await bitcoin!.getLightningInvoice(wallet, BigInt.zero);
|
||
if (invoice != null) {
|
||
depositAddress = invoice;
|
||
}
|
||
}
|
||
|
||
if (receiveCurrency == CryptoCurrency.btcln &&
|
||
receiveAddress == wallet.walletAddresses.addressForExchange) {
|
||
final invoice = await bitcoin!.getLightningInvoice(wallet, BigInt.zero);
|
||
if (invoice != null) {
|
||
receiveAddress = invoice;
|
||
}
|
||
}
|
||
|
||
late final Map<double, ExchangeProvider> providers;
|
||
if (forcedProvider != null) {
|
||
providers = {forcedProviderRate: forcedProvider!};
|
||
} else {
|
||
providers = _sortedAvailableProviders;
|
||
}
|
||
|
||
// Ensure we have providers available before attempting to create trade
|
||
if (providers.isEmpty) {
|
||
await calculateBestRate();
|
||
|
||
if (providers.isEmpty) {
|
||
tradeState = TradeIsCreatedFailure(
|
||
title: S.current.trade_not_created,
|
||
error: S.current.none_of_selected_providers_can_exchange);
|
||
return;
|
||
}
|
||
}
|
||
|
||
try {
|
||
// snapshot of providers to avoid concurrent modification issues
|
||
final providersSnapshot = providers.values.toList();
|
||
final ratesSnapshot = providers.keys.toList();
|
||
|
||
for (var i = 0; i < providersSnapshot.length; i++) {
|
||
final provider = providersSnapshot[i];
|
||
final providerRate = ratesSnapshot[i];
|
||
|
||
if (!(await provider.checkIsAvailable())) continue;
|
||
|
||
bestRate = providerRate;
|
||
bestRateProvider = provider;
|
||
|
||
await changeDepositAmount(amount: _depositAmount, isCanonical: true);
|
||
|
||
final request = TradeRequest(
|
||
fromCurrency: depositCurrency,
|
||
toCurrency: receiveCurrency,
|
||
fromAmount: _depositAmount.replaceAll(',', '.'),
|
||
toAmount: _receiveAmount.replaceAll(',', '.'),
|
||
refundAddress: depositAddress,
|
||
toAddress: receiveAddress,
|
||
isFixedRate: isFixedRateMode,
|
||
);
|
||
|
||
if (hideAddressAfterExchange) {
|
||
wallet.walletAddresses.hiddenAddresses.add(depositAddress);
|
||
await wallet.walletAddresses.saveAddressesInBox();
|
||
}
|
||
|
||
var amount = isFixedRateMode ? _receiveAmount : _depositAmount;
|
||
amount = amount.replaceAll(',', '.');
|
||
|
||
if (limitsState is LimitsLoadedSuccessfully) {
|
||
if (double.tryParse(amount) == null) continue;
|
||
|
||
if (limits.min != null && double.parse(amount) < limits.min!) {
|
||
continue;
|
||
} else if (limits.max != null && double.parse(amount) > limits.max!) {
|
||
continue;
|
||
} else {
|
||
try {
|
||
tradeState = TradeIsCreating();
|
||
final trade = await provider.createTrade(
|
||
request: request,
|
||
isFixedRateMode: isFixedRateMode,
|
||
isSendAll: isSendAllEnabled,
|
||
);
|
||
trade.walletId = wallet.id;
|
||
trade.chainId = wallet.chainId;
|
||
trade.fromWalletAddress = wallet.walletAddresses.address;
|
||
|
||
final canCreateTrade = await isCanCreateTrade(trade);
|
||
if (!canCreateTrade.result) {
|
||
continue;
|
||
}
|
||
|
||
tradesStore.setTrade(trade);
|
||
if (trade.provider != ExchangeProviderDescription.thorChain) await trades.add(trade);
|
||
tradeState = TradeIsCreatedSuccessfully(trade: trade);
|
||
|
||
/// return after the first successful trade
|
||
return;
|
||
} catch (e) {
|
||
continue;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// if the code reached here then none of the providers succeeded
|
||
tradeState = TradeIsCreatedFailure(
|
||
title: S.current.trade_not_created,
|
||
error: S.current.none_of_selected_providers_can_exchange);
|
||
} on ConcurrentModificationError {
|
||
/// if create trade happened at the exact same time of the scheduled rate update
|
||
/// then delay the create trade a bit and try again
|
||
///
|
||
/// this is because the limitation of the SplayTreeMap that
|
||
/// you can't modify it while iterating through it
|
||
Future.delayed(Duration(milliseconds: 200), createTrade);
|
||
}
|
||
}
|
||
|
||
@action
|
||
void reset() {
|
||
_initialPairBasedOnWallet();
|
||
isReceiveAmountEntered = false;
|
||
_depositAmount = '';
|
||
_receiveAmount = '';
|
||
depositAddress =
|
||
depositCurrency == wallet.currency ? wallet.walletAddresses.addressForExchange : '';
|
||
receiveAddress =
|
||
receiveCurrency == wallet.currency ? wallet.walletAddresses.addressForExchange : '';
|
||
isDepositAddressEnabled = !(depositCurrency == wallet.currency);
|
||
isFixedRateMode = false;
|
||
_onPairChange();
|
||
}
|
||
|
||
@action
|
||
void enableSendAllAmount() {
|
||
isSendAllEnabled = true;
|
||
isFixedRateMode = false;
|
||
calculateDepositAllAmount();
|
||
}
|
||
|
||
@action
|
||
void enableFixedRateMode() {
|
||
isSendAllEnabled = false;
|
||
isFixedRateMode = true;
|
||
}
|
||
|
||
@action
|
||
Future<void> calculateDepositAllAmount() async {
|
||
if ([
|
||
WalletType.litecoin,
|
||
WalletType.bitcoin,
|
||
WalletType.bitcoinCash,
|
||
WalletType.dogecoin,
|
||
].contains(wallet.type)) {
|
||
final priority = _settingsStore.getPriority(wallet.type)!;
|
||
|
||
final amount = depositCurrency == CryptoCurrency.btcln
|
||
// FIXME amount estimation is broken/impossible for ln, konsti suggested this
|
||
? (amountParsingProxy
|
||
.parseCryptoString(depositAvailableAmount, depositCurrency).toInt() - 10)
|
||
: await bitcoin!.estimateFakeSendAllTxAmount(
|
||
wallet,
|
||
priority,
|
||
coinTypeToSpendFrom: wallet.type == WalletType.litecoin
|
||
? UnspentCoinType.nonMweb
|
||
: depositCurrency == CryptoCurrency.btcln
|
||
? UnspentCoinType.lightning
|
||
: UnspentCoinType.any,
|
||
);
|
||
|
||
changeDepositAmount(amount: wallet.currency.formatAmount(BigInt.from(amount)), isCanonical: true);
|
||
} else if (wallet.type == WalletType.monero) {
|
||
final amount = await unspentCoinsListViewModel.getSendingBalance(UnspentCoinType.any);
|
||
|
||
changeDepositAmount(amount: wallet.currency.formatAmount(BigInt.from(amount)), isCanonical: true);
|
||
}
|
||
}
|
||
|
||
@observable
|
||
String depositAvailableAmount = "";
|
||
|
||
@action
|
||
void reverseSwapDirection() {
|
||
final tmpAmount = _receiveAmount;
|
||
final tmpCurrency = depositCurrency;
|
||
changeDepositCurrency(currency: receiveCurrency);
|
||
changeReceiveCurrency(currency: tmpCurrency);
|
||
_depositAmount = tmpAmount;
|
||
}
|
||
|
||
void updateTemplate() => _exchangeTemplateStore.update();
|
||
|
||
void addTemplate(
|
||
{required String amount,
|
||
required String depositCurrency,
|
||
required String receiveCurrency,
|
||
required String provider,
|
||
required String depositAddress,
|
||
required String receiveAddress,
|
||
required String depositCurrencyTitle,
|
||
required String receiveCurrencyTitle}) =>
|
||
_exchangeTemplateStore.addTemplate(
|
||
amount: amount,
|
||
depositCurrency: depositCurrency,
|
||
receiveCurrency: receiveCurrency,
|
||
provider: provider,
|
||
depositAddress: depositAddress,
|
||
receiveAddress: receiveAddress,
|
||
depositCurrencyTitle: depositCurrencyTitle,
|
||
receiveCurrencyTitle: receiveCurrencyTitle);
|
||
|
||
void removeTemplate({required ExchangeTemplate template}) =>
|
||
_exchangeTemplateStore.remove(template: template);
|
||
|
||
void _onPairChange() {
|
||
_depositAmount = '';
|
||
_receiveAmount = '';
|
||
bestRate = 0.0;
|
||
bestRateProvider = null;
|
||
loadLimits();
|
||
_setAvailableProviders();
|
||
}
|
||
|
||
void _initialPairBasedOnWallet() {
|
||
if (isEVMCompatibleChain(wallet.type)) {
|
||
depositCurrency = wallet.currency;
|
||
receiveCurrency = CryptoCurrency.xmr;
|
||
return;
|
||
}
|
||
|
||
switch (wallet.type) {
|
||
case WalletType.monero:
|
||
depositCurrency = CryptoCurrency.xmr;
|
||
receiveCurrency = CryptoCurrency.btc;
|
||
break;
|
||
case WalletType.bitcoin:
|
||
depositCurrency = CryptoCurrency.btc;
|
||
receiveCurrency = CryptoCurrency.xmr;
|
||
break;
|
||
case WalletType.litecoin:
|
||
depositCurrency = CryptoCurrency.ltc;
|
||
receiveCurrency = CryptoCurrency.xmr;
|
||
break;
|
||
case WalletType.bitcoinCash:
|
||
depositCurrency = CryptoCurrency.bch;
|
||
receiveCurrency = CryptoCurrency.xmr;
|
||
break;
|
||
case WalletType.dogecoin:
|
||
depositCurrency = CryptoCurrency.doge;
|
||
receiveCurrency = CryptoCurrency.xmr;
|
||
break;
|
||
case WalletType.haven:
|
||
depositCurrency = CryptoCurrency.xhv;
|
||
receiveCurrency = CryptoCurrency.btc;
|
||
break;
|
||
case WalletType.ethereum:
|
||
depositCurrency = CryptoCurrency.eth;
|
||
receiveCurrency = CryptoCurrency.xmr;
|
||
break;
|
||
case WalletType.nano:
|
||
depositCurrency = CryptoCurrency.nano;
|
||
receiveCurrency = CryptoCurrency.xmr;
|
||
break;
|
||
case WalletType.banano:
|
||
depositCurrency = CryptoCurrency.banano;
|
||
receiveCurrency = CryptoCurrency.xmr;
|
||
break;
|
||
case WalletType.polygon:
|
||
depositCurrency = CryptoCurrency.maticpoly;
|
||
receiveCurrency = CryptoCurrency.xmr;
|
||
break;
|
||
case WalletType.base:
|
||
depositCurrency = CryptoCurrency.baseEth;
|
||
receiveCurrency = CryptoCurrency.xmr;
|
||
break;
|
||
case WalletType.arbitrum:
|
||
depositCurrency = CryptoCurrency.arbEth;
|
||
receiveCurrency = CryptoCurrency.xmr;
|
||
break;
|
||
case WalletType.bsc:
|
||
depositCurrency = CryptoCurrency.bnb;
|
||
receiveCurrency = CryptoCurrency.xmr;
|
||
break;
|
||
case WalletType.solana:
|
||
depositCurrency = CryptoCurrency.sol;
|
||
receiveCurrency = CryptoCurrency.xmr;
|
||
break;
|
||
case WalletType.tron:
|
||
depositCurrency = CryptoCurrency.trx;
|
||
receiveCurrency = CryptoCurrency.xmr;
|
||
break;
|
||
case WalletType.wownero:
|
||
depositCurrency = CryptoCurrency.wow;
|
||
receiveCurrency = CryptoCurrency.xmr;
|
||
break;
|
||
case WalletType.zano:
|
||
depositCurrency = CryptoCurrency.zano;
|
||
receiveCurrency = CryptoCurrency.xmr;
|
||
break;
|
||
case WalletType.decred:
|
||
depositCurrency = CryptoCurrency.dcr;
|
||
receiveCurrency = CryptoCurrency.xmr;
|
||
break;
|
||
case WalletType.zcash:
|
||
depositCurrency = CryptoCurrency.zec;
|
||
receiveCurrency = CryptoCurrency.xmr;
|
||
break;
|
||
case WalletType.none:
|
||
break;
|
||
}
|
||
}
|
||
|
||
void _defineIsReceiveAmountEditable() {
|
||
/*if ((provider is ChangeNowExchangeProvider)
|
||
&&(depositCurrency == CryptoCurrency.xmr)
|
||
&&(receiveCurrency == CryptoCurrency.btc)) {
|
||
isReceiveAmountEditable = true;
|
||
} else {
|
||
isReceiveAmountEditable = false;
|
||
}*/
|
||
//isReceiveAmountEditable = false;
|
||
// isReceiveAmountEditable = selectedProviders.any((provider) => provider is ChangeNowExchangeProvider);
|
||
// isReceiveAmountEditable = provider is ChangeNowExchangeProvider || provider is SimpleSwapExchangeProvider;
|
||
isReceiveAmountEditable = true;
|
||
}
|
||
|
||
@action
|
||
void addExchangeProvider(ExchangeProvider provider) {
|
||
if (provider.description.isCentralized && forceDecentralizedExchanges) return;
|
||
if (!selectedProviders.contains(provider)) selectedProviders.add(provider);
|
||
if (providerList.contains(provider)) _tradeAvailableProviders.add(provider);
|
||
}
|
||
|
||
@action
|
||
void removeExchangeProvider(ExchangeProvider provider) {
|
||
selectedProviders.remove(provider);
|
||
_tradeAvailableProviders.remove(provider);
|
||
if (forcedProvider == provider) {
|
||
setForcedProvider(null);
|
||
}
|
||
}
|
||
|
||
@action
|
||
void saveSelectedProviders() {
|
||
_depositAmount = '';
|
||
_receiveAmount = '';
|
||
isFixedRateMode = false;
|
||
_defineIsReceiveAmountEditable();
|
||
bestRateProvider = null;
|
||
bestRate = 0.0;
|
||
loadLimits();
|
||
|
||
final Map<String, dynamic> exchangeProvidersSelection =
|
||
json.decode(sharedPreferences.getString(PreferencesKey.exchangeProvidersSelection) ?? "{}")
|
||
as Map<String, dynamic>;
|
||
|
||
for (var provider in providerList) {
|
||
exchangeProvidersSelection[provider.title] = selectedProviders.contains(provider);
|
||
}
|
||
|
||
sharedPreferences.setString(
|
||
PreferencesKey.exchangeProvidersSelection,
|
||
json.encode(exchangeProvidersSelection),
|
||
);
|
||
}
|
||
|
||
@action
|
||
Future<void> updateAllTrocadorProviderStates(TrocadorExchangeProvider trocadorProvider) async {
|
||
try {
|
||
var providers = await trocadorProvider.fetchProviders();
|
||
var providerNames = providers.map((e) => e.name).toList();
|
||
await _settingsStore.updateAllTrocadorProviderStates(providerNames);
|
||
} catch (e) {
|
||
printV('Error updating trocador provider states: $e');
|
||
}
|
||
}
|
||
|
||
bool get isAvailableInSelected {
|
||
return selectedProviders
|
||
.any((element) => element.isAvailable && providerList.contains(element));
|
||
}
|
||
|
||
@computed
|
||
bool get forceDecentralizedExchanges => _settingsStore.forceDecentralizedExchanges;
|
||
|
||
@action
|
||
void toggleForceDecentralizedExchanges() {
|
||
_settingsStore.forceDecentralizedExchanges = !_settingsStore.forceDecentralizedExchanges;
|
||
if(forceDecentralizedExchanges) {
|
||
final providers = selectedProviders.toList();
|
||
for (final provider in providers) {
|
||
if (forceDecentralizedExchanges && provider.description.isCentralized) {
|
||
removeExchangeProvider(provider);
|
||
}
|
||
}
|
||
} else {
|
||
for(final provider in providerList) {
|
||
if(!selectedProviders.contains(provider) && provider.description.isCentralized) {
|
||
addExchangeProvider(provider);
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
void _setAvailableProviders() {
|
||
_tradeAvailableProviders.clear();
|
||
|
||
_tradeAvailableProviders
|
||
.addAll(selectedProviders.where((provider) => providerList.contains(provider)));
|
||
}
|
||
|
||
void _setProviders() {
|
||
if (_settingsStore.exchangeStatus == ExchangeApiMode.torOnly)
|
||
providerList = _allProviders.where((provider) => provider.supportsOnionAddress).toList();
|
||
else
|
||
providerList = _allProviders;
|
||
}
|
||
|
||
int get depositMaxDigits => depositCurrency.decimals;
|
||
|
||
int get receiveMaxDigits => receiveCurrency.decimals;
|
||
|
||
Future<CreateTradeResult> isCanCreateTrade(Trade trade) async {
|
||
|
||
if (trade.provider == ExchangeProviderDescription.swapsXyz) {
|
||
|
||
final tradeFrom = trade.fromRaw >= 0 ? trade.from : trade.userCurrencyFrom;
|
||
|
||
if (tradeFrom == null) {
|
||
return CreateTradeResult(
|
||
result: false,
|
||
errorMessage: 'From currency is null',
|
||
);
|
||
}
|
||
|
||
final isNativeSupportedToken = walletTypes.contains(cryptoCurrencyOrTokenToWalletType(tradeFrom));
|
||
|
||
if (!isNativeSupportedToken) {
|
||
bool _isEthToken() =>
|
||
wallet.currency == CryptoCurrency.eth && tradeFrom.tag == CryptoCurrency.eth.title;
|
||
|
||
bool _isPolygonToken() =>
|
||
wallet.currency == CryptoCurrency.maticpoly &&
|
||
tradeFrom.tag == CryptoCurrency.maticpoly.tag;
|
||
|
||
bool _isBaseToken() =>
|
||
wallet.currency == CryptoCurrency.baseEth && tradeFrom.tag == CryptoCurrency.baseEth.tag;
|
||
|
||
bool _isTronToken() =>
|
||
wallet.currency == CryptoCurrency.trx && tradeFrom.tag == CryptoCurrency.trx.title;
|
||
|
||
bool _isSplToken() =>
|
||
wallet.currency == CryptoCurrency.sol && tradeFrom.tag == CryptoCurrency.sol.title;
|
||
|
||
bool isArbitrumToken() =>
|
||
wallet.currency == CryptoCurrency.arbEth && tradeFrom.tag == CryptoCurrency.arbEth.tag;
|
||
|
||
bool isBscToken() =>
|
||
wallet.currency == CryptoCurrency.bnb && tradeFrom.tag == CryptoCurrency.bnb.tag;
|
||
|
||
if(!(_isEthToken() || _isPolygonToken() || _isBaseToken() || _isTronToken() || _isSplToken() || isArbitrumToken() || isBscToken())) {
|
||
return CreateTradeResult(
|
||
result: false,
|
||
errorMessage: 'This token isn’t supported on the current wallet/network for Swaps.xyz. Switch to a supported wallet or asset',
|
||
);
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
if (trade.provider == ExchangeProviderDescription.thorChain) {
|
||
final payoutAddress = trade.payoutAddress ?? '';
|
||
final fromWalletAddress = trade.fromWalletAddress ?? '';
|
||
final tapRootPattern = RegExp(P2trAddress.regex.pattern);
|
||
|
||
if (tapRootPattern.hasMatch(payoutAddress) || tapRootPattern.hasMatch(fromWalletAddress)) {
|
||
return CreateTradeResult(
|
||
result: false,
|
||
errorMessage: S.current.thorchain_taproot_address_not_supported,
|
||
);
|
||
}
|
||
|
||
if ((trade.memo == null || trade.memo!.isEmpty)) {
|
||
return CreateTradeResult(
|
||
result: false,
|
||
errorMessage: 'Memo is required for Thorchain trade',
|
||
);
|
||
}
|
||
|
||
final currenciesToCheckPattern = RegExp('0x[0-9a-zA-Z]');
|
||
|
||
// Perform checks for payOutAddress
|
||
final isPayOutAddressAccordingToPattern = currenciesToCheckPattern.hasMatch(payoutAddress);
|
||
|
||
if (isPayOutAddressAccordingToPattern) {
|
||
final isPayOutAddressEOA = await _isExternallyOwnedAccountAddress(payoutAddress);
|
||
|
||
return CreateTradeResult(
|
||
result: isPayOutAddressEOA,
|
||
errorMessage:
|
||
!isPayOutAddressEOA ? S.current.thorchain_contract_address_not_supported : null,
|
||
);
|
||
}
|
||
|
||
// Perform checks for fromWalletAddress
|
||
final isFromWalletAddressAddressAccordingToPattern =
|
||
currenciesToCheckPattern.hasMatch(fromWalletAddress);
|
||
|
||
if (isFromWalletAddressAddressAccordingToPattern) {
|
||
final isFromWalletAddressEOA = await _isExternallyOwnedAccountAddress(fromWalletAddress);
|
||
|
||
return CreateTradeResult(
|
||
result: isFromWalletAddressEOA,
|
||
errorMessage:
|
||
!isFromWalletAddressEOA ? S.current.thorchain_contract_address_not_supported : null,
|
||
);
|
||
}
|
||
}
|
||
return CreateTradeResult(result: true);
|
||
}
|
||
|
||
String _normalizeReceiveCurrency(CryptoCurrency receiveCurrency) {
|
||
switch (receiveCurrency) {
|
||
case CryptoCurrency.eth:
|
||
return 'eth';
|
||
case CryptoCurrency.maticpoly:
|
||
return 'polygon';
|
||
default:
|
||
return receiveCurrency.tag ?? '';
|
||
}
|
||
}
|
||
|
||
Future<bool> _isExternallyOwnedAccountAddress(String receivingAddress) async {
|
||
final normalizedReceiveCurrency = _normalizeReceiveCurrency(receiveCurrency);
|
||
|
||
final isEOAAddress = !(await _isContractAddress(normalizedReceiveCurrency, receivingAddress));
|
||
return isEOAAddress;
|
||
}
|
||
|
||
Future<bool> _isContractAddress(String chainName, String contractAddress) async {
|
||
final uri = Uri.https(
|
||
'deep-index.moralis.io',
|
||
'/api/v2.2/erc20/metadata',
|
||
{
|
||
"chain": chainName,
|
||
"addresses": contractAddress,
|
||
},
|
||
);
|
||
|
||
try {
|
||
final response = await ProxyWrapper().get(
|
||
clearnetUri: uri,
|
||
headers: {
|
||
"Accept": "application/json",
|
||
"X-API-Key": secrets.moralisApiKey,
|
||
},
|
||
);
|
||
|
||
final decodedResponse = jsonDecode(response.body)[0] as Map<String, dynamic>;
|
||
|
||
final name = decodedResponse['name'] as String?;
|
||
|
||
bool isContractAddress = name!.isNotEmpty;
|
||
|
||
return isContractAddress;
|
||
} catch (e) {
|
||
printV(e);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// Adding user's Erc20 tokens to the list of currencies
|
||
|
||
@action
|
||
Future<void> _injectUserEthTokensIntoCurrencyLists() async {
|
||
final tokens = await TokenUtilities.loadEvmTokensForSwap();
|
||
final toAddReceive = <CryptoCurrency>[];
|
||
final toAddDeposit = <CryptoCurrency>[];
|
||
|
||
for (final token in tokens) {
|
||
if (!_listContainsToken(receiveCurrencies, token)) toAddReceive.add(token);
|
||
if (!_listContainsToken(depositCurrencies, token)) toAddDeposit.add(token);
|
||
}
|
||
|
||
if (toAddReceive.isNotEmpty) receiveCurrencies.addAll(toAddReceive);
|
||
if (toAddDeposit.isNotEmpty) depositCurrencies.addAll(toAddDeposit);
|
||
}
|
||
|
||
bool _listContainsToken(List<CryptoCurrency> list, Erc20Token token) {
|
||
return list.any((item) {
|
||
if (item is Erc20Token) {
|
||
return item.contractAddress.toLowerCase() == token.contractAddress.toLowerCase();
|
||
}
|
||
return item.title.toUpperCase() == token.symbol.toUpperCase() &&
|
||
(item.tag?.toUpperCase() == token.tag?.toUpperCase() || item.tag == null);
|
||
});
|
||
}
|
||
|
||
// Adding user's Solana tokens to the list of currencies
|
||
|
||
bool _listContainsSplToken(List<CryptoCurrency> list, SPLToken token) {
|
||
return list.any((item) {
|
||
if (item is SPLToken) {
|
||
return item.mintAddress.toLowerCase() == token.mintAddress.toLowerCase();
|
||
}
|
||
return item.title.toUpperCase() == token.symbol.toUpperCase() &&
|
||
(item.tag?.toUpperCase() == token.tag?.toUpperCase() || item.tag == null);
|
||
});
|
||
}
|
||
|
||
@action
|
||
Future<void> _injectUserSplTokensIntoCurrencyLists() async {
|
||
final tokens = await TokenUtilities.loadSolTokensForSwap();
|
||
final toAddReceive = <CryptoCurrency>[];
|
||
final toAddDeposit = <CryptoCurrency>[];
|
||
|
||
for (final token in tokens) {
|
||
if (!_listContainsSplToken(receiveCurrencies, token)) toAddReceive.add(token);
|
||
if (!_listContainsSplToken(depositCurrencies, token)) toAddDeposit.add(token);
|
||
}
|
||
|
||
if (toAddReceive.isNotEmpty) receiveCurrencies.addAll(toAddReceive);
|
||
if (toAddDeposit.isNotEmpty) depositCurrencies.addAll(toAddDeposit);
|
||
}
|
||
|
||
// Adding user's Tron tokens to the list of currencies
|
||
|
||
bool _listContainsTronToken(List<CryptoCurrency> list, TronToken token) {
|
||
return list.any((item) {
|
||
if (item is TronToken) {
|
||
return item.contractAddress.toLowerCase() == token.contractAddress.toLowerCase();
|
||
}
|
||
return item.title.toUpperCase() == token.symbol.toUpperCase() &&
|
||
(item.tag?.toUpperCase() == token.tag?.toUpperCase() || item.tag == null);
|
||
});
|
||
}
|
||
|
||
@action
|
||
Future<void> _injectUserTronTokensIntoCurrencyLists() async {
|
||
final tokens = await TokenUtilities.loadTronTokensForSwap();
|
||
final toAddReceive = <CryptoCurrency>[];
|
||
final toAddDeposit = <CryptoCurrency>[];
|
||
|
||
for (final token in tokens) {
|
||
if (!_listContainsTronToken(receiveCurrencies, token)) toAddReceive.add(token);
|
||
if (!_listContainsTronToken(depositCurrencies, token)) toAddDeposit.add(token);
|
||
}
|
||
|
||
if (toAddReceive.isNotEmpty) receiveCurrencies.addAll(toAddReceive);
|
||
if (toAddDeposit.isNotEmpty) depositCurrencies.addAll(toAddDeposit);
|
||
}
|
||
|
||
double initialAmountByAssets(CryptoCurrency ticker) {
|
||
final amount = switch (ticker) {
|
||
CryptoCurrency.trx => 1000,
|
||
CryptoCurrency.nano => 10,
|
||
CryptoCurrency.zano => 10,
|
||
CryptoCurrency.wow => 1000,
|
||
CryptoCurrency.ada => 1000,
|
||
CryptoCurrency.dash => 10,
|
||
CryptoCurrency.rune => 10,
|
||
_ => 1
|
||
};
|
||
return amount.toDouble();
|
||
}
|
||
}
|