From d5cc2fe78009ecc60502eb4ed7503063800f1bed Mon Sep 17 00:00:00 2001 From: mytonwalletorg Date: Sun, 17 Nov 2024 20:54:22 +0300 Subject: [PATCH] v3.1.0 --- capacitor.config.ts | 3 +- changelogs/3.0.36.txt | 1 + changelogs/3.1.0.txt | 19 + mobile/android/app/capacitor.build.gradle | 2 + .../android/app/src/main/AndroidManifest.xml | 12 +- .../org/mytonwallet/app/MainActivity.java | 29 + mobile/android/capacitor.settings.gradle | 6 + mobile/ios/App/App.xcodeproj/project.pbxproj | 8 + mobile/ios/App/App/AppDelegate.swift | 1 - mobile/ios/App/App/Info.plist | 8 +- mobile/ios/App/App/MyTonWalletApp.swift | 28 + mobile/ios/App/App/main.swift | 8 + mobile/ios/App/Podfile | 4 +- mobile/ios/App/Podfile.lock | 20 +- mobile/ios/App/fastlane/Fastfile | 6 +- mobile/plugins/native-bottom-sheet/README.md | 20 + .../native-bottom-sheet/dist/docs.json | 20 + .../dist/esm/definitions.d.ts | 2 + .../dist/esm/definitions.js.map | 2 +- .../ios/Plugin/BottomSheetPlugin.m | 2 + .../ios/Plugin/BottomSheetPlugin.swift | 56 +- .../plugins/native-bottom-sheet/package.json | 2 +- .../native-bottom-sheet/src/definitions.ts | 4 + package-lock.json | 940 +----------------- package.json | 9 +- public/version.txt | 2 +- src/api/chains/ton/swap.ts | 6 +- src/api/chains/ton/transactions.ts | 62 +- src/api/common/backend.ts | 7 +- src/api/common/swap.ts | 16 + src/api/methods/auth.ts | 1 - src/api/methods/dapps.ts | 4 + src/api/methods/index.ts | 1 + src/api/methods/swap.ts | 152 +-- src/api/types/backend.ts | 1 + src/components/App.tsx | 2 + src/components/AppLocked.module.scss | 66 ++ src/components/AppLocked.tsx | 246 +++++ src/components/dapps/DappFeed.tsx | 16 +- src/components/dapps/DappFeedItem.tsx | 14 +- .../ledger/LedgerConfirmOperation.tsx | 4 +- src/components/ledger/LedgerConnect.tsx | 108 +- src/components/ledger/LedgerModal.module.scss | 25 + src/components/ledger/LedgerModal.tsx | 5 +- .../main/sections/Content/Content.module.scss | 5 + src/components/main/sections/Content/Swap.tsx | 79 +- .../sections/Content/Transaction.module.scss | 72 +- .../main/sections/Content/Transaction.tsx | 76 +- .../receive/ReceiveModal.module.scss | 4 + src/components/receive/content/TonContent.tsx | 5 +- src/components/settings/Settings.tsx | 12 +- src/components/settings/SettingsSecurity.tsx | 53 +- src/components/staking/Staking.module.scss | 4 +- src/components/staking/UnstakeModal.tsx | 8 +- src/components/swap/SwapInitial.tsx | 5 +- src/components/transfer/TransferInitial.tsx | 4 +- src/components/ui/InAppBrowser.tsx | 6 +- src/components/ui/Modal.module.scss | 15 +- src/components/ui/PasswordForm.tsx | 62 +- src/components/ui/PinPad.tsx | 11 +- src/components/ui/Transition.tsx | 3 + src/config.ts | 25 +- src/giveaways/components/App.tsx | 4 +- src/giveaways/index.tsx | 2 +- src/global/actions/api/auth.ts | 83 +- src/global/actions/api/dapps.ts | 75 +- src/global/actions/api/swap.ts | 26 +- src/global/actions/index.ts | 1 + src/global/actions/ui/misc.ts | 121 ++- src/global/actions/ui/settings.ts | 11 + src/global/actions/ui/shared.ts | 21 + src/global/cache.ts | 1 + src/global/types.ts | 25 +- src/hooks/useBackgroundMode.ts | 15 +- src/i18n/de.yaml | 15 +- src/i18n/en.yaml | 15 +- src/i18n/es.yaml | 15 +- src/i18n/pl.yaml | 15 +- src/i18n/ru.yaml | 17 +- src/i18n/th.yaml | 15 +- src/i18n/tr.yaml | 15 +- src/i18n/uk.yaml | 16 +- src/i18n/zh-Hans.yaml | 15 +- src/i18n/zh-Hant.yaml | 15 +- .../ledger-hw-transport-ble/BleTransport.ts | 811 +++++++++++++++ .../ledger-hw-transport-ble/awaitsBleOn.ts | 12 + .../monitorCharacteristic.ts | 35 + .../ledger-hw-transport-ble/remapErrors.ts | 22 + src/lib/ledger-hw-transport-ble/types.ts | 9 + src/styles/_variables.scss | 5 + src/util/deeplink/index.ts | 8 +- src/util/fetch.ts | 2 +- src/util/ledger/bleConnector.ts | 141 +++ src/util/ledger/index.ts | 181 +++- src/util/ledger/types.ts | 2 + src/util/ledger/utils.ts | 5 + src/util/url.ts | 18 +- src/util/windowEnvironment.ts | 2 +- 98 files changed, 2800 insertions(+), 1390 deletions(-) create mode 100644 changelogs/3.0.36.txt create mode 100644 changelogs/3.1.0.txt create mode 100644 mobile/ios/App/App/MyTonWalletApp.swift create mode 100644 mobile/ios/App/App/main.swift create mode 100644 src/components/AppLocked.module.scss create mode 100644 src/components/AppLocked.tsx create mode 100644 src/global/actions/ui/shared.ts create mode 100644 src/lib/ledger-hw-transport-ble/BleTransport.ts create mode 100644 src/lib/ledger-hw-transport-ble/awaitsBleOn.ts create mode 100644 src/lib/ledger-hw-transport-ble/monitorCharacteristic.ts create mode 100644 src/lib/ledger-hw-transport-ble/remapErrors.ts create mode 100644 src/lib/ledger-hw-transport-ble/types.ts create mode 100644 src/util/ledger/bleConnector.ts diff --git a/capacitor.config.ts b/capacitor.config.ts index 13b61d06..ba0f85fe 100644 --- a/capacitor.config.ts +++ b/capacitor.config.ts @@ -13,13 +13,13 @@ const config: CapacitorConfig = { android: { path: 'mobile/android', includePlugins: [ + '@capacitor-community/bluetooth-le', '@capacitor-mlkit/barcode-scanning', '@capacitor/app', '@capacitor/filesystem', '@capacitor/clipboard', '@capacitor/share', '@capacitor/haptics', - '@capacitor/status-bar', '@capgo/capacitor-native-biometric', '@capgo/native-audio', '@mauricewegner/capacitor-navigation-bar', @@ -27,6 +27,7 @@ const config: CapacitorConfig = { 'capacitor-native-settings', 'capacitor-plugin-safe-area', 'cordova-plugin-inappbrowser', + 'mtw-capacitor-usb-hid', 'native-dialog', 'native-bottom-sheet', 'capacitor-secure-storage-plugin', diff --git a/changelogs/3.0.36.txt b/changelogs/3.0.36.txt new file mode 100644 index 00000000..619f4cd5 --- /dev/null +++ b/changelogs/3.0.36.txt @@ -0,0 +1 @@ +Bug fixes and performance improvements diff --git a/changelogs/3.1.0.txt b/changelogs/3.1.0.txt new file mode 100644 index 00000000..4ba51d57 --- /dev/null +++ b/changelogs/3.1.0.txt @@ -0,0 +1,19 @@ +Previously, we introduced the first-ever multichain wallet on TON with support for TON & TRON networks. Now, we're back with exciting new updates! Here's what's inside: + +• Auto-lock feature and an ability to change password for enhanced wallet security. + +• Easier dapp navigation: connected applications are now displayed in the "Explore" section. + +•️ Connect your Ledger on a mobile device: Bluetooth and USB options are available depending on your platform. + +• Convenient operations through W5 Gasless and Mintless Jettons for TON users. Make transactions even with zero $TON balance. + +•️ With anti-poisoning, MyTonWallet automatically detects suspicious addresses and marks them with a ‘Scam’ label. + +•️ Search for saved addresses: easily filter addresses using test auto-suggest. + +• New deeplinks for quick access: send TON, USDT, jettons, NFTs, perform swaps or stake in one click. + +• Switched our UI links to Tonscan, the first and the most powerful TON explorer. + +• Updated UI animations, bug fixes, performance improvements, and other tiny optimizations for a smoother, more enjoyable experience. diff --git a/mobile/android/app/capacitor.build.gradle b/mobile/android/app/capacitor.build.gradle index 45464b45..dc21ad93 100644 --- a/mobile/android/app/capacitor.build.gradle +++ b/mobile/android/app/capacitor.build.gradle @@ -9,6 +9,7 @@ android { apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" dependencies { + implementation project(':capacitor-community-bluetooth-le') implementation project(':capacitor-mlkit-barcode-scanning') implementation project(':capacitor-app') implementation project(':capacitor-filesystem') @@ -21,6 +22,7 @@ dependencies { implementation project(':sina_kh-mtw-capacitor-status-bar') implementation project(':capacitor-native-settings') implementation project(':capacitor-plugin-safe-area') + implementation project(':mtw-capacitor-usb-hid') implementation project(':native-dialog') implementation project(':native-bottom-sheet') implementation project(':capacitor-secure-storage-plugin') diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml index 71806347..665ffde8 100644 --- a/mobile/android/app/src/main/AndroidManifest.xml +++ b/mobile/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ - + @@ -83,4 +84,13 @@ + + + + + diff --git a/mobile/android/app/src/main/java/org/mytonwallet/app/MainActivity.java b/mobile/android/app/src/main/java/org/mytonwallet/app/MainActivity.java index d413718d..5baa9e41 100644 --- a/mobile/android/app/src/main/java/org/mytonwallet/app/MainActivity.java +++ b/mobile/android/app/src/main/java/org/mytonwallet/app/MainActivity.java @@ -8,6 +8,8 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.view.KeyEvent; +import android.view.MotionEvent; import android.view.View; import android.view.Window; import android.view.WindowManager; @@ -17,11 +19,15 @@ import androidx.core.view.WindowInsetsControllerCompat; import androidx.interpolator.view.animation.FastOutSlowInInterpolator; +import com.getcapacitor.Bridge; import com.getcapacitor.BridgeActivity; +import java.util.Date; + public class MainActivity extends BridgeActivity { private boolean keep = true; private final int DELAY = 1000; + private long lastTouchEventTimestamp = 0; @Override protected void onCreate(Bundle savedInstanceState) { @@ -90,4 +96,27 @@ private void updateStatusBarStyle() { windowInsetsControllerCompat.setAppearanceLightStatusBars(!style.equals("DARK")); windowInsetsControllerCompat.setAppearanceLightNavigationBars(!style.equals("DARK")); } + + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + triggerTouchEvent(); + return super.dispatchTouchEvent(event); + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + triggerTouchEvent(); + return super.dispatchKeyEvent(event); + } + + private void triggerTouchEvent() { + Bridge bridge = getBridge(); + if (bridge == null) + return; + long now = new Date().getTime(); + if (now < lastTouchEventTimestamp + 5000) + return; + lastTouchEventTimestamp = now; + bridge.triggerWindowJSEvent("touch"); + } } diff --git a/mobile/android/capacitor.settings.gradle b/mobile/android/capacitor.settings.gradle index 15bdcd1c..99b63e6a 100644 --- a/mobile/android/capacitor.settings.gradle +++ b/mobile/android/capacitor.settings.gradle @@ -2,6 +2,9 @@ include ':capacitor-android' project(':capacitor-android').projectDir = new File('../../node_modules/@capacitor/android/capacitor') +include ':capacitor-community-bluetooth-le' +project(':capacitor-community-bluetooth-le').projectDir = new File('../../node_modules/@capacitor-community/bluetooth-le/android') + include ':capacitor-mlkit-barcode-scanning' project(':capacitor-mlkit-barcode-scanning').projectDir = new File('../../node_modules/@capacitor-mlkit/barcode-scanning/android') @@ -38,6 +41,9 @@ project(':capacitor-native-settings').projectDir = new File('../../node_modules/ include ':capacitor-plugin-safe-area' project(':capacitor-plugin-safe-area').projectDir = new File('../../node_modules/capacitor-plugin-safe-area/android') +include ':mtw-capacitor-usb-hid' +project(':mtw-capacitor-usb-hid').projectDir = new File('../../node_modules/mtw-capacitor-usb-hid/android') + include ':native-dialog' project(':native-dialog').projectDir = new File('../plugins/native-dialog/android') diff --git a/mobile/ios/App/App.xcodeproj/project.pbxproj b/mobile/ios/App/App.xcodeproj/project.pbxproj index 98c2c37d..26451ade 100644 --- a/mobile/ios/App/App.xcodeproj/project.pbxproj +++ b/mobile/ios/App/App.xcodeproj/project.pbxproj @@ -8,6 +8,8 @@ /* Begin PBXBuildFile section */ 0EB354256D6B4E26B50364CD /* Pods_MyTonWallet.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1BABB2300AAC81FD0F3E47EB /* Pods_MyTonWallet.framework */; }; + 27E5F5BB2CE402F4006A9298 /* MyTonWalletApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E5F5BA2CE402EC006A9298 /* MyTonWalletApp.swift */; }; + 27E5F5BD2CE40454006A9298 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E5F5BC2CE40453006A9298 /* main.swift */; }; 2FAD9763203C412B000D30F8 /* config.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2FAD9762203C412B000D30F8 /* config.xml */; }; 50379B232058CBB4000EE86E /* capacitor.config.json in Resources */ = {isa = PBXBuildFile; fileRef = 50379B222058CBB4000EE86E /* capacitor.config.json */; }; 504EC3081FED79650016851F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504EC3071FED79650016851F /* AppDelegate.swift */; }; @@ -19,6 +21,8 @@ /* Begin PBXFileReference section */ 1BABB2300AAC81FD0F3E47EB /* Pods_MyTonWallet.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MyTonWallet.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 27E5F5BA2CE402EC006A9298 /* MyTonWalletApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyTonWalletApp.swift; sourceTree = ""; }; + 27E5F5BC2CE40453006A9298 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 2FAD9762203C412B000D30F8 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = config.xml; sourceTree = ""; }; 50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = ""; }; 504EC3041FED79650016851F /* MyTonWallet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MyTonWallet.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -78,7 +82,9 @@ isa = PBXGroup; children = ( 50379B222058CBB4000EE86E /* capacitor.config.json */, + 27E5F5BC2CE40453006A9298 /* main.swift */, 504EC3071FED79650016851F /* AppDelegate.swift */, + 27E5F5BA2CE402EC006A9298 /* MyTonWalletApp.swift */, 504EC30B1FED79650016851F /* Main.storyboard */, 504EC30E1FED79650016851F /* Assets.xcassets */, 504EC3101FED79650016851F /* LaunchScreen.storyboard */, @@ -213,7 +219,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 27E5F5BB2CE402F4006A9298 /* MyTonWalletApp.swift in Sources */, 504EC3081FED79650016851F /* AppDelegate.swift in Sources */, + 27E5F5BD2CE40454006A9298 /* main.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/mobile/ios/App/App/AppDelegate.swift b/mobile/ios/App/App/AppDelegate.swift index c3cd83b5..76e21547 100644 --- a/mobile/ios/App/App/AppDelegate.swift +++ b/mobile/ios/App/App/AppDelegate.swift @@ -1,7 +1,6 @@ import UIKit import Capacitor -@UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? diff --git a/mobile/ios/App/App/Info.plist b/mobile/ios/App/App/Info.plist index c702f992..2a7cc967 100644 --- a/mobile/ios/App/App/Info.plist +++ b/mobile/ios/App/App/Info.plist @@ -66,6 +66,12 @@ NSFaceIDUsageDescription For an easier and faster operation verification. NSPhotoLibraryAddUsageDescription - We need access to your photo library to save your awesome NFTs! + Photo library access is needed to download NFTs. + NSBluetoothAlwaysUsageDescription + Bluetooth access is needed to connect Ledger. + UIBackgroundModes + + bluetooth-central + diff --git a/mobile/ios/App/App/MyTonWalletApp.swift b/mobile/ios/App/App/MyTonWalletApp.swift new file mode 100644 index 00000000..e0c0c88b --- /dev/null +++ b/mobile/ios/App/App/MyTonWalletApp.swift @@ -0,0 +1,28 @@ +import UIKit +import Capacitor + +class MyTonWalletApp: UIApplication { + + private var lastTouchEventTimestamp = TimeInterval(0) + + override func sendEvent(_ event: UIEvent) { + super.sendEvent(event) + + guard let touches = event.allTouches, + !touches.isEmpty else { + return + } + + let now = Date().timeIntervalSince1970 + guard now >= lastTouchEventTimestamp + 5 else { + return + } + + guard let vc = (delegate as? AppDelegate)?.window?.rootViewController as? CAPBridgeViewController else { + return + } + lastTouchEventTimestamp = now + vc.bridge?.triggerWindowJSEvent(eventName: "touch") + } + +} diff --git a/mobile/ios/App/App/main.swift b/mobile/ios/App/App/main.swift new file mode 100644 index 00000000..da3d520a --- /dev/null +++ b/mobile/ios/App/App/main.swift @@ -0,0 +1,8 @@ +import UIKit + +UIApplicationMain( + CommandLine.argc, + CommandLine.unsafeArgv, + NSStringFromClass(MyTonWalletApp.self), + NSStringFromClass(AppDelegate.self) +) diff --git a/mobile/ios/App/Podfile b/mobile/ios/App/Podfile index a19b9417..4a8bad28 100644 --- a/mobile/ios/App/Podfile +++ b/mobile/ios/App/Podfile @@ -11,6 +11,7 @@ install! 'cocoapods', :disable_input_output_paths => true def capacitor_pods pod 'Capacitor', :path => '../../../node_modules/@capacitor/ios' pod 'CapacitorCordova', :path => '../../../node_modules/@capacitor/ios' + pod 'CapacitorCommunityBluetoothLe', :path => '../../../node_modules/@capacitor-community/bluetooth-le' pod 'CapacitorMlkitBarcodeScanning', :path => '../../../node_modules/@capacitor-mlkit/barcode-scanning' pod 'CapacitorApp', :path => '../../../node_modules/@capacitor/app' pod 'CapacitorAppLauncher', :path => '../../../node_modules/@capacitor/app-launcher' @@ -26,6 +27,7 @@ def capacitor_pods pod 'CapacitorNativeSettings', :path => '../../../node_modules/capacitor-native-settings' pod 'CapacitorPluginSafeArea', :path => '../../../node_modules/capacitor-plugin-safe-area' pod 'CapacitorSecureStoragePlugin', :path => '../../../node_modules/capacitor-secure-storage-plugin' + pod 'MtwCapacitorUsbHid', :path => '../../../node_modules/mtw-capacitor-usb-hid' pod 'NativeBottomSheet', :path => '../../plugins/native-bottom-sheet' pod 'NativeDialog', :path => '../../plugins/native-dialog' pod 'CordovaPlugins', :path => '../capacitor-cordova-ios-plugins' @@ -34,7 +36,7 @@ end target 'MyTonWallet' do capacitor_pods # Add your Pods here - pod 'FloatingPanel', :git => 'https://github.com/mytonwalletorg/FloatingPanel', :commit => '124f09299ee26544cad45f95f2e3cb085b76f11a' + pod 'FloatingPanel', :git => 'https://github.com/mytonwalletorg/FloatingPanel', :commit => 'f58a8b94008540e680faba61867debf8b3cec682' end post_install do |installer| diff --git a/mobile/ios/App/Podfile.lock b/mobile/ios/App/Podfile.lock index 7a4d6b89..d7c7c0c3 100644 --- a/mobile/ios/App/Podfile.lock +++ b/mobile/ios/App/Podfile.lock @@ -7,6 +7,8 @@ PODS: - Capacitor - CapacitorClipboard (6.0.1): - Capacitor + - CapacitorCommunityBluetoothLe (6.0.1): + - Capacitor - CapacitorCordova (6.1.2) - CapacitorFilesystem (6.0.1): - Capacitor @@ -85,6 +87,8 @@ PODS: - GTMSessionFetcher/Core (< 4.0, >= 1.1) - MLImage (= 1.0.0-beta5) - MLKitCommon (~> 10.0) + - MtwCapacitorUsbHid (0.0.1): + - Capacitor - nanopb (2.30910.0): - nanopb/decode (= 2.30910.0) - nanopb/encode (= 2.30910.0) @@ -107,6 +111,7 @@ DEPENDENCIES: - "CapacitorApp (from `../../../node_modules/@capacitor/app`)" - "CapacitorAppLauncher (from `../../../node_modules/@capacitor/app-launcher`)" - "CapacitorClipboard (from `../../../node_modules/@capacitor/clipboard`)" + - "CapacitorCommunityBluetoothLe (from `../../../node_modules/@capacitor-community/bluetooth-le`)" - "CapacitorCordova (from `../../../node_modules/@capacitor/ios`)" - "CapacitorFilesystem (from `../../../node_modules/@capacitor/filesystem`)" - "CapacitorHaptics (from `../../../node_modules/@capacitor/haptics`)" @@ -118,8 +123,9 @@ DEPENDENCIES: - "CapgoCapacitorNativeBiometric (from `../../../node_modules/@capgo/capacitor-native-biometric`)" - "CapgoNativeAudio (from `../../../node_modules/@capgo/native-audio`)" - CordovaPlugins (from `../capacitor-cordova-ios-plugins`) - - FloatingPanel (from `https://github.com/mytonwalletorg/FloatingPanel`, commit `124f09299ee26544cad45f95f2e3cb085b76f11a`) + - FloatingPanel (from `https://github.com/mytonwalletorg/FloatingPanel`, commit `f58a8b94008540e680faba61867debf8b3cec682`) - "MauricewegnerCapacitorNavigationBar (from `../../../node_modules/@mauricewegner/capacitor-navigation-bar`)" + - MtwCapacitorUsbHid (from `../../../node_modules/mtw-capacitor-usb-hid`) - NativeBottomSheet (from `../../plugins/native-bottom-sheet`) - NativeDialog (from `../../plugins/native-dialog`) - "SinaKhMtwCapacitorSplashScreen (from `../../../node_modules/@sina_kh/mtw-capacitor-splash-screen`)" @@ -150,6 +156,8 @@ EXTERNAL SOURCES: :path: "../../../node_modules/@capacitor/app-launcher" CapacitorClipboard: :path: "../../../node_modules/@capacitor/clipboard" + CapacitorCommunityBluetoothLe: + :path: "../../../node_modules/@capacitor-community/bluetooth-le" CapacitorCordova: :path: "../../../node_modules/@capacitor/ios" CapacitorFilesystem: @@ -173,10 +181,12 @@ EXTERNAL SOURCES: CordovaPlugins: :path: "../capacitor-cordova-ios-plugins" FloatingPanel: - :commit: 124f09299ee26544cad45f95f2e3cb085b76f11a + :commit: f58a8b94008540e680faba61867debf8b3cec682 :git: https://github.com/mytonwalletorg/FloatingPanel MauricewegnerCapacitorNavigationBar: :path: "../../../node_modules/@mauricewegner/capacitor-navigation-bar" + MtwCapacitorUsbHid: + :path: "../../../node_modules/mtw-capacitor-usb-hid" NativeBottomSheet: :path: "../../plugins/native-bottom-sheet" NativeDialog: @@ -188,7 +198,7 @@ EXTERNAL SOURCES: CHECKOUT OPTIONS: FloatingPanel: - :commit: 124f09299ee26544cad45f95f2e3cb085b76f11a + :commit: f58a8b94008540e680faba61867debf8b3cec682 :git: https://github.com/mytonwalletorg/FloatingPanel SPEC CHECKSUMS: @@ -196,6 +206,7 @@ SPEC CHECKSUMS: CapacitorApp: 0bc633b4eae40a1f32cd2834788fad3bc42da6a1 CapacitorAppLauncher: 9ac785e8d3936388249212b6e16cb32225960c5f CapacitorClipboard: 756cd7e83e8d5d19b0c74f40b57517c287bd5fe2 + CapacitorCommunityBluetoothLe: 694d91cd954a2b3149db8967faa7af433700eb6b CapacitorCordova: f48c89f96c319101cd2f0ce8a2b7449b5fb8b3dd CapacitorFilesystem: 37fb3aa5c945b4539ab11c74a5c57925a302bf24 CapacitorHaptics: fe689ade56ef20ec9b041a753c6da70c5d8ec9a9 @@ -219,6 +230,7 @@ SPEC CHECKSUMS: MLKitBarcodeScanning: 9cb0ec5ec65bbb5db31de4eba0a3289626beab4e MLKitCommon: afcd11b6c0735066a0dde8b4bf2331f6197cbca2 MLKitVision: 90922bca854014a856f8b649d1f1f04f63fd9c79 + MtwCapacitorUsbHid: ee48577f497c225b50d5db1f1bd40fb5c5a1ca99 nanopb: 438bc412db1928dac798aa6fd75726007be04262 NativeBottomSheet: 81b2b53d470537e42858d0d6485af318c51c3a5f NativeDialog: f347d7f1a10adac33a1cb1a02be03911cf2fe067 @@ -229,4 +241,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 1bbc72e2fbfb2ae871c9a96aef2548830c3c0d6c -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/mobile/ios/App/fastlane/Fastfile b/mobile/ios/App/fastlane/Fastfile index 29f7aa52..d94a1eb2 100644 --- a/mobile/ios/App/fastlane/Fastfile +++ b/mobile/ios/App/fastlane/Fastfile @@ -56,7 +56,11 @@ platform :ios do ) changelog_from_git_commits(merge_commit_filtering: "exclude_merges", commits_count: 3) - upload_to_testflight(distribute_external: true, groups: "MTW external group") + upload_to_testflight( + distribute_external: true, + groups: "MTW external group", + beta_app_description: "Bug fixes and performance improvements" + ) end lane :release do diff --git a/mobile/plugins/native-bottom-sheet/README.md b/mobile/plugins/native-bottom-sheet/README.md index ced7cc31..176ca4dc 100644 --- a/mobile/plugins/native-bottom-sheet/README.md +++ b/mobile/plugins/native-bottom-sheet/README.md @@ -18,6 +18,8 @@ npx cap sync * [`clearScrollPatch()`](#clearscrollpatch) * [`disable()`](#disable) * [`enable()`](#enable) +* [`hide()`](#hide) +* [`show()`](#show) * [`delegate(...)`](#delegate) * [`release(...)`](#release) * [`openSelf(...)`](#openself) @@ -80,6 +82,24 @@ enable() => Promise -------------------- +### hide() + +```typescript +hide() => Promise +``` + +-------------------- + + +### show() + +```typescript +show() => Promise +``` + +-------------------- + + ### delegate(...) ```typescript diff --git a/mobile/plugins/native-bottom-sheet/dist/docs.json b/mobile/plugins/native-bottom-sheet/dist/docs.json index 901b651d..1d60e76d 100644 --- a/mobile/plugins/native-bottom-sheet/dist/docs.json +++ b/mobile/plugins/native-bottom-sheet/dist/docs.json @@ -55,6 +55,26 @@ "complexTypes": [], "slug": "enable" }, + { + "name": "hide", + "signature": "() => Promise", + "parameters": [], + "returns": "Promise", + "tags": [], + "docs": "", + "complexTypes": [], + "slug": "hide" + }, + { + "name": "show", + "signature": "() => Promise", + "parameters": [], + "returns": "Promise", + "tags": [], + "docs": "", + "complexTypes": [], + "slug": "show" + }, { "name": "delegate", "signature": "(options: { key: BottomSheetKeys; globalJson: string; }) => Promise", diff --git a/mobile/plugins/native-bottom-sheet/dist/esm/definitions.d.ts b/mobile/plugins/native-bottom-sheet/dist/esm/definitions.d.ts index 62a7a96e..dac1d9e3 100644 --- a/mobile/plugins/native-bottom-sheet/dist/esm/definitions.d.ts +++ b/mobile/plugins/native-bottom-sheet/dist/esm/definitions.d.ts @@ -6,6 +6,8 @@ export interface BottomSheetPlugin { clearScrollPatch(): Promise; disable(): Promise; enable(): Promise; + hide(): Promise; + show(): Promise; delegate(options: { key: BottomSheetKeys; globalJson: string; diff --git a/mobile/plugins/native-bottom-sheet/dist/esm/definitions.js.map b/mobile/plugins/native-bottom-sheet/dist/esm/definitions.js.map index bf55add1..847e234b 100644 --- a/mobile/plugins/native-bottom-sheet/dist/esm/definitions.js.map +++ b/mobile/plugins/native-bottom-sheet/dist/esm/definitions.js.map @@ -1 +1 @@ -{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import { PluginListenerHandle } from '@capacitor/core';\n\nexport type BottomSheetKeys =\n 'initial'\n | 'receive'\n | 'invoice'\n | 'transfer'\n | 'swap'\n | 'stake'\n | 'unstake'\n | 'staking-info'\n | 'vesting-info'\n | 'vesting-confirm'\n | 'transaction-info'\n | 'swap-activity'\n | 'backup'\n | 'add-account'\n | 'settings'\n | 'qr-scanner'\n | 'dapp-connect'\n | 'dapp-transfer'\n | 'disclaimer'\n | 'backup-warning'\n | 'onramp-widget';\n\nexport interface BottomSheetPlugin {\n prepare(): Promise;\n\n applyScrollPatch(): Promise;\n\n clearScrollPatch(): Promise;\n\n disable(): Promise;\n\n enable(): Promise;\n\n delegate(options: { key: BottomSheetKeys, globalJson: string }): Promise;\n\n release(options: { key: BottomSheetKeys | '*' }): Promise;\n\n openSelf(options: { key: BottomSheetKeys, height: string, backgroundColor: string }): Promise;\n\n closeSelf(options: { key: BottomSheetKeys }): Promise;\n\n toggleSelfFullSize(options: { isFullSize: boolean }): Promise;\n\n openInMain(options: { key: BottomSheetKeys }): Promise;\n\n addListener(\n eventName: 'delegate',\n handler: (options: { key: BottomSheetKeys, globalJson: string }) => void,\n ): Promise & PluginListenerHandle;\n\n addListener(\n eventName: 'move',\n handler: () => void,\n ): Promise & PluginListenerHandle;\n\n\n addListener(\n eventName: 'openInMain',\n handler: (options: { key: BottomSheetKeys }) => void,\n ): Promise & PluginListenerHandle;\n}\n"]} \ No newline at end of file +{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import { PluginListenerHandle } from '@capacitor/core';\n\nexport type BottomSheetKeys =\n 'initial'\n | 'receive'\n | 'invoice'\n | 'transfer'\n | 'swap'\n | 'stake'\n | 'unstake'\n | 'staking-info'\n | 'vesting-info'\n | 'vesting-confirm'\n | 'transaction-info'\n | 'swap-activity'\n | 'backup'\n | 'add-account'\n | 'settings'\n | 'qr-scanner'\n | 'dapp-connect'\n | 'dapp-transfer'\n | 'disclaimer'\n | 'backup-warning'\n | 'onramp-widget';\n\nexport interface BottomSheetPlugin {\n prepare(): Promise;\n\n applyScrollPatch(): Promise;\n\n clearScrollPatch(): Promise;\n\n disable(): Promise;\n\n enable(): Promise;\n\n hide(): Promise;\n\n show(): Promise;\n\n delegate(options: { key: BottomSheetKeys, globalJson: string }): Promise;\n\n release(options: { key: BottomSheetKeys | '*' }): Promise;\n\n openSelf(options: { key: BottomSheetKeys, height: string, backgroundColor: string }): Promise;\n\n closeSelf(options: { key: BottomSheetKeys }): Promise;\n\n toggleSelfFullSize(options: { isFullSize: boolean }): Promise;\n\n openInMain(options: { key: BottomSheetKeys }): Promise;\n\n addListener(\n eventName: 'delegate',\n handler: (options: { key: BottomSheetKeys, globalJson: string }) => void,\n ): Promise & PluginListenerHandle;\n\n addListener(\n eventName: 'move',\n handler: () => void,\n ): Promise & PluginListenerHandle;\n\n\n addListener(\n eventName: 'openInMain',\n handler: (options: { key: BottomSheetKeys }) => void,\n ): Promise & PluginListenerHandle;\n}\n"]} \ No newline at end of file diff --git a/mobile/plugins/native-bottom-sheet/ios/Plugin/BottomSheetPlugin.m b/mobile/plugins/native-bottom-sheet/ios/Plugin/BottomSheetPlugin.m index 41bd68fd..eacb45e5 100644 --- a/mobile/plugins/native-bottom-sheet/ios/Plugin/BottomSheetPlugin.m +++ b/mobile/plugins/native-bottom-sheet/ios/Plugin/BottomSheetPlugin.m @@ -15,4 +15,6 @@ CAP_PLUGIN_METHOD(clearScrollPatch, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(disable, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(enable, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(hide, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(show, CAPPluginReturnPromise); ) diff --git a/mobile/plugins/native-bottom-sheet/ios/Plugin/BottomSheetPlugin.swift b/mobile/plugins/native-bottom-sheet/ios/Plugin/BottomSheetPlugin.swift index 550b91c1..b0297c3b 100644 --- a/mobile/plugins/native-bottom-sheet/ios/Plugin/BottomSheetPlugin.swift +++ b/mobile/plugins/native-bottom-sheet/ios/Plugin/BottomSheetPlugin.swift @@ -23,6 +23,8 @@ public class BottomSheetPlugin: CAPPlugin, FloatingPanelControllerDelegate { var currentHalfY: CGFloat? var prevStatusBarStyle: UIStatusBarStyle? public var isHalfSize = false + public var wasFullScreenBeforeHiding = false + public var halfSizeAddedTemporarily = false @objc func prepare(_ call: CAPPluginCall) { ensureLocalOrigin() @@ -103,6 +105,28 @@ public class BottomSheetPlugin: CAPPlugin, FloatingPanelControllerDelegate { } } + @objc func hide(_ call: CAPPluginCall) { + DispatchQueue.main.async { [self] in + self.fpc.view.alpha = 0 + if self.fpc.state == .full { + wasFullScreenBeforeHiding = true + set(halfSize: true, animated: false) + } + call.resolve() + } + } + + @objc func show(_ call: CAPPluginCall) { + DispatchQueue.main.async { [self] in + self.fpc.view.alpha = 1 + if wasFullScreenBeforeHiding { + set(halfSize: false, animated: false) + wasFullScreenBeforeHiding = false + } + call.resolve() + } + } + @objc func applyScrollPatch(_ call: CAPPluginCall) { DispatchQueue.main.async { [self] in guard let topVc = bridge?.viewController?.parent?.presentingViewController as? CAPBridgeViewController else { @@ -116,7 +140,10 @@ public class BottomSheetPlugin: CAPPlugin, FloatingPanelControllerDelegate { @objc func clearScrollPatch(_ call: CAPPluginCall) { DispatchQueue.main.async { [self] in - let topVc = bridge!.viewController!.parent!.presentingViewController as! CAPBridgeViewController + guard let topVc = bridge?.viewController?.parent?.presentingViewController as? CAPBridgeViewController else { + call.resolve() + return + } let topBottomSheetPlugin = topVc.bridge!.plugin(withName: "BottomSheet") as! BottomSheetPlugin call.resolve() } @@ -192,6 +219,29 @@ public class BottomSheetPlugin: CAPPlugin, FloatingPanelControllerDelegate { } } + private func set(halfSize: Bool, animated: Bool) { + let layout = fpc.layout as! MyPanelLayout + + if halfSize { + if layout.anchors[.half] == nil { + layout.anchors[.half] = FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .superview) + halfSizeAddedTemporarily = true + } else { + halfSizeAddedTemporarily = false + } + } else if !halfSize, halfSizeAddedTemporarily { + layout.anchors[.half] = nil + } + + if !halfSize && layout.anchors[.full] == nil { + layout.anchors[.full] = layout.fullAnchor + } else if halfSize && layout.anchors[.full] != nil { + layout.anchors[.full] = nil + } + + animateTo(to: halfSize ? .half : .full, duration: animated ? nil : 0) + } + @objc func toggleSelfFullSize(_ call: CAPPluginCall) { ensureLocalOrigin() ensureDelegated() @@ -294,13 +344,13 @@ public class BottomSheetPlugin: CAPPlugin, FloatingPanelControllerDelegate { childBottomSheetPlugin.currentOpenSelfCall = nil } - private func animateTo(to: FloatingPanelState) { + private func animateTo(to: FloatingPanelState, duration: Double? = nil) { if to == .half && fpc.layout.anchors[.half] == nil { return } let timing = UICubicTimingParameters(controlPoint1: EASING_1, controlPoint2: EASING_2) - let animator = UIViewPropertyAnimator(duration: ANIMATION_DURATION, timingParameters: timing) + let animator = UIViewPropertyAnimator(duration: duration ?? ANIMATION_DURATION, timingParameters: timing) fpc.isAnimating = true animator.addAnimations { [self] in diff --git a/mobile/plugins/native-bottom-sheet/package.json b/mobile/plugins/native-bottom-sheet/package.json index 53455607..8a0e98fb 100644 --- a/mobile/plugins/native-bottom-sheet/package.json +++ b/mobile/plugins/native-bottom-sheet/package.json @@ -47,7 +47,7 @@ "@capacitor/android": "6.1.2", "@capacitor/core": "6.1.2", "@capacitor/docgen": "0.0.18", - "@capacitor/ios": "^6.0.0", + "@capacitor/ios": "6.1.2", "@ionic/eslint-config": "0.4.0", "@ionic/prettier-config": "1.0.1", "@ionic/swiftlint-config": "1.1.2", diff --git a/mobile/plugins/native-bottom-sheet/src/definitions.ts b/mobile/plugins/native-bottom-sheet/src/definitions.ts index 63a87e80..af6befae 100644 --- a/mobile/plugins/native-bottom-sheet/src/definitions.ts +++ b/mobile/plugins/native-bottom-sheet/src/definitions.ts @@ -34,6 +34,10 @@ export interface BottomSheetPlugin { enable(): Promise; + hide(): Promise; + + show(): Promise; + delegate(options: { key: BottomSheetKeys, globalJson: string }): Promise; release(options: { key: BottomSheetKeys | '*' }): Promise; diff --git a/package-lock.json b/package-lock.json index 99f53db7..62db0e2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,17 @@ { "name": "mytonwallet", - "version": "3.0.35", + "version": "3.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mytonwallet", - "version": "3.0.35", + "version": "3.1.0", "license": "GPL-3.0-or-later", "dependencies": { "@awesome-cordova-plugins/core": "6.9.0", "@awesome-cordova-plugins/in-app-browser": "6.9.0", + "@capacitor-community/bluetooth-le": "github:mytonwallet-org/capacitor-bluetooth-le#0ac4f47716a9fbccbd7184384dcfab69d8fede97", "@capacitor-mlkit/barcode-scanning": "6.2.0", "@capacitor/android": "6.1.2", "@capacitor/app": "6.0.1", @@ -37,14 +38,14 @@ "capacitor-native-settings": "6.0.1", "capacitor-plugin-safe-area": "3.0.3", "capacitor-secure-storage-plugin": "github:mytonwallet-org/capacitor-secure-storage-plugin#03a96f6c0e7207b302b1dcb2d13c0c9e2ae6cc29", - "cordova-plugin-inappbrowser": "github:mytonwalletorg/cordova-plugin-inappbrowser#c585b61d05f5bef7bc0920158db7e4febf48a9ca", + "cordova-plugin-inappbrowser": "github:mytonwalletorg/cordova-plugin-inappbrowser#c5d3d89bead5e2b52df60ab7dd0217497a22a3a1", "create-hmac": "1.1.7", "dexie": "4.0.8", "idb-keyval": "6.2.1", + "mtw-capacitor-usb-hid": "github:mytonwallet-org/capacitor-usb-hid#814f226d655536664d09c02263d898a1982228c2", "native-bottom-sheet": "file:mobile/plugins/native-bottom-sheet", "native-dialog": "file:mobile/plugins/native-dialog", "pako": "2.1.0", - "process": "^0.11.10", "qr-code-styling": "github:mytonwallet-org/qr-code-styling#8d56b409da29122a92da2767d049b45b1bc24db2", "qrcode-generator": "1.4.4", "stream-browserify": "3.0.0", @@ -82,6 +83,7 @@ "@types/react": "18.3.11", "@types/react-dom": "18.3.0", "@types/sha256": "0.2.2", + "@types/uuid": "10.0.0", "@types/webextension-polyfill": "0.12.1", "@types/webpack": "5.28.5", "@types/webpack-bundle-analyzer": "4.7.0", @@ -135,6 +137,7 @@ "postcss": "8.4.47", "postcss-loader": "8.1.1", "postcss-modules": "6.0.0", + "process": "0.11.10", "replace-in-file": "8.2.0", "sass": "1.79.4", "sass-loader": "16.0.2", @@ -163,7 +166,7 @@ "@capacitor/android": "6.1.2", "@capacitor/core": "6.1.2", "@capacitor/docgen": "0.0.18", - "@capacitor/ios": "^6.0.0", + "@capacitor/ios": "6.1.2", "@ionic/eslint-config": "0.4.0", "@ionic/prettier-config": "1.0.1", "@ionic/swiftlint-config": "1.1.2", @@ -743,8 +746,6 @@ }, "node_modules/@babel/code-frame": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", - "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", "dev": true, "license": "MIT", "dependencies": { @@ -757,8 +758,6 @@ }, "node_modules/@babel/compat-data": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.7.tgz", - "integrity": "sha512-9ickoLz+hcXCeh7jrcin+/SLWm+GkxE2kTvoYyp38p4WkdFXfQJxDFGWp/YHjiKLPx06z2A7W8XKuqbReXDzsw==", "dev": true, "license": "MIT", "engines": { @@ -767,8 +766,6 @@ }, "node_modules/@babel/core": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.7.tgz", - "integrity": "sha512-yJ474Zv3cwiSOO9nXJuqzvwEeM+chDuQ8GJirw+pZ91sCGCyOZ3dJkVE09fTV0VEVzXyLWhh3G/AolYTPX7Mow==", "dev": true, "license": "MIT", "dependencies": { @@ -831,8 +828,6 @@ }, "node_modules/@babel/generator": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz", - "integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==", "dev": true, "license": "MIT", "dependencies": { @@ -847,8 +842,6 @@ }, "node_modules/@babel/helper-annotate-as-pure": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.7.tgz", - "integrity": "sha512-4xwU8StnqnlIhhioZf1tqnVWeQ9pvH/ujS8hRfw/WOza+/a+1qv69BWNy+oY231maTCWgKWhfBU7kDpsds6zAA==", "dev": true, "license": "MIT", "dependencies": { @@ -860,8 +853,6 @@ }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.7.tgz", - "integrity": "sha512-12xfNeKNH7jubQNm7PAkzlLwEmCs1tfuX3UjIw6vP6QXi+leKh6+LyC/+Ed4EIQermwd58wsyh070yjDHFlNGg==", "dev": true, "license": "MIT", "dependencies": { @@ -874,8 +865,6 @@ }, "node_modules/@babel/helper-compilation-targets": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.7.tgz", - "integrity": "sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==", "dev": true, "license": "MIT", "dependencies": { @@ -899,8 +888,6 @@ }, "node_modules/@babel/helper-create-class-features-plugin": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.7.tgz", - "integrity": "sha512-bD4WQhbkx80mAyj/WCm4ZHcF4rDxkoLFO6ph8/5/mQ3z4vAzltQXAmbc7GvVJx5H+lk5Mi5EmbTeox5nMGCsbw==", "dev": true, "license": "MIT", "dependencies": { @@ -929,8 +916,6 @@ }, "node_modules/@babel/helper-create-regexp-features-plugin": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.7.tgz", - "integrity": "sha512-byHhumTj/X47wJ6C6eLpK7wW/WBEcnUeb7D0FNc/jFQnQVw7DOso3Zz5u9x/zLrFVkHa89ZGDbkAa1D54NdrCQ==", "dev": true, "license": "MIT", "dependencies": { @@ -979,8 +964,6 @@ }, "node_modules/@babel/helper-member-expression-to-functions": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.7.tgz", - "integrity": "sha512-O31Ssjd5K6lPbTX9AAYpSKrZmLeagt9uwschJd+Ixo6QiRyfpvgtVQp8qrDR9UNFjZ8+DO34ZkdrN+BnPXemeA==", "dev": true, "license": "MIT", "dependencies": { @@ -993,8 +976,6 @@ }, "node_modules/@babel/helper-module-imports": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz", - "integrity": "sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==", "dev": true, "license": "MIT", "dependencies": { @@ -1007,8 +988,6 @@ }, "node_modules/@babel/helper-module-transforms": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.7.tgz", - "integrity": "sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1026,8 +1005,6 @@ }, "node_modules/@babel/helper-optimise-call-expression": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.7.tgz", - "integrity": "sha512-VAwcwuYhv/AT+Vfr28c9y6SHzTan1ryqrydSTFGjU0uDJHw3uZ+PduI8plCLkRsDnqK2DMEDmwrOQRsK/Ykjng==", "dev": true, "license": "MIT", "dependencies": { @@ -1039,8 +1016,6 @@ }, "node_modules/@babel/helper-plugin-utils": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.7.tgz", - "integrity": "sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==", "dev": true, "license": "MIT", "engines": { @@ -1049,8 +1024,6 @@ }, "node_modules/@babel/helper-remap-async-to-generator": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.7.tgz", - "integrity": "sha512-kRGE89hLnPfcz6fTrlNU+uhgcwv0mBE4Gv3P9Ke9kLVJYpi4AMVVEElXvB5CabrPZW4nCM8P8UyyjrzCM0O2sw==", "dev": true, "license": "MIT", "dependencies": { @@ -1067,8 +1040,6 @@ }, "node_modules/@babel/helper-replace-supers": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.7.tgz", - "integrity": "sha512-iy8JhqlUW9PtZkd4pHM96v6BdJ66Ba9yWSE4z0W4TvSZwLBPkyDsiIU3ENe4SmrzRBs76F7rQXTy1lYC49n6Lw==", "dev": true, "license": "MIT", "dependencies": { @@ -1085,8 +1056,6 @@ }, "node_modules/@babel/helper-simple-access": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.7.tgz", - "integrity": "sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1099,8 +1068,6 @@ }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.7.tgz", - "integrity": "sha512-pPbNbchZBkPMD50K0p3JGcFMNLVUCuU/ABybm/PGNj4JiHrpmNyqqCphBk4i19xXtNV0JhldQJJtbSW5aUvbyA==", "dev": true, "license": "MIT", "dependencies": { @@ -1124,8 +1091,6 @@ }, "node_modules/@babel/helper-string-parser": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", - "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", "dev": true, "license": "MIT", "engines": { @@ -1134,8 +1099,6 @@ }, "node_modules/@babel/helper-validator-identifier": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", - "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", "dev": true, "license": "MIT", "engines": { @@ -1144,8 +1107,6 @@ }, "node_modules/@babel/helper-validator-option": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.7.tgz", - "integrity": "sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==", "dev": true, "license": "MIT", "engines": { @@ -1154,8 +1115,6 @@ }, "node_modules/@babel/helper-wrap-function": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.7.tgz", - "integrity": "sha512-MA0roW3JF2bD1ptAaJnvcabsVlNQShUaThyJbCDD4bCp8NEgiFvpoqRI2YS22hHlc2thjO/fTg2ShLMC3jygAg==", "dev": true, "license": "MIT", "dependencies": { @@ -1169,8 +1128,6 @@ }, "node_modules/@babel/helpers": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.7.tgz", - "integrity": "sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==", "dev": true, "license": "MIT", "dependencies": { @@ -1183,8 +1140,6 @@ }, "node_modules/@babel/highlight": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", - "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", "dev": true, "license": "MIT", "dependencies": { @@ -1199,8 +1154,6 @@ }, "node_modules/@babel/parser": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.7.tgz", - "integrity": "sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==", "dev": true, "license": "MIT", "dependencies": { @@ -1215,8 +1168,6 @@ }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.7.tgz", - "integrity": "sha512-UV9Lg53zyebzD1DwQoT9mzkEKa922LNUp5YkTJ6Uta0RbyXaQNUgcvSt7qIu1PpPzVb6rd10OVNTzkyBGeVmxQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1232,8 +1183,6 @@ }, "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.7.tgz", - "integrity": "sha512-GDDWeVLNxRIkQTnJn2pDOM1pkCgYdSqPeT1a9vh9yIqu2uzzgw1zcqEb+IJOhy+dTBMlNdThrDIksr2o09qrrQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1248,8 +1197,6 @@ }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.7.tgz", - "integrity": "sha512-wxyWg2RYaSUYgmd9MR0FyRGyeOMQE/Uzr1wzd/g5cf5bwi9A4v6HFdDm7y1MgDtod/fLOSTZY6jDgV0xU9d5bA==", "dev": true, "license": "MIT", "dependencies": { @@ -1264,8 +1211,6 @@ }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.7.tgz", - "integrity": "sha512-Xwg6tZpLxc4iQjorYsyGMyfJE7nP5MV8t/Ka58BgiA7Jw0fRqQNcANlLfdJ/yvBt9z9LD2We+BEkT7vLqZRWng==", "dev": true, "license": "MIT", "dependencies": { @@ -1282,8 +1227,6 @@ }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.7.tgz", - "integrity": "sha512-UVATLMidXrnH+GMUIuxq55nejlj02HP7F5ETyBONzP6G87fPBogG4CH6kxrSrdIuAjdwNO9VzyaYsrZPscWUrw==", "dev": true, "license": "MIT", "dependencies": { @@ -1480,8 +1423,6 @@ }, "node_modules/@babel/plugin-syntax-dynamic-import": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1493,8 +1434,6 @@ }, "node_modules/@babel/plugin-syntax-export-namespace-from": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1520,8 +1459,6 @@ }, "node_modules/@babel/plugin-syntax-import-assertions": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.25.7.tgz", - "integrity": "sha512-ZvZQRmME0zfJnDQnVBKYzHxXT7lYBB3Revz1GuS7oLXWMgqUPX4G+DDbT30ICClht9WKV34QVrZhSw6WdklwZQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1536,8 +1473,6 @@ }, "node_modules/@babel/plugin-syntax-import-attributes": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.7.tgz", - "integrity": "sha512-AqVo+dguCgmpi/3mYBdu9lkngOBlQ2w2vnNpa6gfiCxQZLzV4ZbhsXitJ2Yblkoe1VQwtHSaNmIaGll/26YWRw==", "dev": true, "license": "MIT", "dependencies": { @@ -1574,8 +1509,6 @@ }, "node_modules/@babel/plugin-syntax-jsx": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.7.tgz", - "integrity": "sha512-ruZOnKO+ajVL/MVx+PwNBPOkrnXTXoWMtte1MBpegfCArhqOe3Bj52avVj1huLLxNKYKXYaSxZ2F+woK1ekXfw==", "dev": true, "license": "MIT", "dependencies": { @@ -1684,8 +1617,6 @@ }, "node_modules/@babel/plugin-syntax-typescript": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.7.tgz", - "integrity": "sha512-rR+5FDjpCHqqZN2bzZm18bVYGaejGq5ZkpVCJLXor/+zlSrSoc4KWcHI0URVWjl/68Dyr1uwZUz/1njycEAv9g==", "dev": true, "license": "MIT", "dependencies": { @@ -1715,8 +1646,6 @@ }, "node_modules/@babel/plugin-transform-arrow-functions": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.7.tgz", - "integrity": "sha512-EJN2mKxDwfOUCPxMO6MUI58RN3ganiRAG/MS/S3HfB6QFNjroAMelQo/gybyYq97WerCBAZoyrAoW8Tzdq2jWg==", "dev": true, "license": "MIT", "dependencies": { @@ -1731,8 +1660,6 @@ }, "node_modules/@babel/plugin-transform-async-generator-functions": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.7.tgz", - "integrity": "sha512-4B6OhTrwYKHYYgcwErvZjbmH9X5TxQBsaBHdzEIB4l71gR5jh/tuHGlb9in47udL2+wVUcOz5XXhhfhVJwEpEg==", "dev": true, "license": "MIT", "dependencies": { @@ -1750,8 +1677,6 @@ }, "node_modules/@babel/plugin-transform-async-to-generator": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.7.tgz", - "integrity": "sha512-ZUCjAavsh5CESCmi/xCpX1qcCaAglzs/7tmuvoFnJgA1dM7gQplsguljoTg+Ru8WENpX89cQyAtWoaE0I3X3Pg==", "dev": true, "license": "MIT", "dependencies": { @@ -1768,8 +1693,6 @@ }, "node_modules/@babel/plugin-transform-block-scoped-functions": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.7.tgz", - "integrity": "sha512-xHttvIM9fvqW+0a3tZlYcZYSBpSWzGBFIt/sYG3tcdSzBB8ZeVgz2gBP7Df+sM0N1850jrviYSSeUuc+135dmQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1784,8 +1707,6 @@ }, "node_modules/@babel/plugin-transform-block-scoping": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.7.tgz", - "integrity": "sha512-ZEPJSkVZaeTFG/m2PARwLZQ+OG0vFIhPlKHK/JdIMy8DbRJ/htz6LRrTFtdzxi9EHmcwbNPAKDnadpNSIW+Aow==", "dev": true, "license": "MIT", "dependencies": { @@ -1800,8 +1721,6 @@ }, "node_modules/@babel/plugin-transform-class-properties": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.7.tgz", - "integrity": "sha512-mhyfEW4gufjIqYFo9krXHJ3ElbFLIze5IDp+wQTxoPd+mwFb1NxatNAwmv8Q8Iuxv7Zc+q8EkiMQwc9IhyGf4g==", "dev": true, "license": "MIT", "dependencies": { @@ -1817,8 +1736,6 @@ }, "node_modules/@babel/plugin-transform-class-static-block": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.25.7.tgz", - "integrity": "sha512-rvUUtoVlkDWtDWxGAiiQj0aNktTPn3eFynBcMC2IhsXweehwgdI9ODe+XjWw515kEmv22sSOTp/rxIRuTiB7zg==", "dev": true, "license": "MIT", "dependencies": { @@ -1835,8 +1752,6 @@ }, "node_modules/@babel/plugin-transform-classes": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.7.tgz", - "integrity": "sha512-9j9rnl+YCQY0IGoeipXvnk3niWicIB6kCsWRGLwX241qSXpbA4MKxtp/EdvFxsc4zI5vqfLxzOd0twIJ7I99zg==", "dev": true, "license": "MIT", "dependencies": { @@ -1856,8 +1771,6 @@ }, "node_modules/@babel/plugin-transform-computed-properties": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.7.tgz", - "integrity": "sha512-QIv+imtM+EtNxg/XBKL3hiWjgdLjMOmZ+XzQwSgmBfKbfxUjBzGgVPklUuE55eq5/uVoh8gg3dqlrwR/jw3ZeA==", "dev": true, "license": "MIT", "dependencies": { @@ -1873,8 +1786,6 @@ }, "node_modules/@babel/plugin-transform-destructuring": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.7.tgz", - "integrity": "sha512-xKcfLTlJYUczdaM1+epcdh1UGewJqr9zATgrNHcLBcV2QmfvPPEixo/sK/syql9cEmbr7ulu5HMFG5vbbt/sEA==", "dev": true, "license": "MIT", "dependencies": { @@ -1889,8 +1800,6 @@ }, "node_modules/@babel/plugin-transform-dotall-regex": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.7.tgz", - "integrity": "sha512-kXzXMMRzAtJdDEgQBLF4oaiT6ZCU3oWHgpARnTKDAqPkDJ+bs3NrZb310YYevR5QlRo3Kn7dzzIdHbZm1VzJdQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1906,8 +1815,6 @@ }, "node_modules/@babel/plugin-transform-duplicate-keys": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.7.tgz", - "integrity": "sha512-by+v2CjoL3aMnWDOyCIg+yxU9KXSRa9tN6MbqggH5xvymmr9p4AMjYkNlQy4brMceBnUyHZ9G8RnpvT8wP7Cfg==", "dev": true, "license": "MIT", "dependencies": { @@ -1922,8 +1829,6 @@ }, "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.7.tgz", - "integrity": "sha512-HvS6JF66xSS5rNKXLqkk7L9c/jZ/cdIVIcoPVrnl8IsVpLggTjXs8OWekbLHs/VtYDDh5WXnQyeE3PPUGm22MA==", "dev": true, "license": "MIT", "dependencies": { @@ -1939,8 +1844,6 @@ }, "node_modules/@babel/plugin-transform-dynamic-import": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.7.tgz", - "integrity": "sha512-UvcLuual4h7/GfylKm2IAA3aph9rwvAM2XBA0uPKU3lca+Maai4jBjjEVUS568ld6kJcgbouuumCBhMd/Yz17w==", "dev": true, "license": "MIT", "dependencies": { @@ -1956,8 +1859,6 @@ }, "node_modules/@babel/plugin-transform-exponentiation-operator": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.7.tgz", - "integrity": "sha512-yjqtpstPfZ0h/y40fAXRv2snciYr0OAoMXY/0ClC7tm4C/nG5NJKmIItlaYlLbIVAWNfrYuy9dq1bE0SbX0PEg==", "dev": true, "license": "MIT", "dependencies": { @@ -1973,8 +1874,6 @@ }, "node_modules/@babel/plugin-transform-export-namespace-from": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.7.tgz", - "integrity": "sha512-h3MDAP5l34NQkkNulsTNyjdaR+OiB0Im67VU//sFupouP8Q6m9Spy7l66DcaAQxtmCqGdanPByLsnwFttxKISQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2005,8 +1904,6 @@ }, "node_modules/@babel/plugin-transform-for-of": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.7.tgz", - "integrity": "sha512-n/TaiBGJxYFWvpJDfsxSj9lEEE44BFM1EPGz4KEiTipTgkoFVVcCmzAL3qA7fdQU96dpo4gGf5HBx/KnDvqiHw==", "dev": true, "license": "MIT", "dependencies": { @@ -2022,8 +1919,6 @@ }, "node_modules/@babel/plugin-transform-function-name": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.7.tgz", - "integrity": "sha512-5MCTNcjCMxQ63Tdu9rxyN6cAWurqfrDZ76qvVPrGYdBxIj+EawuuxTu/+dgJlhK5eRz3v1gLwp6XwS8XaX2NiQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2040,8 +1935,6 @@ }, "node_modules/@babel/plugin-transform-json-strings": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.7.tgz", - "integrity": "sha512-Ot43PrL9TEAiCe8C/2erAjXMeVSnE/BLEx6eyrKLNFCCw5jvhTHKyHxdI1pA0kz5njZRYAnMO2KObGqOCRDYSA==", "dev": true, "license": "MIT", "dependencies": { @@ -2057,8 +1950,6 @@ }, "node_modules/@babel/plugin-transform-literals": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.7.tgz", - "integrity": "sha512-fwzkLrSu2fESR/cm4t6vqd7ebNIopz2QHGtjoU+dswQo/P6lwAG04Q98lliE3jkz/XqnbGFLnUcE0q0CVUf92w==", "dev": true, "license": "MIT", "dependencies": { @@ -2073,8 +1964,6 @@ }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.7.tgz", - "integrity": "sha512-iImzbA55BjiovLyG2bggWS+V+OLkaBorNvc/yJoeeDQGztknRnDdYfp2d/UPmunZYEnZi6Lg8QcTmNMHOB0lGA==", "dev": true, "license": "MIT", "dependencies": { @@ -2090,8 +1979,6 @@ }, "node_modules/@babel/plugin-transform-member-expression-literals": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.7.tgz", - "integrity": "sha512-Std3kXwpXfRV0QtQy5JJcRpkqP8/wG4XL7hSKZmGlxPlDqmpXtEPRmhF7ztnlTCtUN3eXRUJp+sBEZjaIBVYaw==", "dev": true, "license": "MIT", "dependencies": { @@ -2106,8 +1993,6 @@ }, "node_modules/@babel/plugin-transform-modules-amd": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.7.tgz", - "integrity": "sha512-CgselSGCGzjQvKzghCvDTxKHP3iooenLpJDO842ehn5D2G5fJB222ptnDwQho0WjEvg7zyoxb9P+wiYxiJX5yA==", "dev": true, "license": "MIT", "dependencies": { @@ -2123,8 +2008,6 @@ }, "node_modules/@babel/plugin-transform-modules-commonjs": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.7.tgz", - "integrity": "sha512-L9Gcahi0kKFYXvweO6n0wc3ZG1ChpSFdgG+eV1WYZ3/dGbJK7vvk91FgGgak8YwRgrCuihF8tE/Xg07EkL5COg==", "dev": true, "license": "MIT", "dependencies": { @@ -2141,8 +2024,6 @@ }, "node_modules/@babel/plugin-transform-modules-systemjs": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.7.tgz", - "integrity": "sha512-t9jZIvBmOXJsiuyOwhrIGs8dVcD6jDyg2icw1VL4A/g+FnWyJKwUfSSU2nwJuMV2Zqui856El9u+ElB+j9fV1g==", "dev": true, "license": "MIT", "dependencies": { @@ -2160,8 +2041,6 @@ }, "node_modules/@babel/plugin-transform-modules-umd": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.7.tgz", - "integrity": "sha512-p88Jg6QqsaPh+EB7I9GJrIqi1Zt4ZBHUQtjw3z1bzEXcLh6GfPqzZJ6G+G1HBGKUNukT58MnKG7EN7zXQBCODw==", "dev": true, "license": "MIT", "dependencies": { @@ -2177,8 +2056,6 @@ }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.7.tgz", - "integrity": "sha512-BtAT9LzCISKG3Dsdw5uso4oV1+v2NlVXIIomKJgQybotJY3OwCwJmkongjHgwGKoZXd0qG5UZ12JUlDQ07W6Ow==", "dev": true, "license": "MIT", "dependencies": { @@ -2194,8 +2071,6 @@ }, "node_modules/@babel/plugin-transform-new-target": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.7.tgz", - "integrity": "sha512-CfCS2jDsbcZaVYxRFo2qtavW8SpdzmBXC2LOI4oO0rP+JSRDxxF3inF4GcPsLgfb5FjkhXG5/yR/lxuRs2pySA==", "dev": true, "license": "MIT", "dependencies": { @@ -2210,8 +2085,6 @@ }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.7.tgz", - "integrity": "sha512-FbuJ63/4LEL32mIxrxwYaqjJxpbzxPVQj5a+Ebrc8JICV6YX8nE53jY+K0RZT3um56GoNWgkS2BQ/uLGTjtwfw==", "dev": true, "license": "MIT", "dependencies": { @@ -2227,8 +2100,6 @@ }, "node_modules/@babel/plugin-transform-numeric-separator": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.7.tgz", - "integrity": "sha512-8CbutzSSh4hmD+jJHIA8vdTNk15kAzOnFLVVgBSMGr28rt85ouT01/rezMecks9pkU939wDInImwCKv4ahU4IA==", "dev": true, "license": "MIT", "dependencies": { @@ -2244,8 +2115,6 @@ }, "node_modules/@babel/plugin-transform-object-rest-spread": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.7.tgz", - "integrity": "sha512-1JdVKPhD7Y5PvgfFy0Mv2brdrolzpzSoUq2pr6xsR+m+3viGGeHEokFKsCgOkbeFOQxfB1Vt2F0cPJLRpFI4Zg==", "dev": true, "license": "MIT", "dependencies": { @@ -2263,8 +2132,6 @@ }, "node_modules/@babel/plugin-transform-object-super": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.7.tgz", - "integrity": "sha512-pWT6UXCEW3u1t2tcAGtE15ornCBvopHj9Bps9D2DsH15APgNVOTwwczGckX+WkAvBmuoYKRCFa4DK+jM8vh5AA==", "dev": true, "license": "MIT", "dependencies": { @@ -2280,8 +2147,6 @@ }, "node_modules/@babel/plugin-transform-optional-catch-binding": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.7.tgz", - "integrity": "sha512-m9obYBA39mDPN7lJzD5WkGGb0GO54PPLXsbcnj1Hyeu8mSRz7Gb4b1A6zxNX32ZuUySDK4G6it8SDFWD1nCnqg==", "dev": true, "license": "MIT", "dependencies": { @@ -2297,8 +2162,6 @@ }, "node_modules/@babel/plugin-transform-optional-chaining": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.7.tgz", - "integrity": "sha512-h39agClImgPWg4H8mYVAbD1qP9vClFbEjqoJmt87Zen8pjqK8FTPUwrOXAvqu5soytwxrLMd2fx2KSCp2CHcNg==", "dev": true, "license": "MIT", "dependencies": { @@ -2315,8 +2178,6 @@ }, "node_modules/@babel/plugin-transform-parameters": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.7.tgz", - "integrity": "sha512-FYiTvku63me9+1Nz7TOx4YMtW3tWXzfANZtrzHhUZrz4d47EEtMQhzFoZWESfXuAMMT5mwzD4+y1N8ONAX6lMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2331,8 +2192,6 @@ }, "node_modules/@babel/plugin-transform-private-methods": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.7.tgz", - "integrity": "sha512-KY0hh2FluNxMLwOCHbxVOKfdB5sjWG4M183885FmaqWWiGMhRZq4DQRKH6mHdEucbJnyDyYiZNwNG424RymJjA==", "dev": true, "license": "MIT", "dependencies": { @@ -2348,8 +2207,6 @@ }, "node_modules/@babel/plugin-transform-private-property-in-object": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.7.tgz", - "integrity": "sha512-LzA5ESzBy7tqj00Yjey9yWfs3FKy4EmJyKOSWld144OxkTji81WWnUT8nkLUn+imN/zHL8ZQlOu/MTUAhHaX3g==", "dev": true, "license": "MIT", "dependencies": { @@ -2367,8 +2224,6 @@ }, "node_modules/@babel/plugin-transform-property-literals": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.7.tgz", - "integrity": "sha512-lQEeetGKfFi0wHbt8ClQrUSUMfEeI3MMm74Z73T9/kuz990yYVtfofjf3NuA42Jy3auFOpbjDyCSiIkTs1VIYw==", "dev": true, "license": "MIT", "dependencies": { @@ -2383,8 +2238,6 @@ }, "node_modules/@babel/plugin-transform-react-display-name": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.7.tgz", - "integrity": "sha512-r0QY7NVU8OnrwE+w2IWiRom0wwsTbjx4+xH2RTd7AVdof3uurXOF+/mXHQDRk+2jIvWgSaCHKMgggfvM4dyUGA==", "dev": true, "license": "MIT", "dependencies": { @@ -2399,8 +2252,6 @@ }, "node_modules/@babel/plugin-transform-react-jsx": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.7.tgz", - "integrity": "sha512-vILAg5nwGlR9EXE8JIOX4NHXd49lrYbN8hnjffDtoULwpL9hUx/N55nqh2qd0q6FyNDfjl9V79ecKGvFbcSA0Q==", "dev": true, "license": "MIT", "dependencies": { @@ -2419,8 +2270,6 @@ }, "node_modules/@babel/plugin-transform-react-jsx-development": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.7.tgz", - "integrity": "sha512-5yd3lH1PWxzW6IZj+p+Y4OLQzz0/LzlOG8vGqonHfVR3euf1vyzyMUJk9Ac+m97BH46mFc/98t9PmYLyvgL3qg==", "dev": true, "license": "MIT", "dependencies": { @@ -2435,8 +2284,6 @@ }, "node_modules/@babel/plugin-transform-react-pure-annotations": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.7.tgz", - "integrity": "sha512-6YTHJ7yjjgYqGc8S+CbEXhLICODk0Tn92j+vNJo07HFk9t3bjFgAKxPLFhHwF2NjmQVSI1zBRfBWUeVBa2osfA==", "dev": true, "license": "MIT", "dependencies": { @@ -2452,8 +2299,6 @@ }, "node_modules/@babel/plugin-transform-regenerator": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.7.tgz", - "integrity": "sha512-mgDoQCRjrY3XK95UuV60tZlFCQGXEtMg8H+IsW72ldw1ih1jZhzYXbJvghmAEpg5UVhhnCeia1CkGttUvCkiMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2469,8 +2314,6 @@ }, "node_modules/@babel/plugin-transform-reserved-words": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.7.tgz", - "integrity": "sha512-3OfyfRRqiGeOvIWSagcwUTVk2hXBsr/ww7bLn6TRTuXnexA+Udov2icFOxFX9abaj4l96ooYkcNN1qi2Zvqwng==", "dev": true, "license": "MIT", "dependencies": { @@ -2504,8 +2347,6 @@ }, "node_modules/@babel/plugin-transform-shorthand-properties": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.7.tgz", - "integrity": "sha512-uBbxNwimHi5Bv3hUccmOFlUy3ATO6WagTApenHz9KzoIdn0XeACdB12ZJ4cjhuB2WSi80Ez2FWzJnarccriJeA==", "dev": true, "license": "MIT", "dependencies": { @@ -2520,8 +2361,6 @@ }, "node_modules/@babel/plugin-transform-spread": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.7.tgz", - "integrity": "sha512-Mm6aeymI0PBh44xNIv/qvo8nmbkpZze1KvR8MkEqbIREDxoiWTi18Zr2jryfRMwDfVZF9foKh060fWgni44luw==", "dev": true, "license": "MIT", "dependencies": { @@ -2537,8 +2376,6 @@ }, "node_modules/@babel/plugin-transform-sticky-regex": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.7.tgz", - "integrity": "sha512-ZFAeNkpGuLnAQ/NCsXJ6xik7Id+tHuS+NT+ue/2+rn/31zcdnupCdmunOizEaP0JsUmTFSTOPoQY7PkK2pttXw==", "dev": true, "license": "MIT", "dependencies": { @@ -2553,8 +2390,6 @@ }, "node_modules/@babel/plugin-transform-template-literals": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.7.tgz", - "integrity": "sha512-SI274k0nUsFFmyQupiO7+wKATAmMFf8iFgq2O+vVFXZ0SV9lNfT1NGzBEhjquFmD8I9sqHLguH+gZVN3vww2AA==", "dev": true, "license": "MIT", "dependencies": { @@ -2569,8 +2404,6 @@ }, "node_modules/@babel/plugin-transform-typeof-symbol": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.7.tgz", - "integrity": "sha512-OmWmQtTHnO8RSUbL0NTdtpbZHeNTnm68Gj5pA4Y2blFNh+V4iZR68V1qL9cI37J21ZN7AaCnkfdHtLExQPf2uA==", "dev": true, "license": "MIT", "dependencies": { @@ -2585,8 +2418,6 @@ }, "node_modules/@babel/plugin-transform-typescript": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.7.tgz", - "integrity": "sha512-VKlgy2vBzj8AmEzunocMun2fF06bsSWV+FvVXohtL6FGve/+L217qhHxRTVGHEDO/YR8IANcjzgJsd04J8ge5Q==", "dev": true, "license": "MIT", "dependencies": { @@ -2605,8 +2436,6 @@ }, "node_modules/@babel/plugin-transform-unicode-escapes": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.7.tgz", - "integrity": "sha512-BN87D7KpbdiABA+t3HbVqHzKWUDN3dymLaTnPFAMyc8lV+KN3+YzNhVRNdinaCPA4AUqx7ubXbQ9shRjYBl3SQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2621,8 +2450,6 @@ }, "node_modules/@babel/plugin-transform-unicode-property-regex": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.7.tgz", - "integrity": "sha512-IWfR89zcEPQGB/iB408uGtSPlQd3Jpq11Im86vUgcmSTcoWAiQMCTOa2K2yNNqFJEBVICKhayctee65Ka8OB0w==", "dev": true, "license": "MIT", "dependencies": { @@ -2638,8 +2465,6 @@ }, "node_modules/@babel/plugin-transform-unicode-regex": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.7.tgz", - "integrity": "sha512-8JKfg/hiuA3qXnlLx8qtv5HWRbgyFx2hMMtpDDuU2rTckpKkGu4ycK5yYHwuEa16/quXfoxHBIApEsNyMWnt0g==", "dev": true, "license": "MIT", "dependencies": { @@ -2655,8 +2480,6 @@ }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.7.tgz", - "integrity": "sha512-YRW8o9vzImwmh4Q3Rffd09bH5/hvY0pxg+1H1i0f7APoUeg12G7+HhLj9ZFNIrYkgBXhIijPJ+IXypN0hLTIbw==", "dev": true, "license": "MIT", "dependencies": { @@ -2672,8 +2495,6 @@ }, "node_modules/@babel/preset-env": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.7.tgz", - "integrity": "sha512-Gibz4OUdyNqqLj+7OAvBZxOD7CklCtMA5/j0JgUEwOnaRULsPDXmic2iKxL2DX2vQduPR5wH2hjZas/Vr/Oc0g==", "dev": true, "license": "MIT", "dependencies": { @@ -2829,8 +2650,6 @@ }, "node_modules/@babel/preset-react": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.25.7.tgz", - "integrity": "sha512-GjV0/mUEEXpi1U5ZgDprMRRgajGMRW3G5FjMr5KLKD8nT2fTG8+h/klV3+6Dm5739QE+K5+2e91qFKAYI3pmRg==", "dev": true, "license": "MIT", "dependencies": { @@ -2850,8 +2669,6 @@ }, "node_modules/@babel/preset-typescript": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.25.7.tgz", - "integrity": "sha512-rkkpaXJZOFN45Fb+Gki0c+KMIglk4+zZXOoMJuyEK8y8Kkc8Jd3BDmP7qPsz0zQMJj+UD7EprF+AqAXcILnexw==", "dev": true, "license": "MIT", "dependencies": { @@ -2870,8 +2687,6 @@ }, "node_modules/@babel/register": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.25.7.tgz", - "integrity": "sha512-qHTd2Rhn/rKhSUwdY6+n98FmwXN+N+zxSVx3zWqRe9INyvTpv+aQ5gDV2+43ACd3VtMBzPPljbb0gZb8u5ma6Q==", "dev": true, "license": "MIT", "dependencies": { @@ -3009,8 +2824,6 @@ }, "node_modules/@babel/template": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", - "integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==", "dev": true, "license": "MIT", "dependencies": { @@ -3024,8 +2837,6 @@ }, "node_modules/@babel/traverse": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz", - "integrity": "sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==", "dev": true, "license": "MIT", "dependencies": { @@ -3043,8 +2854,6 @@ }, "node_modules/@babel/types": { "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.7.tgz", - "integrity": "sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3061,10 +2870,20 @@ "dev": true, "license": "MIT" }, + "node_modules/@capacitor-community/bluetooth-le": { + "version": "6.0.1", + "resolved": "git+ssh://git@github.com/mytonwallet-org/capacitor-bluetooth-le.git#0ac4f47716a9fbccbd7184384dcfab69d8fede97", + "integrity": "sha512-2M12dnEzjA97rqv4BgZ4WV1L9ZDbcjyi2/d+xPia9QkXXr0e7jG28Ig47cu5XRGp7ToXbPocO5iq8Ez1r6vn6w==", + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.20" + }, + "peerDependencies": { + "@capacitor/core": "^6.0.0" + } + }, "node_modules/@capacitor-mlkit/barcode-scanning": { "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@capacitor-mlkit/barcode-scanning/-/barcode-scanning-6.2.0.tgz", - "integrity": "sha512-XnnErDabpCUty9flugqB646ERejCxrtKcKOJrdoh9ZVLTQXUnyjxUDWOlqHVxrBHy+e86ZgpZX7D5zcaNvS0lQ==", "funding": [ { "type": "github", @@ -3518,7 +3337,7 @@ "node_modules/@capgo/capacitor-native-biometric": { "version": "6.0.0", "resolved": "git+ssh://git@github.com/mytonwallet-org/capacitor-native-biometric.git#956d06d1ab78a839f1293921d0bff449edf4ef4d", - "integrity": "sha512-shjTAjynu9dqrcrk9tJ0DToNNjpFgiSY4Jdvwx/9ogsoasJrqfG/Lcp8S4L2tc+godJoHI2xzRYwp6HVri66DA==", + "integrity": "sha512-Y6Kuu/QK7nKmnOD7MR+88dTpu/OWJzIDft7+ml/RYNWcRrFkwzGhehxW7ewMlpkw3Dv8NowiC5IHu+2qrb1DBg==", "license": "MIT", "peerDependencies": { "@capacitor/core": "^6.0.0" @@ -3526,8 +3345,6 @@ }, "node_modules/@capgo/native-audio": { "version": "6.4.21", - "resolved": "https://registry.npmjs.org/@capgo/native-audio/-/native-audio-6.4.21.tgz", - "integrity": "sha512-ukvJrENU0DYXN8lm4JYKtAaFiqp4DI5yKfoEuW0CcZQcBCp/m1brm0kVC3/dQ/XYuVvabHhx9mh4aYxmafpbMQ==", "license": "MIT", "peerDependencies": { "@capacitor/core": "^6.0.0" @@ -3550,8 +3367,6 @@ }, "node_modules/@develar/schema-utils": { "version": "2.6.5", - "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", - "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==", "dev": true, "license": "MIT", "dependencies": { @@ -3584,8 +3399,6 @@ }, "node_modules/@electron/asar": { "version": "3.2.13", - "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.13.tgz", - "integrity": "sha512-pY5z2qQSwbFzJsBdgfJIzXf5ElHTVMutC2dxh0FD60njknMu3n1NnTABOcQwbb5/v5soqE79m9UjaJryBf3epg==", "dev": true, "license": "MIT", "dependencies": { @@ -3603,8 +3416,6 @@ }, "node_modules/@electron/asar/node_modules/commander": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", "dev": true, "license": "MIT", "engines": { @@ -3642,7 +3453,7 @@ "node_modules/@electron/node-gyp": { "version": "10.2.0-electron.1", "resolved": "git+ssh://git@github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2", - "integrity": "sha512-CrYo6TntjpoMO1SHjl5Pa/JoUsECNqNdB7Kx49WLQpWzPw53eEITJ2Hs9fh/ryUYDn4pxZz11StaBYBrLFJdqg==", + "integrity": "sha512-lBSgDMQqt7QWMuIjS8zNAq5FI5o5RVBAcJUGWGI6GgoQITJt3msAkUrHp8YHj3RTVE+h70ndqMGqURjp3IfRyQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3666,8 +3477,6 @@ }, "node_modules/@electron/node-gyp/node_modules/brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "license": "MIT", "dependencies": { @@ -3676,9 +3485,6 @@ }, "node_modules/@electron/node-gyp/node_modules/glob": { "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", "dependencies": { @@ -3697,8 +3503,6 @@ }, "node_modules/@electron/node-gyp/node_modules/minimatch": { "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "license": "ISC", "dependencies": { @@ -3710,8 +3514,6 @@ }, "node_modules/@electron/notarize": { "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.5.0.tgz", - "integrity": "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==", "dev": true, "license": "MIT", "dependencies": { @@ -3725,8 +3527,6 @@ }, "node_modules/@electron/notarize/node_modules/fs-extra": { "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3741,8 +3541,6 @@ }, "node_modules/@electron/notarize/node_modules/jsonfile": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3754,8 +3552,6 @@ }, "node_modules/@electron/notarize/node_modules/universalify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "license": "MIT", "engines": { @@ -3764,8 +3560,6 @@ }, "node_modules/@electron/osx-sign": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.1.tgz", - "integrity": "sha512-BAfviURMHpmb1Yb50YbCxnOY0wfwaLXH5KJ4+80zS0gUkzDX3ec23naTlEqKsN+PwYn+a1cCzM7BJ4Wcd3sGzw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -3786,8 +3580,6 @@ }, "node_modules/@electron/osx-sign/node_modules/fs-extra": { "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3801,8 +3593,6 @@ }, "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { "version": "4.0.10", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", - "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", "dev": true, "license": "MIT", "engines": { @@ -3814,8 +3604,6 @@ }, "node_modules/@electron/osx-sign/node_modules/jsonfile": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3827,8 +3615,6 @@ }, "node_modules/@electron/osx-sign/node_modules/universalify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "license": "MIT", "engines": { @@ -3837,12 +3623,10 @@ }, "node_modules/@electron/rebuild": { "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-3.7.0.tgz", - "integrity": "sha512-VW++CNSlZwMYP7MyXEbrKjpzEwhB5kDNbzGtiPEjwYysqyTCF+YbNJ210Dj3AjWsGSV4iEEwNkmJN9yGZmVvmw==", "dev": true, "license": "MIT", "dependencies": { - "@electron/node-gyp": "git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2", + "@electron/node-gyp": "https://github.com/electron/node-gyp#06b29aafb7708acef8b3669835c8a7857ebc92d2", "@malept/cross-spawn-promise": "^2.0.0", "chalk": "^4.0.0", "debug": "^4.1.1", @@ -3962,8 +3746,6 @@ }, "node_modules/@electron/universal": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.1.tgz", - "integrity": "sha512-fKpv9kg4SPmt+hY7SVBnIYULE9QJl8L3sCfcBsnqbJwwBwAeTLokJ9TRt9y7bK0JAzIW2y78TVVjvnQEms/yyA==", "dev": true, "license": "MIT", "dependencies": { @@ -3981,8 +3763,6 @@ }, "node_modules/@electron/universal/node_modules/brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "license": "MIT", "dependencies": { @@ -3991,8 +3771,6 @@ }, "node_modules/@electron/universal/node_modules/fs-extra": { "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "dev": true, "license": "MIT", "dependencies": { @@ -4006,8 +3784,6 @@ }, "node_modules/@electron/universal/node_modules/jsonfile": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4019,8 +3795,6 @@ }, "node_modules/@electron/universal/node_modules/minimatch": { "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", "dependencies": { @@ -4035,8 +3809,6 @@ }, "node_modules/@electron/universal/node_modules/universalify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "license": "MIT", "engines": { @@ -5302,8 +5074,6 @@ }, "node_modules/@ledgerhq/devices": { "version": "8.4.4", - "resolved": "https://registry.npmjs.org/@ledgerhq/devices/-/devices-8.4.4.tgz", - "integrity": "sha512-sz/ryhe/R687RHtevIE9RlKaV8kkKykUV4k29e7GAVwzHX1gqG+O75cu1NCJUHLbp3eABV5FdvZejqRUlLis9A==", "license": "Apache-2.0", "dependencies": { "@ledgerhq/errors": "^6.19.1", @@ -5314,14 +5084,10 @@ }, "node_modules/@ledgerhq/errors": { "version": "6.19.1", - "resolved": "https://registry.npmjs.org/@ledgerhq/errors/-/errors-6.19.1.tgz", - "integrity": "sha512-75yK7Nnit/Gp7gdrJAz0ipp31CCgncRp+evWt6QawQEtQKYEDfGo10QywgrrBBixeRxwnMy1DP6g2oCWRf1bjw==", "license": "Apache-2.0" }, "node_modules/@ledgerhq/hw-transport": { "version": "6.31.4", - "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-6.31.4.tgz", - "integrity": "sha512-6c1ir/cXWJm5dCWdq55NPgCJ3UuKuuxRvf//Xs36Bq9BwkV2YaRQhZITAkads83l07NAdR16hkTWqqpwFMaI6A==", "license": "Apache-2.0", "dependencies": { "@ledgerhq/devices": "^8.4.4", @@ -5332,8 +5098,6 @@ }, "node_modules/@ledgerhq/hw-transport-webhid": { "version": "6.29.4", - "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-webhid/-/hw-transport-webhid-6.29.4.tgz", - "integrity": "sha512-XkF37lcuyg9zVExMyfDQathWly8rRcGac13wgZATBa3nZ+hUzzWr5QVKg1pKCw10izVHGErW/9a4tbb72rUEmQ==", "license": "Apache-2.0", "dependencies": { "@ledgerhq/devices": "^8.4.4", @@ -5386,8 +5150,6 @@ }, "node_modules/@malept/flatpak-bundler": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", - "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==", "dev": true, "license": "MIT", "dependencies": { @@ -5402,8 +5164,6 @@ }, "node_modules/@malept/flatpak-bundler/node_modules/fs-extra": { "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5418,8 +5178,6 @@ }, "node_modules/@malept/flatpak-bundler/node_modules/jsonfile": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5431,8 +5189,6 @@ }, "node_modules/@malept/flatpak-bundler/node_modules/universalify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "license": "MIT", "engines": { @@ -5442,7 +5198,7 @@ "node_modules/@mauricewegner/capacitor-navigation-bar": { "version": "2.0.3", "resolved": "git+ssh://git@github.com/mytonwallet-org/capacitor-navigation-bar.git#0d0af23c191f86819a01c0665c1592774c66a91a", - "integrity": "sha512-rld1E53LvHxkib2A7vu2QTE/CtNoglygYMH2AEtFGtsVeYiW/QUGXMAwEfIAjr5/k+KcqYO8KC9qJi9ES0RnrQ==", + "integrity": "sha512-MxnZkvOd+neP8hu2szyqn12uR8A04JE44XU46tz6sTjqo9bo6t6dJgTKSjJvrCWhlvYraFHn59b1054q42y+SA==", "license": "MIT", "peerDependencies": { "@capacitor/core": "^6.0.0" @@ -5563,8 +5319,6 @@ }, "node_modules/@playwright/test": { "version": "1.48.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.0.tgz", - "integrity": "sha512-W5lhqPUVPqhtc/ySvZI5Q8X2ztBOUgZ8LbAFy0JQgrXZs2xaILrUcNO3rQjwbLPfGK13+rZsDa1FpG+tqYkT5w==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5577,38 +5331,8 @@ "node": ">=18" } }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", - "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", - "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, "node_modules/@rollup/rollup-darwin-arm64": { "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", - "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", "cpu": [ "arm64" ], @@ -5619,188 +5343,6 @@ "darwin" ] }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", - "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", - "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", - "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", - "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", - "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", - "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", - "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", - "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", - "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", - "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", - "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", - "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", - "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@rtsao/scc": { "version": "1.1.0", "dev": true, @@ -6380,8 +5922,6 @@ }, "node_modules/@ton/core": { "version": "0.59.0", - "resolved": "https://registry.npmjs.org/@ton/core/-/core-0.59.0.tgz", - "integrity": "sha512-LSIkGst7BoY7fMWshejzcH0UJnoW21JGlRrW0ch+6A7Xb/7EuekxgdKym7fHxcry6OIf6FoeFg97lJ960N/Ghg==", "license": "MIT", "dependencies": { "symbol.inspect": "1.0.1" @@ -6409,7 +5949,7 @@ "node_modules/@ton/ton": { "version": "13.11.2", "resolved": "git+ssh://git@github.com/mytonwallet-org/tonkeeper-ton.git#0d0fe1cd7ccfe1b87c1083b157a158aa0f50cda8", - "integrity": "sha512-9fGiEJ3JYrOHLsZSfU806LmF3KMe24jiDwftQ3+trjbaCvvND0LMhJacmOrIMy72b4lL6YvOFgl3ywRzgjjVog==", + "integrity": "sha512-BsJbOWVoqJpuRiQApiLydeLvtSqLwLqD4J85R8oChaW0/0c3KhoHIRTQy8W1o84Q1IqbgusuxIMpOY13imyinw==", "license": "MIT", "dependencies": { "axios": "^1.6.7", @@ -6554,8 +6094,6 @@ }, "node_modules/@types/chrome": { "version": "0.0.277", - "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.277.tgz", - "integrity": "sha512-qoTgBcDWblSsX+jvFnpUlLUE3LAuOhZfBh9MyMWMQHDsQiYVgBvdZWu9COrdB9+aNnInEyXcFgfc2HE16sdSYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6596,8 +6134,6 @@ }, "node_modules/@types/debug": { "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6646,8 +6182,6 @@ }, "node_modules/@types/fs-extra": { "version": "9.0.13", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", - "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", "dev": true, "license": "MIT", "dependencies": { @@ -6656,8 +6190,6 @@ }, "node_modules/@types/glob": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", "dev": true, "license": "MIT", "dependencies": { @@ -6724,8 +6256,6 @@ }, "node_modules/@types/jest": { "version": "29.5.13", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.13.tgz", - "integrity": "sha512-wd+MVEZCHt23V0/L642O5APvspWply/rGY5BcW4SUETo2UzPU3Z26qr8jC2qxpimI2jjx9h7+2cj2FwIr01bXg==", "dev": true, "license": "MIT", "dependencies": { @@ -6768,8 +6298,6 @@ }, "node_modules/@types/minimatch": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", "dev": true, "license": "MIT" }, @@ -6780,15 +6308,11 @@ }, "node_modules/@types/ms": { "version": "0.7.34", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", - "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", "dev": true, "license": "MIT" }, "node_modules/@types/node": { "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", - "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6820,8 +6344,6 @@ }, "node_modules/@types/plist": { "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz", - "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", "dev": true, "license": "MIT", "optional": true, @@ -6847,8 +6369,6 @@ }, "node_modules/@types/react": { "version": "18.3.11", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.11.tgz", - "integrity": "sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6935,14 +6455,25 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/verror": { "version": "1.10.10", - "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.10.tgz", - "integrity": "sha512-l4MM0Jppn18hb9xmM6wwD1uTdShpf9Pn80aXTStnK1C94gtPvJcV2FrDmbOQUAQfJ1cKZHktkQUDwEqaAKXMMg==", "dev": true, "license": "MIT", "optional": true }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz", + "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==", + "license": "MIT" + }, "node_modules/@types/webextension-polyfill": { "version": "0.12.1", "dev": true, @@ -7696,8 +7227,6 @@ }, "node_modules/7zip-bin": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz", - "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==", "dev": true, "license": "MIT" }, @@ -7936,15 +7465,11 @@ }, "node_modules/app-builder-bin": { "version": "5.0.0-alpha.10", - "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-5.0.0-alpha.10.tgz", - "integrity": "sha512-Ev4jj3D7Bo+O0GPD2NMvJl+PGiBAfS7pUGawntBNpCbxtpncfUixqFj9z9Jme7V7s3LBGqsWZZP54fxBX3JKJw==", "dev": true, "license": "MIT" }, "node_modules/app-builder-lib": { "version": "25.1.7", - "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-25.1.7.tgz", - "integrity": "sha512-JxmN+D/Dn7BLQoN+cTFO+zbMHcpI10v/xjyjFO1FKpHbApOG+OQt/xUyVjKWp4FYplIfuHdpxqTXo1PN/Wzm/A==", "dev": true, "license": "MIT", "dependencies": { @@ -7991,8 +7516,6 @@ }, "node_modules/app-builder-lib/node_modules/@electron/rebuild": { "version": "3.6.1", - "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-3.6.1.tgz", - "integrity": "sha512-f6596ZHpEq/YskUd8emYvOUne89ij8mQgjYFA5ru25QwbrRO+t1SImofdDv7kKOuWCmVOuU5tvfkbgGxIl3E/w==", "dev": true, "license": "MIT", "dependencies": { @@ -8020,8 +7543,6 @@ }, "node_modules/app-builder-lib/node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", "dependencies": { @@ -8036,8 +7557,6 @@ }, "node_modules/app-builder-lib/node_modules/brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "license": "MIT", "dependencies": { @@ -8046,8 +7565,6 @@ }, "node_modules/app-builder-lib/node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { @@ -8063,8 +7580,6 @@ }, "node_modules/app-builder-lib/node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8076,15 +7591,11 @@ }, "node_modules/app-builder-lib/node_modules/color-name": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, "license": "MIT" }, "node_modules/app-builder-lib/node_modules/fs-extra": { "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8098,8 +7609,6 @@ }, "node_modules/app-builder-lib/node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", "engines": { @@ -8108,8 +7617,6 @@ }, "node_modules/app-builder-lib/node_modules/jsonfile": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8121,8 +7628,6 @@ }, "node_modules/app-builder-lib/node_modules/minimatch": { "version": "10.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", "dev": true, "license": "ISC", "dependencies": { @@ -8137,8 +7642,6 @@ }, "node_modules/app-builder-lib/node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { @@ -8150,8 +7653,6 @@ }, "node_modules/app-builder-lib/node_modules/universalify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "license": "MIT", "engines": { @@ -8184,8 +7685,6 @@ }, "node_modules/archiver": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", - "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", "dev": true, "license": "MIT", "peer": true, @@ -8204,8 +7703,6 @@ }, "node_modules/archiver-utils": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", - "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", "dev": true, "license": "MIT", "peer": true, @@ -8227,16 +7724,12 @@ }, "node_modules/archiver-utils/node_modules/isarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true, "license": "MIT", "peer": true }, "node_modules/archiver-utils/node_modules/readable-stream": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "license": "MIT", "peer": true, @@ -8252,16 +7745,12 @@ }, "node_modules/archiver-utils/node_modules/safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, "license": "MIT", "peer": true }, "node_modules/archiver-utils/node_modules/string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "license": "MIT", "peer": true, @@ -8477,8 +7966,6 @@ }, "node_modules/assert-plus": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true, "license": "MIT", "optional": true, @@ -8501,15 +7988,11 @@ }, "node_modules/async": { "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "dev": true, "license": "MIT" }, "node_modules/async-exit-hook": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", - "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", "dev": true, "license": "MIT", "engines": { @@ -8530,8 +8013,6 @@ }, "node_modules/atomically": { "version": "1.7.0", - "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", - "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==", "dev": true, "license": "MIT", "engines": { @@ -8540,8 +8021,6 @@ }, "node_modules/autoprefixer": { "version": "10.4.20", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", - "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", "dev": true, "funding": [ { @@ -8600,8 +8079,6 @@ }, "node_modules/axios": { "version": "1.7.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -8703,8 +8180,6 @@ }, "node_modules/babel-loader": { "version": "9.2.1", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz", - "integrity": "sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==", "dev": true, "license": "MIT", "dependencies": { @@ -9018,15 +8493,11 @@ }, "node_modules/bluebird": { "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true, "license": "MIT" }, "node_modules/bluebird-lst": { "version": "1.0.9", - "resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.9.tgz", - "integrity": "sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==", "dev": true, "license": "MIT", "dependencies": { @@ -9247,8 +8718,6 @@ }, "node_modules/browserslist": { "version": "4.24.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", - "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", "dev": true, "funding": [ { @@ -9334,8 +8803,6 @@ }, "node_modules/builder-util": { "version": "25.1.7", - "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-25.1.7.tgz", - "integrity": "sha512-7jPjzBwEGRbwNcep0gGNpLXG9P94VA3CPAZQCzxkFXiV2GMQKlziMbY//rXPI7WKfhsvGgFXjTcXdBEwgXw9ww==", "dev": true, "license": "MIT", "dependencies": { @@ -9359,8 +8826,6 @@ }, "node_modules/builder-util-runtime": { "version": "9.2.10", - "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.10.tgz", - "integrity": "sha512-6p/gfG1RJSQeIbz8TK5aPNkoztgY1q5TgmGFMAXcY8itsGW6Y2ld1ALsZ5UJn8rog7hKF3zHx5iQbNQ8uLcRlw==", "dev": true, "license": "MIT", "dependencies": { @@ -9373,8 +8838,6 @@ }, "node_modules/builder-util/node_modules/agent-base": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dev": true, "license": "MIT", "dependencies": { @@ -9386,8 +8849,6 @@ }, "node_modules/builder-util/node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", "dependencies": { @@ -9402,8 +8863,6 @@ }, "node_modules/builder-util/node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { @@ -9419,8 +8878,6 @@ }, "node_modules/builder-util/node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9432,15 +8889,11 @@ }, "node_modules/builder-util/node_modules/color-name": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, "license": "MIT" }, "node_modules/builder-util/node_modules/fs-extra": { "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9454,8 +8907,6 @@ }, "node_modules/builder-util/node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", "engines": { @@ -9464,8 +8915,6 @@ }, "node_modules/builder-util/node_modules/http-proxy-agent": { "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, "license": "MIT", "dependencies": { @@ -9478,8 +8927,6 @@ }, "node_modules/builder-util/node_modules/https-proxy-agent": { "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", "dev": true, "license": "MIT", "dependencies": { @@ -9492,8 +8939,6 @@ }, "node_modules/builder-util/node_modules/jsonfile": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9505,8 +8950,6 @@ }, "node_modules/builder-util/node_modules/source-map-support": { "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "license": "MIT", "dependencies": { @@ -9516,8 +8959,6 @@ }, "node_modules/builder-util/node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { @@ -9529,8 +8970,6 @@ }, "node_modules/builder-util/node_modules/universalify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "license": "MIT", "engines": { @@ -9670,8 +9109,6 @@ }, "node_modules/caniuse-lite": { "version": "1.0.30001667", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001667.tgz", - "integrity": "sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==", "dev": true, "funding": [ { @@ -9706,7 +9143,7 @@ "node_modules/capacitor-secure-storage-plugin": { "version": "0.10.0", "resolved": "git+ssh://git@github.com/mytonwallet-org/capacitor-secure-storage-plugin.git#03a96f6c0e7207b302b1dcb2d13c0c9e2ae6cc29", - "integrity": "sha512-VYLkwCao0OQNDB0Ye9Qzcsef6y8pwikjhfC51M/ihvFXTBFsmHAyffja/5aRur8pDGewp6YEBswR5cewdSH5gg==", + "integrity": "sha512-T5q3xCSdTfqjMC5iuSH78kds5m3j1gcAGU097k/Y7d0fK/xCf+ywgxF+88gLPw17MlVqLITDapNDr2shyjAlFw==", "license": "MIT", "peerDependencies": { "@capacitor/core": "^6.0.0" @@ -9887,8 +9324,6 @@ }, "node_modules/chromium-pickle-js": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", - "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==", "dev": true, "license": "MIT" }, @@ -10223,8 +9658,6 @@ }, "node_modules/compare-version": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", - "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", "dev": true, "license": "MIT", "engines": { @@ -10233,8 +9666,6 @@ }, "node_modules/compress-commons": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", - "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", "dev": true, "license": "MIT", "peer": true, @@ -10405,8 +9836,6 @@ }, "node_modules/conf": { "version": "10.2.0", - "resolved": "https://registry.npmjs.org/conf/-/conf-10.2.0.tgz", - "integrity": "sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==", "dev": true, "license": "MIT", "dependencies": { @@ -10430,8 +9859,6 @@ }, "node_modules/conf/node_modules/ajv": { "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", "dependencies": { @@ -10447,15 +9874,11 @@ }, "node_modules/conf/node_modules/json-schema-traverse": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, "license": "MIT" }, "node_modules/config-file-ts": { "version": "0.2.8-rc1", - "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.8-rc1.tgz", - "integrity": "sha512-GtNECbVI82bT4RiDIzBSVuTKoSHufnU7Ce7/42bkWZJZFLjmDF2WBpVsvRkhKCfKBnTBb3qZrBwPpFBU/Myvhg==", "dev": true, "license": "MIT", "dependencies": { @@ -10465,8 +9888,6 @@ }, "node_modules/config-file-ts/node_modules/brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "license": "MIT", "dependencies": { @@ -10475,8 +9896,6 @@ }, "node_modules/config-file-ts/node_modules/glob": { "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "license": "ISC", "dependencies": { @@ -10496,8 +9915,6 @@ }, "node_modules/config-file-ts/node_modules/minimatch": { "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", "dependencies": { @@ -10512,8 +9929,6 @@ }, "node_modules/config-file-ts/node_modules/minipass": { "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "license": "ISC", "engines": { @@ -10638,8 +10053,8 @@ }, "node_modules/cordova-plugin-inappbrowser": { "version": "6.0.1-dev", - "resolved": "git+ssh://git@github.com/mytonwalletorg/cordova-plugin-inappbrowser.git#c585b61d05f5bef7bc0920158db7e4febf48a9ca", - "integrity": "sha512-qp+YBZBXRwYRAlxvGLgyZRR3ct3Pa2cDMTMhcyMwk2jTfw8oZM33DKqrQAa11BO/EFtoKsxQNz2ptTmUATnHmg==", + "resolved": "git+ssh://git@github.com/mytonwalletorg/cordova-plugin-inappbrowser.git#c5d3d89bead5e2b52df60ab7dd0217497a22a3a1", + "integrity": "sha512-ji24W9rOBPtOdWds4BDueTUpf929fHBPwqx6+pHKq+7INtKbxVJIq5Qqbl0bw4z0ZMHg3dA+ZK2MlgqKLapIuQ==", "license": "Apache-2.0", "engines": { "cordovaDependencies": { @@ -10700,8 +10115,6 @@ }, "node_modules/crc": { "version": "3.8.0", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", - "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", "dev": true, "license": "MIT", "optional": true, @@ -10711,8 +10124,6 @@ }, "node_modules/crc-32": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", "dev": true, "license": "Apache-2.0", "peer": true, @@ -10725,8 +10136,6 @@ }, "node_modules/crc/node_modules/buffer": { "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, "funding": [ { @@ -10751,8 +10160,6 @@ }, "node_modules/crc32-stream": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", - "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", "dev": true, "license": "MIT", "peer": true, @@ -11249,8 +10656,6 @@ }, "node_modules/debounce-fn": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", - "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11265,8 +10670,6 @@ }, "node_modules/debounce-fn/node_modules/mimic-fn": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", - "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", "dev": true, "license": "MIT", "engines": { @@ -11556,8 +10959,6 @@ }, "node_modules/dir-compare": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-4.2.0.tgz", - "integrity": "sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11578,8 +10979,6 @@ }, "node_modules/dmg-builder": { "version": "25.1.7", - "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-25.1.7.tgz", - "integrity": "sha512-Hac0AfXQrAV62JT99Had6bvUJb/f7vjJTaLOsmA/gAQcrc/cLmNAqCJ0ZZDqwKy2+LKXnxx45TvMXvovKd4iMg==", "dev": true, "license": "MIT", "dependencies": { @@ -11596,8 +10995,6 @@ }, "node_modules/dmg-builder/node_modules/fs-extra": { "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11611,8 +11008,6 @@ }, "node_modules/dmg-builder/node_modules/iconv-lite": { "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, "license": "MIT", "dependencies": { @@ -11624,8 +11019,6 @@ }, "node_modules/dmg-builder/node_modules/jsonfile": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11637,8 +11030,6 @@ }, "node_modules/dmg-builder/node_modules/universalify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "license": "MIT", "engines": { @@ -11647,8 +11038,6 @@ }, "node_modules/dmg-license": { "version": "1.0.11", - "resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz", - "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==", "dev": true, "license": "MIT", "optional": true, @@ -11769,8 +11158,6 @@ }, "node_modules/dot-prop": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", - "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", "dev": true, "license": "MIT", "dependencies": { @@ -11785,8 +11172,6 @@ }, "node_modules/dotenv": { "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -11798,8 +11183,6 @@ }, "node_modules/dotenv-expand": { "version": "11.0.6", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.6.tgz", - "integrity": "sha512-8NHi73otpWsZGBSZwwknTXS5pqMOrk9+Ssrna8xCaxkzEpU9OTf9R5ArQGVw03//Zmk9MOwLPng9WwndvpAJ5g==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -11829,8 +11212,6 @@ }, "node_modules/ejs": { "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -11862,8 +11243,6 @@ }, "node_modules/electron-builder": { "version": "25.1.7", - "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-25.1.7.tgz", - "integrity": "sha512-lsKtX93GSHWnmuteNRvBzgJIjRiiYB0qrJVRjShwBi75Ns+mRdWeOGZiXItqOWj+3g5UyY722kgoq2WlqCB87A==", "dev": true, "license": "MIT", "dependencies": { @@ -11888,8 +11267,6 @@ }, "node_modules/electron-builder-squirrel-windows": { "version": "25.1.7", - "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-25.1.7.tgz", - "integrity": "sha512-nJMvw1FNy+6YP8HmjSb0JwMowpdlZpydZGab9KevKO/fIC9wTcr5rkhbLsTfEPOjdAqOTycRoK0mOJCFB/1uig==", "dev": true, "license": "MIT", "peer": true, @@ -11902,8 +11279,6 @@ }, "node_modules/electron-builder-squirrel-windows/node_modules/fs-extra": { "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, "license": "MIT", "peer": true, @@ -11918,8 +11293,6 @@ }, "node_modules/electron-builder-squirrel-windows/node_modules/jsonfile": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "license": "MIT", "peer": true, @@ -11932,8 +11305,6 @@ }, "node_modules/electron-builder-squirrel-windows/node_modules/universalify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "license": "MIT", "peer": true, @@ -12082,8 +11453,6 @@ }, "node_modules/electron-publish": { "version": "25.1.7", - "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-25.1.7.tgz", - "integrity": "sha512-+jbTkR9m39eDBMP4gfbqglDd6UvBC7RLh5Y0MhFSsc6UkGHj9Vj9TWobxevHYMMqmoujL11ZLjfPpMX+Pt6YEg==", "dev": true, "license": "MIT", "dependencies": { @@ -12098,8 +11467,6 @@ }, "node_modules/electron-publish/node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", "dependencies": { @@ -12114,8 +11481,6 @@ }, "node_modules/electron-publish/node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { @@ -12131,8 +11496,6 @@ }, "node_modules/electron-publish/node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -12144,15 +11507,11 @@ }, "node_modules/electron-publish/node_modules/color-name": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, "license": "MIT" }, "node_modules/electron-publish/node_modules/fs-extra": { "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, "license": "MIT", "dependencies": { @@ -12166,8 +11525,6 @@ }, "node_modules/electron-publish/node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", "engines": { @@ -12176,8 +11533,6 @@ }, "node_modules/electron-publish/node_modules/jsonfile": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "license": "MIT", "dependencies": { @@ -12189,8 +11544,6 @@ }, "node_modules/electron-publish/node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { @@ -12202,8 +11555,6 @@ }, "node_modules/electron-publish/node_modules/universalify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "license": "MIT", "engines": { @@ -12212,8 +11563,6 @@ }, "node_modules/electron-store": { "version": "8.2.0", - "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-8.2.0.tgz", - "integrity": "sha512-ukLL5Bevdil6oieAOXz3CMy+OgaItMiVBg701MNlG6W5RaC0AHN7rvlqTCmeb6O7jP0Qa1KKYTE0xV0xbhF4Hw==", "dev": true, "license": "MIT", "dependencies": { @@ -12226,8 +11575,6 @@ }, "node_modules/electron-store/node_modules/type-fest": { "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -12239,15 +11586,11 @@ }, "node_modules/electron-to-chromium": { "version": "1.5.35", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.35.tgz", - "integrity": "sha512-hOSRInrIDm0Brzp4IHW2F/VM+638qOL2CzE0DgpnGzKW27C95IqqeqgKz/hxHGnvPxvQGpHUGD5qRVC9EZY2+A==", "dev": true, "license": "ISC" }, "node_modules/electron-updater": { "version": "6.3.9", - "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.3.9.tgz", - "integrity": "sha512-2PJNONi+iBidkoC5D1nzT9XqsE8Q1X28Fn6xRQhO3YX8qRRyJ3mkV4F1aQsuRnYPqq6Hw+E51y27W75WgDoofw==", "dev": true, "license": "MIT", "dependencies": { @@ -13411,7 +12754,7 @@ "node_modules/eslint-plugin-teactn": { "version": "0.1.0-development", "resolved": "git+ssh://git@github.com/korenskoy/eslint-plugin-teactn.git#c2c39dd005d58c07c24c4361de804dce1c6261b5", - "integrity": "sha512-qOQqdOvGfCMN3UeERZlOb3HurpzAFSWa61JWfZEeix6dU/AaWH6LdyFeelCJHi0/lvR/86DMgnYDcv/NqVoWcg==", + "integrity": "sha512-8Xedzwbkp50WfJUMhstW5ZOc7KKmWbzEyZMPIqTlF2kHnMrMuic6yZ9Q06hM2/AVtGIw48XK2NOXZIM7qMXHIA==", "dev": true, "license": "ISC", "dependencies": { @@ -13820,8 +13163,6 @@ }, "node_modules/exponential-backoff": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", - "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", "dev": true, "license": "Apache-2.0" }, @@ -13999,8 +13340,6 @@ }, "node_modules/extsprintf": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", - "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", "dev": true, "engines": [ "node >=0.6.0" @@ -14132,8 +13471,6 @@ }, "node_modules/fast-uri": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.2.tgz", - "integrity": "sha512-GR6f0hD7XXyNJa25Tb9BuIdN0tdr+0BMi6/CJPH3wJO1JjNG3n/VsSw38AwRdKZABm8lGbPfakLRkYzx2V9row==", "dev": true, "license": "MIT" }, @@ -14206,8 +13543,6 @@ }, "node_modules/filelist": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -14216,8 +13551,6 @@ }, "node_modules/filelist/node_modules/brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "license": "MIT", "dependencies": { @@ -14226,8 +13559,6 @@ }, "node_modules/filelist/node_modules/minimatch": { "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "license": "ISC", "dependencies": { @@ -14464,8 +13795,6 @@ }, "node_modules/fraction.js": { "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "dev": true, "license": "MIT", "engines": { @@ -14486,8 +13815,6 @@ }, "node_modules/fs-constants": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "dev": true, "license": "MIT", "peer": true @@ -15458,8 +14785,6 @@ }, "node_modules/iconv-corefoundation": { "version": "1.1.7", - "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", - "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", "dev": true, "license": "MIT", "optional": true, @@ -15476,8 +14801,6 @@ }, "node_modules/iconv-corefoundation/node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", "optional": true, @@ -15493,8 +14816,6 @@ }, "node_modules/iconv-corefoundation/node_modules/cli-truncate": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", "dev": true, "license": "MIT", "optional": true, @@ -15511,8 +14832,6 @@ }, "node_modules/iconv-corefoundation/node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "license": "MIT", "optional": true, @@ -15525,24 +14844,18 @@ }, "node_modules/iconv-corefoundation/node_modules/color-name": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, "license": "MIT", "optional": true }, "node_modules/iconv-corefoundation/node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, "license": "MIT", "optional": true }, "node_modules/iconv-corefoundation/node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "license": "MIT", "optional": true, @@ -15552,8 +14865,6 @@ }, "node_modules/iconv-corefoundation/node_modules/slice-ansi": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", "dev": true, "license": "MIT", "optional": true, @@ -15568,8 +14879,6 @@ }, "node_modules/iconv-corefoundation/node_modules/string-width": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", "optional": true, @@ -15888,8 +15197,6 @@ }, "node_modules/is-ci": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", "dev": true, "license": "MIT", "dependencies": { @@ -16119,8 +15426,6 @@ }, "node_modules/is-obj": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", "dev": true, "license": "MIT", "engines": { @@ -16326,8 +15631,6 @@ }, "node_modules/isbinaryfile": { "version": "5.0.2", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.2.tgz", - "integrity": "sha512-GvcjojwonMjWbTkfMpnVHVqXW/wKMYDfEpY94/8zy8HFMOqb/VL6oeONq9v87q4ttVlaTLnGXnJD4B5B1OTGIg==", "dev": true, "license": "MIT", "engines": { @@ -16458,8 +15761,6 @@ }, "node_modules/jake": { "version": "10.9.2", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", - "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -16477,8 +15778,6 @@ }, "node_modules/jake/node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", "dependencies": { @@ -16493,8 +15792,6 @@ }, "node_modules/jake/node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { @@ -16510,8 +15807,6 @@ }, "node_modules/jake/node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -16523,15 +15818,11 @@ }, "node_modules/jake/node_modules/color-name": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, "license": "MIT" }, "node_modules/jake/node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", "engines": { @@ -16540,8 +15831,6 @@ }, "node_modules/jake/node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { @@ -18038,8 +17327,6 @@ }, "node_modules/jsesc": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "dev": true, "license": "MIT", "bin": { @@ -18066,8 +17353,6 @@ }, "node_modules/json-schema-typed": { "version": "7.0.3", - "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz", - "integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==", "dev": true, "license": "BSD-2-Clause" }, @@ -18183,8 +17468,6 @@ }, "node_modules/lazystream": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", - "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", "dev": true, "license": "MIT", "peer": true, @@ -18197,16 +17480,12 @@ }, "node_modules/lazystream/node_modules/isarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true, "license": "MIT", "peer": true }, "node_modules/lazystream/node_modules/readable-stream": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "license": "MIT", "peer": true, @@ -18222,16 +17501,12 @@ }, "node_modules/lazystream/node_modules/safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, "license": "MIT", "peer": true }, "node_modules/lazystream/node_modules/string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "license": "MIT", "peer": true, @@ -18596,8 +17871,6 @@ }, "node_modules/lodash.defaults": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", "dev": true, "license": "MIT", "peer": true @@ -18614,8 +17887,6 @@ }, "node_modules/lodash.flatten": { "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", "dev": true, "license": "MIT", "peer": true @@ -18627,8 +17898,6 @@ }, "node_modules/lodash.isplainobject": { "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", "dev": true, "license": "MIT", "peer": true @@ -18650,8 +17919,6 @@ }, "node_modules/lodash.union": { "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", - "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", "dev": true, "license": "MIT", "peer": true @@ -19304,8 +18571,6 @@ }, "node_modules/mime": { "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "dev": true, "license": "MIT", "bin": { @@ -19540,6 +18805,17 @@ "dev": true, "license": "MIT" }, + "node_modules/mtw-capacitor-usb-hid": { + "version": "0.0.1", + "resolved": "git+ssh://git@github.com/mytonwallet-org/capacitor-usb-hid.git#814f226d655536664d09c02263d898a1982228c2", + "integrity": "sha512-OXMMZggQRLUmVybAVxttY9mqMqTS9iLGwb2Tb+Jwbb3kZN6jQzG6uNXf25Xpkk1WjWCy0YSPxrU9YpSQSBjS+A==", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": "^6.0.0", + "@ledgerhq/devices": "^8.0.0", + "@ledgerhq/hw-transport": "^6.0.0" + } + }, "node_modules/multicast-dns": { "version": "7.2.5", "dev": true, @@ -19664,16 +18940,12 @@ }, "node_modules/node-addon-api": { "version": "1.7.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", - "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", "dev": true, "license": "MIT", "optional": true }, "node_modules/node-api-version": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.0.tgz", - "integrity": "sha512-fthTTsi8CxaBXMaBAD7ST2uylwvsnYxh2PfaScwpMhos6KlSFajXQPcM4ogNE1q2s3Lbz9GCGqeIHC+C6OZnKg==", "dev": true, "license": "MIT", "dependencies": { @@ -20357,8 +19629,6 @@ }, "node_modules/pe-library": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-0.4.1.tgz", - "integrity": "sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==", "dev": true, "license": "MIT", "engines": { @@ -20479,8 +19749,6 @@ }, "node_modules/pkg-up": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", "dev": true, "license": "MIT", "dependencies": { @@ -20492,8 +19760,6 @@ }, "node_modules/pkg-up/node_modules/find-up": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "license": "MIT", "dependencies": { @@ -20505,8 +19771,6 @@ }, "node_modules/pkg-up/node_modules/locate-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "license": "MIT", "dependencies": { @@ -20519,8 +19783,6 @@ }, "node_modules/pkg-up/node_modules/p-limit": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "license": "MIT", "dependencies": { @@ -20535,8 +19797,6 @@ }, "node_modules/pkg-up/node_modules/p-locate": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "license": "MIT", "dependencies": { @@ -20548,8 +19808,6 @@ }, "node_modules/pkg-up/node_modules/path-exists": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", "dev": true, "license": "MIT", "engines": { @@ -20558,8 +19816,6 @@ }, "node_modules/playwright": { "version": "1.48.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.0.tgz", - "integrity": "sha512-qPqFaMEHuY/ug8o0uteYJSRfMGFikhUysk8ZvAtfKmUK3kc/6oNl/y3EczF8OFGYIi/Ex2HspMfzYArk6+XQSA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -20577,8 +19833,6 @@ }, "node_modules/playwright-core": { "version": "1.48.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.0.tgz", - "integrity": "sha512-RBvzjM9rdpP7UUFrQzRwR8L/xR4HyC1QXMzGYTbf1vjw25/ya9NRAVnXi/0fvFopjebvyPzsmoK58xxeEOaVvA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -20590,10 +19844,7 @@ }, "node_modules/playwright/node_modules/fsevents": { "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, - "hasInstallScript": true, "license": "MIT", "optional": true, "os": [ @@ -20634,8 +19885,6 @@ }, "node_modules/postcss": { "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "dev": true, "funding": [ { @@ -21319,8 +20568,6 @@ }, "node_modules/proc-log": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz", - "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==", "dev": true, "license": "ISC", "engines": { @@ -21329,6 +20576,7 @@ }, "node_modules/process": { "version": "0.11.10", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6.0" @@ -21469,7 +20717,7 @@ "node_modules/qr-code-styling": { "version": "1.5.1", "resolved": "git+ssh://git@github.com/mytonwallet-org/qr-code-styling.git#8d56b409da29122a92da2767d049b45b1bc24db2", - "integrity": "sha512-BLmpA4CSQSiztR2w/S9J5DH0LvOTvzHPSnurN4oZ9FqvnJs0g22m1IKXMgS7P/G+sieqSjUSlLMl5Lp6Qd/lyw==", + "integrity": "sha512-qrT0ZF3zZFo5Al3jddpcptqyFJZ2TBJZ6D8ARzx9Y/UVErvRgAKX7Wb6+LBvLufEwZPGlSlC1FfH11D5CO0fPA==", "license": "MIT", "dependencies": { "qrcode-generator": "^1.4.3" @@ -21732,8 +20980,6 @@ }, "node_modules/readdir-glob": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", - "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", "dev": true, "license": "Apache-2.0", "peer": true, @@ -21743,8 +20989,6 @@ }, "node_modules/readdir-glob/node_modules/brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "license": "MIT", "peer": true, @@ -21754,8 +20998,6 @@ }, "node_modules/readdir-glob/node_modules/minimatch": { "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "license": "ISC", "peer": true, @@ -21823,15 +21065,11 @@ }, "node_modules/regenerate": { "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", "dev": true, "license": "MIT" }, "node_modules/regenerate-unicode-properties": { "version": "10.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", - "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", "dev": true, "license": "MIT", "dependencies": { @@ -21847,8 +21085,6 @@ }, "node_modules/regenerator-transform": { "version": "0.15.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", "dev": true, "license": "MIT", "dependencies": { @@ -21879,8 +21115,6 @@ }, "node_modules/regexpu-core": { "version": "6.1.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz", - "integrity": "sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==", "dev": true, "license": "MIT", "dependencies": { @@ -21917,15 +21151,11 @@ }, "node_modules/regjsgen": { "version": "0.8.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", "dev": true, "license": "MIT" }, "node_modules/regjsparser": { "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.1.tgz", - "integrity": "sha512-1DHODs4B8p/mQHU9kr+jv8+wIC9mtG4eBHxWxIq5mhjE3D5oORhCc6deRKzTjs9DcfRFmj9BHSDguZklqCGFWQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -21962,8 +21192,6 @@ }, "node_modules/replace-in-file": { "version": "8.2.0", - "resolved": "https://registry.npmjs.org/replace-in-file/-/replace-in-file-8.2.0.tgz", - "integrity": "sha512-hMsQtdYHwWviQT5ZbNsgfu0WuCiNlcUSnnD+aHAL081kbU9dPkPocDaHlDvAHKydTWWpx1apfcEcmvIyQk3CpQ==", "dev": true, "license": "MIT", "dependencies": { @@ -22061,8 +21289,6 @@ }, "node_modules/resedit": { "version": "1.7.2", - "resolved": "https://registry.npmjs.org/resedit/-/resedit-1.7.2.tgz", - "integrity": "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==", "dev": true, "license": "MIT", "dependencies": { @@ -22230,8 +21456,6 @@ }, "node_modules/rollup": { "version": "4.24.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", - "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", "dev": true, "license": "MIT", "dependencies": { @@ -22370,8 +21594,6 @@ }, "node_modules/sanitize-filename": { "version": "1.6.3", - "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", - "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", "dev": true, "license": "WTFPL OR ISC", "dependencies": { @@ -22380,8 +21602,6 @@ }, "node_modules/sass": { "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.79.4.tgz", - "integrity": "sha512-K0QDSNPXgyqO4GZq2HO5Q70TLxTH6cIT59RdoCHMivrC8rqzaTw5ab9prjz9KUN1El4FLXrBXJhik61JR4HcGg==", "dev": true, "license": "MIT", "dependencies": { @@ -22398,8 +21618,6 @@ }, "node_modules/sass-loader": { "version": "16.0.2", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.2.tgz", - "integrity": "sha512-Ll6iXZ1EYwYT19SqW4mSBb76vSSi8JgzElmzIerhEGgzB5hRjDQIWsPmuk1UrAXkR16KJHqVY0eH+5/uw9Tmfw==", "dev": true, "license": "MIT", "dependencies": { @@ -22439,8 +21657,6 @@ }, "node_modules/sass/node_modules/chokidar": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", - "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", "dev": true, "license": "MIT", "dependencies": { @@ -22455,8 +21671,6 @@ }, "node_modules/sass/node_modules/readdirp": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", - "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", "dev": true, "license": "MIT", "engines": { @@ -23154,8 +22368,6 @@ }, "node_modules/stat-mode": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", - "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", "dev": true, "license": "MIT", "engines": { @@ -24290,8 +23502,6 @@ }, "node_modules/tar-stream": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", "dev": true, "license": "MIT", "peer": true, @@ -24332,8 +23542,6 @@ }, "node_modules/temp-file": { "version": "3.4.0", - "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", - "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", "dev": true, "license": "MIT", "dependencies": { @@ -24343,8 +23551,6 @@ }, "node_modules/temp-file/node_modules/fs-extra": { "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, "license": "MIT", "dependencies": { @@ -24358,8 +23564,6 @@ }, "node_modules/temp-file/node_modules/jsonfile": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "license": "MIT", "dependencies": { @@ -24371,8 +23575,6 @@ }, "node_modules/temp-file/node_modules/universalify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "license": "MIT", "engines": { @@ -24560,8 +23762,6 @@ }, "node_modules/tmp": { "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", "dev": true, "license": "MIT", "engines": { @@ -24570,8 +23770,6 @@ }, "node_modules/tmp-promise": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", - "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -24612,8 +23810,6 @@ }, "node_modules/tonapi-sdk-js": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/tonapi-sdk-js/-/tonapi-sdk-js-2.0.3.tgz", - "integrity": "sha512-fSJC2OtnI9SqDLsYLKZWqY/xhfHpPVPnVx4l72jVwnhXjx5ZEdHcUd9R1+0OVQCCtXOA5EV2ALDnwLaPtAIlcw==", "license": "MIT" }, "node_modules/tonweb-mnemonic": { @@ -24663,8 +23859,6 @@ }, "node_modules/tronweb": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/tronweb/-/tronweb-6.0.0.tgz", - "integrity": "sha512-mIh00KG00Iu80UT1SLDgNEBLzWiR24WnttlObP8B9eQyNJ6mg4oD2gE+vG0cd6FcHL9DV6Jd18gKeBp4y3Y7Ew==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.0.0", @@ -24691,8 +23885,6 @@ }, "node_modules/truncate-utf8-bytes": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", - "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", "dev": true, "license": "WTFPL", "dependencies": { @@ -24935,8 +24127,6 @@ }, "node_modules/typescript": { "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -24980,8 +24170,6 @@ }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", - "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", "dev": true, "license": "MIT", "engines": { @@ -24990,8 +24178,6 @@ }, "node_modules/unicode-match-property-ecmascript": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", "dev": true, "license": "MIT", "dependencies": { @@ -25004,8 +24190,6 @@ }, "node_modules/unicode-match-property-value-ecmascript": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", - "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", "dev": true, "license": "MIT", "engines": { @@ -25014,8 +24198,6 @@ }, "node_modules/unicode-property-aliases-ecmascript": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", "dev": true, "license": "MIT", "engines": { @@ -25169,8 +24351,6 @@ }, "node_modules/utf8-byte-length": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", - "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", "dev": true, "license": "(WTFPL OR MIT)" }, @@ -25242,8 +24422,6 @@ }, "node_modules/verror": { "version": "1.10.1", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", - "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", "dev": true, "license": "MIT", "optional": true, @@ -25258,8 +24436,6 @@ }, "node_modules/verror/node_modules/core-util-is": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "dev": true, "license": "MIT", "optional": true @@ -25336,8 +24512,6 @@ }, "node_modules/webpack": { "version": "5.95.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.95.0.tgz", - "integrity": "sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==", "dev": true, "license": "MIT", "dependencies": { @@ -26152,8 +25326,6 @@ }, "node_modules/zip-stream": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", - "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", "dev": true, "license": "MIT", "peer": true, @@ -26168,8 +25340,6 @@ }, "node_modules/zip-stream/node_modules/archiver-utils": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", - "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", "dev": true, "license": "MIT", "peer": true, diff --git a/package.json b/package.json index 2fb7f7cb..d80e3d40 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mytonwallet", - "version": "3.0.35", + "version": "3.1.0", "description": "The most feature-rich web wallet and browser extension for TON – with support of multi-accounts, tokens (jettons), NFT, TON DNS, TON Sites, TON Proxy, and TON Magic.", "main": "index.js", "scripts": { @@ -107,6 +107,7 @@ "@types/react": "18.3.11", "@types/react-dom": "18.3.0", "@types/sha256": "0.2.2", + "@types/uuid": "10.0.0", "@types/webextension-polyfill": "0.12.1", "@types/webpack": "5.28.5", "@types/webpack-bundle-analyzer": "4.7.0", @@ -160,6 +161,7 @@ "postcss": "8.4.47", "postcss-loader": "8.1.1", "postcss-modules": "6.0.0", + "process": "0.11.10", "replace-in-file": "8.2.0", "sass": "1.79.4", "sass-loader": "16.0.2", @@ -179,6 +181,7 @@ "dependencies": { "@awesome-cordova-plugins/core": "6.9.0", "@awesome-cordova-plugins/in-app-browser": "6.9.0", + "@capacitor-community/bluetooth-le": "github:mytonwallet-org/capacitor-bluetooth-le#0ac4f47716a9fbccbd7184384dcfab69d8fede97", "@capacitor-mlkit/barcode-scanning": "6.2.0", "@capacitor/android": "6.1.2", "@capacitor/app": "6.0.1", @@ -205,14 +208,14 @@ "capacitor-native-settings": "6.0.1", "capacitor-plugin-safe-area": "3.0.3", "capacitor-secure-storage-plugin": "github:mytonwallet-org/capacitor-secure-storage-plugin#03a96f6c0e7207b302b1dcb2d13c0c9e2ae6cc29", - "cordova-plugin-inappbrowser": "github:mytonwalletorg/cordova-plugin-inappbrowser#c585b61d05f5bef7bc0920158db7e4febf48a9ca", + "cordova-plugin-inappbrowser": "github:mytonwalletorg/cordova-plugin-inappbrowser#c5d3d89bead5e2b52df60ab7dd0217497a22a3a1", "create-hmac": "1.1.7", "dexie": "4.0.8", "idb-keyval": "6.2.1", + "mtw-capacitor-usb-hid": "github:mytonwallet-org/capacitor-usb-hid#814f226d655536664d09c02263d898a1982228c2", "native-bottom-sheet": "file:mobile/plugins/native-bottom-sheet", "native-dialog": "file:mobile/plugins/native-dialog", "pako": "2.1.0", - "process": "^0.11.10", "qr-code-styling": "github:mytonwallet-org/qr-code-styling#8d56b409da29122a92da2767d049b45b1bc24db2", "qrcode-generator": "1.4.4", "stream-browserify": "3.0.0", diff --git a/public/version.txt b/public/version.txt index f61e0ad6..fd2a0186 100644 --- a/public/version.txt +++ b/public/version.txt @@ -1 +1 @@ -3.0.35 +3.1.0 diff --git a/src/api/chains/ton/swap.ts b/src/api/chains/ton/swap.ts index 9837e32f..c16ce12b 100644 --- a/src/api/chains/ton/swap.ts +++ b/src/api/chains/ton/swap.ts @@ -2,8 +2,10 @@ import type { SwapHistoryRange } from '../../methods'; import type { ApiActivity, ApiNetwork, - ApiSwapBuildRequest, ApiSwapHistoryItem, - ApiTokensTransferPayload, ApiTransaction, + ApiSwapBuildRequest, + ApiSwapHistoryItem, + ApiTokensTransferPayload, + ApiTransaction, ApiTransactionActivity, } from '../../types'; import type { TonTransferParams } from './types'; diff --git a/src/api/chains/ton/transactions.ts b/src/api/chains/ton/transactions.ts index 02fc9d22..b7192e48 100644 --- a/src/api/chains/ton/transactions.ts +++ b/src/api/chains/ton/transactions.ts @@ -37,8 +37,8 @@ import { parseAccountId } from '../../../util/account'; import { bigintMultiplyToNumber } from '../../../util/bigint'; import { compareActivities } from '../../../util/compareActivities'; import { fromDecimal, toDecimal } from '../../../util/decimals'; -import { buildCollectionByKey, omit } from '../../../util/iteratees'; -import { logDebugError } from '../../../util/logs'; +import { buildCollectionByKey, omit, pick } from '../../../util/iteratees'; +import { logDebug, logDebugError } from '../../../util/logs'; import { updatePoisoningCache } from '../../../util/poisoningHash'; import { pause } from '../../../util/schedulers'; import withCacheAsync from '../../../util/withCacheAsync'; @@ -580,7 +580,7 @@ async function signTransaction({ amount, payload, stateInit, - privateKey = new Uint8Array(64), + privateKey, isFullBalance, expireAt, shouldEncrypt, @@ -598,6 +598,12 @@ async function signTransaction({ }) { const { seqno } = await getWalletInfo(network, wallet); + if (!privateKey) { + privateKey = new Uint8Array(64); + } else { + logDebug('Signing transactions', { seqno, toAddress, amount }); + } + if (!expireAt) { expireAt = Math.round(Date.now() / 1000) + TRANSFER_TIMEOUT_SEC; } @@ -878,7 +884,9 @@ export async function checkMultiTransactionDraft( const { balance } = await getWalletInfo(network, wallet); - const { transaction } = await signMultiTransaction(network, wallet, messages, undefined, version); + const { transaction } = await signMultiTransaction({ + network, wallet, messages, version, + }); const realFee = await calculateFee(network, wallet, transaction, isInitialized); @@ -929,9 +937,9 @@ export async function submitMultiTransfer({ const gaslessType = isGasless ? version === 'W5' ? 'w5' : 'diesel' : undefined; const withW5Gasless = gaslessType === 'w5'; - const { seqno, transaction } = await signMultiTransaction( - network, wallet!, messages, privateKey, version, expireAt, withW5Gasless, - ); + const { seqno, transaction } = await signMultiTransaction({ + network, wallet: wallet!, messages, version, privateKey, expireAt, withW5Gasless, + }); if (!isGasless) { const fee = await calculateFee(network, wallet!, transaction, isInitialized); @@ -947,6 +955,9 @@ export async function submitMultiTransfer({ if (!isGasless) { void retrySendBoc(network, fromAddress, boc, seqno, pendingTransfer); + } else { + // TODO: Wait for gasless transfer + pendingTransfer.resolve(); } const clearedMessages = messages.map((message) => { @@ -972,17 +983,34 @@ export async function submitMultiTransfer({ } } -async function signMultiTransaction( - network: ApiNetwork, - wallet: TonWallet, - messages: TonTransferParams[], - privateKey: Uint8Array = new Uint8Array(64), - version: ApiTonWalletVersion, - expireAt?: number, - withW5Gasless = false, -) { +async function signMultiTransaction({ + network, + wallet, + messages, + version, + privateKey, + expireAt, + withW5Gasless, +}: { + network: ApiNetwork; + wallet: TonWallet; + messages: TonTransferParams[]; + version: ApiTonWalletVersion; + privateKey?: Uint8Array; + expireAt?: number; + withW5Gasless?: boolean; +}) { const { seqno } = await getWalletInfo(network, wallet); + if (!privateKey) { + privateKey = new Uint8Array(64); + } else { + logDebug('Signing transaction', { + seqno, + messages: messages.map((msg) => pick(msg, ['toAddress', 'amount'])), + }); + } + if (!expireAt) { expireAt = Math.round(Date.now() / 1000) + TRANSFER_TIMEOUT_SEC; } @@ -1077,7 +1105,7 @@ async function retrySendBoc( ]); // Errors mean that `seqno` was changed or not enough of balance - if (error?.includes('exitcode=33') || error?.includes('inbound external message rejected by account')) { + if (error?.match(/(exitcode=33|exitcode=133|inbound external message rejected by account)/)) { break; } diff --git a/src/api/common/backend.ts b/src/api/common/backend.ts index 271c8c0e..945bf61c 100644 --- a/src/api/common/backend.ts +++ b/src/api/common/backend.ts @@ -43,5 +43,10 @@ export async function callBackendPost(path: string, data: AnyLiteral, options export function callBackendGet(path: string, data?: AnyLiteral, headers?: HeadersInit): Promise { const url = new URL(`${BRILLIANT_API_BASE_URL}${path}`); - return fetchJson(url, data, { headers: { ...headers, 'X-App-Version': APP_VERSION } }); + return fetchJson(url, data, { + headers: { + ...headers, + 'X-App-Version': APP_VERSION, + }, + }); } diff --git a/src/api/common/swap.ts b/src/api/common/swap.ts index 8f6f9a01..ffeda625 100644 --- a/src/api/common/swap.ts +++ b/src/api/common/swap.ts @@ -45,3 +45,19 @@ export function getSwapItemSlug(item: ApiSwapHistoryItem, asset: string) { } return buildTokenSlug('ton', asset); } + +export async function patchSwapItem(options: { + address: string; + swapId: string; + authToken: string; + msgHash?: string; + error?: string; +}) { + const { + address, swapId, authToken, msgHash, error, + } = options; + await callBackendPost(`/swap/history/${address}/${swapId}/update`, { msgHash, error }, { + method: 'PATCH', + authToken, + }); +} diff --git a/src/api/methods/auth.ts b/src/api/methods/auth.ts index 5ee7ed03..4e515187 100644 --- a/src/api/methods/auth.ts +++ b/src/api/methods/auth.ts @@ -152,7 +152,6 @@ export async function importLedgerWallet(network: ApiNetwork, walletInfo: Ledger deviceId, deviceName, }); - void activateAccount(accountId); return { accountId, address, walletInfo }; } diff --git a/src/api/methods/dapps.ts b/src/api/methods/dapps.ts index 6718d1e9..3722c643 100644 --- a/src/api/methods/dapps.ts +++ b/src/api/methods/dapps.ts @@ -229,3 +229,7 @@ export function setSseLastEventId(lastEventId: string) { export function loadExploreSites(): Promise { return callBackendGet('/dapp/catalog'); } + +export function loadDappOriginReplacements(): Promise> { + return callBackendGet('/dapp/origin-replacements'); +} diff --git a/src/api/methods/index.ts b/src/api/methods/index.ts index ebb009e8..c912947c 100644 --- a/src/api/methods/index.ts +++ b/src/api/methods/index.ts @@ -17,6 +17,7 @@ export { deleteAllDapps, deactivateDapp, loadExploreSites, + loadDappOriginReplacements, } from './dapps'; export { startSseConnection, diff --git a/src/api/methods/swap.ts b/src/api/methods/swap.ts index 8efbbdfd..b7fd092c 100644 --- a/src/api/methods/swap.ts +++ b/src/api/methods/swap.ts @@ -23,7 +23,10 @@ import { buildSwapId } from '../../util/swap/buildSwapId'; import chains from '../chains'; import { fetchStoredTonWallet } from '../common/accounts'; import { callBackendGet, callBackendPost } from '../common/backend'; -import { getSwapItemSlug, swapGetHistoryItem, swapItemToActivity } from '../common/swap'; +import { + getSwapItemSlug, patchSwapItem, swapGetHistoryItem, swapItemToActivity, +} from '../common/swap'; +import { ApiServerError } from '../errors'; import { callHook } from '../hooks'; import { getBackendAuthToken } from './other'; @@ -60,15 +63,25 @@ export async function swapBuildTransfer( isBase64Payload: true, })); - await ton.validateDexSwapTransfers(network, address, request, transferList); + try { + await ton.validateDexSwapTransfers(network, address, request, transferList); - const result = await ton.checkMultiTransactionDraft(accountId, transferList, request.shouldTryDiesel); + const result = await ton.checkMultiTransactionDraft(accountId, transferList, request.shouldTryDiesel); - if ('error' in result) { - return result; - } + if ('error' in result) { + await patchSwapItem({ + address, swapId: id, authToken, error: result.error, + }); + return result; + } - return { ...result, id, transfers }; + return { ...result, id, transfers }; + } catch (err: any) { + await patchSwapItem({ + address, swapId: id, authToken, error: errorToString(err), + }); + throw err; + } } export async function swapSubmit( @@ -78,57 +91,72 @@ export async function swapSubmit( historyItem: ApiSwapHistoryItem, isGasless?: boolean, ) { + const swapId = historyItem.id; + const authToken = await getBackendAuthToken(accountId, password); const { address } = await fetchStoredTonWallet(accountId); - const transferList: TonTransferParams[] = transfers.map((transfer) => ({ - ...transfer, - amount: BigInt(transfer.amount), - isBase64Payload: true, - })); - if (historyItem.from !== TONCOIN.symbol) { - transferList[0] = await ton.insertMintlessPayload('mainnet', address, historyItem.from, transferList[0]); - } - - const result = await ton.submitMultiTransfer({ - accountId, password, messages: transferList, isGasless, - }); + try { + const transferList: TonTransferParams[] = transfers.map((transfer) => ({ + ...transfer, + amount: BigInt(transfer.amount), + isBase64Payload: true, + })); + + if (historyItem.from !== TONCOIN.symbol) { + transferList[0] = await ton.insertMintlessPayload('mainnet', address, historyItem.from, transferList[0]); + } + + const result = await ton.submitMultiTransfer({ + accountId, + password, + messages: transferList, + isGasless, + }); + + if ('error' in result) { + await patchSwapItem({ + address, swapId, authToken, error: result.error, + }); + return result; + } + + delete result.messages[0].stateInit; + + const from = getSwapItemSlug(historyItem, historyItem.from); + const to = getSwapItemSlug(historyItem, historyItem.to); + + const swap: ApiSwapActivity = { + ...historyItem, + id: buildSwapId(historyItem.id), + from, + to, + kind: 'swap', + }; + + await patchSwapItem({ + address, swapId, authToken, msgHash: result.msgHash, + }); + + onUpdate({ + type: 'newActivities', + chain: 'ton', + accountId, + activities: [swap], + }); + + void callHook('onSwapCreated', accountId, swap.timestamp - 1); - if ('error' in result) { return result; + } catch (err: any) { + await patchSwapItem({ + address, swapId, authToken, error: errorToString(err), + }); + throw err; } +} - delete result.messages[0].stateInit; - - const from = getSwapItemSlug(historyItem, historyItem.from); - const to = getSwapItemSlug(historyItem, historyItem.to); - - const swap: ApiSwapActivity = { - ...historyItem, - id: buildSwapId(historyItem.id), - from, - to, - kind: 'swap', - }; - - const authToken = await getBackendAuthToken(accountId, password); - - await callBackendPost(`/swap/history/${address}/${historyItem.id}/update`, { - msgHash: result.msgHash, - }, { - method: 'PATCH', - authToken, - }); - - onUpdate({ - type: 'newActivities', - chain: 'ton', - accountId, - activities: [swap], - }); - - void callHook('onSwapCreated', accountId, swap.timestamp - 1); - - return result; +function errorToString(err: Error | string) { + return typeof err === 'string' ? err : err.stack; } export async function fetchSwaps(accountId: string, ids: string[]) { @@ -137,10 +165,22 @@ export async function fetchSwaps(accountId: string, ids: string[]) { ids.map((id) => swapGetHistoryItem(address, id.replace('swap:', ''))), ); - return results - .map((result) => (result.status === 'fulfilled' ? result.value : undefined)) - .filter(Boolean) - .map(swapItemToActivity); + const nonExistentIds: string[] = []; + + const swaps = results + .map((result, i) => { + if (result.status === 'rejected') { + if (result.reason instanceof ApiServerError && result.reason.statusCode === 404) { + nonExistentIds.push(ids[i]); + } + return undefined; + } + + return swapItemToActivity(result.value); + }) + .filter(Boolean); + + return { nonExistentIds, swaps }; } export function swapEstimate(request: ApiSwapEstimateRequest): Promise { diff --git a/src/api/types/backend.ts b/src/api/types/backend.ts index f2bb3778..b6bf7cde 100644 --- a/src/api/types/backend.ts +++ b/src/api/types/backend.ts @@ -77,6 +77,7 @@ export type ApiSwapHistoryItem = { swapFee: string; status: 'pending' | 'completed' | 'failed' | 'expired'; txIds: string[]; + isCanceled?: boolean; cex?: { payinAddress: string; payoutAddress: string; diff --git a/src/components/App.tsx b/src/components/App.tsx index 98809588..c37c46f2 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -26,6 +26,7 @@ import useSyncEffect from '../hooks/useSyncEffect'; import useTimeout from '../hooks/useTimeout'; import AppInactive from './AppInactive'; +import AppLocked from './AppLocked'; import Auth from './auth/Auth'; import DappConnectModal from './dapps/DappConnectModal'; import DappTransferModal from './dapps/DappTransferModal'; @@ -195,6 +196,7 @@ function App({ )} + {!isInactive && ( <> diff --git a/src/components/AppLocked.module.scss b/src/components/AppLocked.module.scss new file mode 100644 index 00000000..e4c86a0c --- /dev/null +++ b/src/components/AppLocked.module.scss @@ -0,0 +1,66 @@ +.appLocked { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + height: 100vh; + + color: var(--color-black); +} + +.appLockedFixed { + position: fixed; + top: calc(50% - 10rem); // Computed empirically + + gap: 2rem; + + width: 100%; + height: unset; +} + +.unlockButtonHidden :global { + opacity: 0; + + animation: fade-out-opacity 0.3s ease; +} + +.title { + font-size: 1.6875rem; + font-weight: 800; + line-height: 1; + color: var(--color-black); +} + +.passwordFormContent { + display: flex; + flex-direction: column; + gap: 2rem; + align-items: center; + justify-content: center; + + min-width: 20rem; + height: fit-content; +} + +.logo { + width: 10rem; + height: 10rem; +} + +.passwordInputWrapper { + width: 100%; + margin-bottom: 0; +} + +.appLockedWrapper { + position: fixed; + z-index: var(--z-autolock); + top: 0; + right: 0; + bottom: 0; + left: 0; + + background-color: var(--color-tint-lock); + backdrop-filter: blur(var(--blur-app-locked)); +} diff --git a/src/components/AppLocked.tsx b/src/components/AppLocked.tsx new file mode 100644 index 00000000..cc1f03df --- /dev/null +++ b/src/components/AppLocked.tsx @@ -0,0 +1,246 @@ +import { BottomSheet } from 'native-bottom-sheet'; +import React, { + memo, useEffect, useMemo, useRef, useState, +} from '../lib/teact/teact'; +import { getActions, withGlobal } from '../global'; + +import type { AutolockValueType, Theme } from '../global/types'; + +import { AUTOLOCK_OPTIONS_LIST, DEBUG, IS_CAPACITOR } from '../config'; +import { selectIsHardwareAccount } from '../global/selectors'; +import buildClassName from '../util/buildClassName'; +import { vibrateOnSuccess } from '../util/capacitor'; +import { createSignal } from '../util/signals'; +import { IS_DELEGATED_BOTTOM_SHEET, IS_DELEGATING_BOTTOM_SHEET } from '../util/windowEnvironment'; +import { callApi } from '../api'; + +import useAppTheme from '../hooks/useAppTheme'; +import useBackgroundMode, { isBackgroundModeActive } from '../hooks/useBackgroundMode'; +import useEffectOnce from '../hooks/useEffectOnce'; +import useFlag from '../hooks/useFlag'; +import useLang from '../hooks/useLang'; +import useLastCallback from '../hooks/useLastCallback'; +import useShowTransition from '../hooks/useShowTransition'; +import useThrottledCallback from '../hooks/useThrottledCallback'; + +import Button from './ui/Button'; +import Image from './ui/Image'; +import { getInAppBrowser } from './ui/InAppBrowser'; +import PasswordForm from './ui/PasswordForm'; +import Transition from './ui/Transition'; + +import styles from './AppLocked.module.scss'; + +import logoDarkPath from '../assets/logoDark.svg'; +import logoLightPath from '../assets/logoLight.svg'; + +const WINDOW_EVENTS_LATENCY = 5000; +const INTERVAL_CHECK_PERIOD = 5000; +const PINPAD_RESET_DELAY = 300; + +interface StateProps { + isNonNativeBiometricAuthEnabled: boolean; + autolockValue?: AutolockValueType; + theme: Theme; + isHardwareAccount?: boolean; +} + +const enum SLIDES { + button, + passwordForm, +} + +const [getActivitySignal, setActivitySignal] = createSignal(Date.now()); + +export function reportAppLockActivityEvent() { + setActivitySignal(Date.now()); +} + +function AppLocked({ + isNonNativeBiometricAuthEnabled, autolockValue = 'never', theme, isHardwareAccount, +}: StateProps): TeactJsx { + const { setIsPinAccepted, clearIsPinAccepted, submitAppLockActivityEvent } = getActions(); + const lang = useLang(); + + const appTheme = useAppTheme(theme); + const logoPath = appTheme === 'light' ? logoLightPath : logoDarkPath; + + const [isLocked, lock, unlock] = useFlag(autolockValue !== 'never' && !isHardwareAccount); + const [shouldRenderUi, showUi, hideUi] = useFlag(isLocked); + const lastActivityTime = useRef(Date.now()); + const [slideForBiometricAuth, setSlideForBiometricAuth] = useState( + isBackgroundModeActive() ? SLIDES.button : SLIDES.passwordForm, + ); + const [passwordError, setPasswordError] = useState(''); + + const afterUnlockCallback = useLastCallback(() => { + hideUi(); + setSlideForBiometricAuth(SLIDES.button); + getInAppBrowser()?.show(); + clearIsPinAccepted(); + if (IS_DELEGATING_BOTTOM_SHEET) void BottomSheet.show(); + }); + + const autolockPeriod = useMemo( + () => AUTOLOCK_OPTIONS_LIST.find((option) => option.value === autolockValue)!.period, [autolockValue], + ); + + const { transitionClassNames } = useShowTransition(isLocked, afterUnlockCallback, true, 'slow'); + + const handleLock = useLastCallback(() => { + if (autolockValue !== 'never' && !isHardwareAccount) { + lock(); + showUi(); + if (IS_DELEGATING_BOTTOM_SHEET) void BottomSheet.hide(); + getInAppBrowser()?.hide(); + } + setSlideForBiometricAuth(SLIDES.button); + }); + + if (DEBUG) (window as any).lock = handleLock; + + const handleChangeSlideForBiometricAuth = useLastCallback(() => { + setSlideForBiometricAuth(SLIDES.passwordForm); + }); + + const handleSubmitPassword = useLastCallback(async (password: string) => { + const result = await callApi('verifyPassword', password); + + if (!result) { + setPasswordError('Wrong password, please try again.'); + return; + } + + if (IS_CAPACITOR) { + setIsPinAccepted(); + await vibrateOnSuccess(true); + } + unlock(); + }); + + const handlePasswordChange = useLastCallback(() => setPasswordError('')); + + const handleActivity = useLastCallback(() => { + if (IS_DELEGATED_BOTTOM_SHEET) { + submitAppLockActivityEvent(); + return; + } + lastActivityTime.current = Date.now(); + }); + + const handleActivityThrottled = useThrottledCallback(handleActivity, [handleActivity], WINDOW_EVENTS_LATENCY); + + useEffectOnce(() => { + window.addEventListener('focus', handleActivityThrottled, { capture: true }); // For Web + window.addEventListener('mousemove', handleActivityThrottled, { capture: true }); // For Web + window.addEventListener('touch', handleActivityThrottled, { capture: true }); // For Capacitor + window.addEventListener('wheel', handleActivityThrottled, { capture: true }); + window.addEventListener('keydown', handleActivityThrottled); + + return () => { + window.removeEventListener('focus', handleActivityThrottled, { capture: true }); + window.removeEventListener('mousemove', handleActivityThrottled, { capture: true }); + window.removeEventListener('touch', handleActivityThrottled, { capture: true }); + window.removeEventListener('wheel', handleActivityThrottled, { capture: true }); + window.removeEventListener('keydown', handleActivityThrottled); + }; + }); + + useEffectOnce(() => { + if (IS_DELEGATED_BOTTOM_SHEET) return undefined; + + return getActivitySignal.subscribe(handleActivityThrottled); + }); + + useEffect(() => { + if (IS_DELEGATED_BOTTOM_SHEET) return undefined; + + const interval = setInterval(() => { + if (!isLocked && Date.now() - lastActivityTime.current > autolockPeriod) { + handleLock(); + } + }, INTERVAL_CHECK_PERIOD); + return () => clearInterval(interval); + }, [isLocked, autolockPeriod, handleLock]); + + useBackgroundMode(undefined, handleChangeSlideForBiometricAuth); + + function renderLogo() { + return ( +
+ Logo +
+ ); + } + + function renderTransitionContent(isActive: boolean) { + return ( +
+ { + isNonNativeBiometricAuthEnabled && slideForBiometricAuth === SLIDES.button ? ( + <> + {renderLogo()} + {lang('MyTonWallet')} + + + ) : ( + + {renderLogo()} + {lang('MyTonWallet')} + + ) + } +
+ ); + } + + const transitionKey = Number(slideForBiometricAuth === SLIDES.passwordForm) + Number(shouldRenderUi) * 2; + + if (IS_DELEGATED_BOTTOM_SHEET) return undefined; + + return ( + + {shouldRenderUi ? renderTransitionContent : undefined} + + ); +} + +export default memo(withGlobal((global): StateProps => { + const { authConfig, autolockValue } = global.settings; + + const isHardwareAccount = selectIsHardwareAccount(global); + + const isBiometricAuthEnabled = !!authConfig && authConfig.kind !== 'password'; + const isNativeBiometricAuthEnabled = !!authConfig && authConfig.kind === 'native-biometrics'; + const isNonNativeBiometricAuthEnabled = isBiometricAuthEnabled && !isNativeBiometricAuthEnabled; + + return { + isNonNativeBiometricAuthEnabled, autolockValue, theme: global.settings.theme, isHardwareAccount, + }; +})(AppLocked)); diff --git a/src/components/dapps/DappFeed.tsx b/src/components/dapps/DappFeed.tsx index 2da3ba83..9c933249 100644 --- a/src/components/dapps/DappFeed.tsx +++ b/src/components/dapps/DappFeed.tsx @@ -8,6 +8,7 @@ import { selectCurrentAccountState } from '../../global/selectors'; import buildClassName from '../../util/buildClassName'; import { MEMO_EMPTY_ARRAY } from '../../util/memo'; +import useEffectOnce from '../../hooks/useEffectOnce'; import useHorizontalScroll from '../../hooks/useHorizontalScroll'; import useLang from '../../hooks/useLang'; @@ -18,6 +19,7 @@ import styles from './Dapp.module.scss'; interface StateProps { dapps: ApiDapp[]; dappLastOpenedDatesByOrigin?: Record; + dappOriginReplacements?: Record; } type DappWithLastOpenedDate = ApiDapp & { lastOpenedAt?: number }; @@ -37,8 +39,12 @@ const MAX_DAPPS_FOR_MINI_MODE = 3; const HIDDEN_FROM_FEED_DAPP_ORIGINS = new Set(['https://checkin.mytonwallet.org']); -function DappFeed({ dapps: dappsFromState, dappLastOpenedDatesByOrigin = {} }: StateProps) { - const { openSettingsWithState } = getActions(); +function DappFeed({ + dapps: dappsFromState, dappLastOpenedDatesByOrigin = {}, dappOriginReplacements = {}, +}: StateProps) { + const { openSettingsWithState, loadDappOriginReplacements } = getActions(); + + useEffectOnce(loadDappOriginReplacements); const dapps: DappWithLastOpenedDate[] = useMemo(() => { return dappsFromState.slice().filter((dapp) => !HIDDEN_FROM_FEED_DAPP_ORIGINS.has(dapp.origin)).map( @@ -76,9 +82,10 @@ function DappFeed({ dapps: dappsFromState, dappLastOpenedDatesByOrigin = {} }: S key={origin} iconUrl={iconUrl} name={name} - url={url} + url={dappOriginReplacements[url] ?? url} mode={mode} origin={origin} + shouldOpenInAppBrowser={dappOriginReplacements[url] !== undefined} /> ); } @@ -111,5 +118,6 @@ function DappFeed({ dapps: dappsFromState, dappLastOpenedDatesByOrigin = {} }: S export default memo(withGlobal((global): StateProps => { const { dapps = MEMO_EMPTY_ARRAY } = selectCurrentAccountState(global) || {}; const { dappLastOpenedDatesByOrigin } = selectCurrentAccountState(global) || {}; - return { dapps, dappLastOpenedDatesByOrigin }; + + return { dapps, dappLastOpenedDatesByOrigin, dappOriginReplacements: global.dappOriginReplacements }; })(DappFeed)); diff --git a/src/components/dapps/DappFeedItem.tsx b/src/components/dapps/DappFeedItem.tsx index 5e7e2ce8..e281a354 100644 --- a/src/components/dapps/DappFeedItem.tsx +++ b/src/components/dapps/DappFeedItem.tsx @@ -17,21 +17,18 @@ interface OwnProps { url: string; mode: 'mini' | 'tile'; origin: string; + shouldOpenInAppBrowser: boolean; } const RERENDER_DAPPS_FEED_DELAY_MS = 1000; -const POPULAR_TELEGRAM_DAPPS_BY_URL: Record = { - 'https://onetime.dog': 'https://t.me/dogshouse_bot/join', // https://cdn.onetime.dog/manifest.json - 'https://hamsterkombatgame.io/': 'https://t.me/hamster_kombat_bot/start', // https://hamsterkombatgame.io/tonconnect-manifest.json -}; - function DappFeedItem({ iconUrl, name, url, mode, origin, + shouldOpenInAppBrowser, }: OwnProps) { const { updateDappLastOpenedAt } = getActions(); const lang = useLang(); @@ -59,12 +56,7 @@ function DappFeedItem({ } const openDapp = useLastCallback(async () => { - const matchedUrl = POPULAR_TELEGRAM_DAPPS_BY_URL[url]; - if (matchedUrl) { - await openUrl(matchedUrl, true); - } else { - await openUrl(url); - } + await openUrl(url, shouldOpenInAppBrowser); setTimeout(() => void updateDappLastOpenedAt({ origin }), RERENDER_DAPPS_FEED_DELAY_MS); }); diff --git a/src/components/ledger/LedgerConfirmOperation.tsx b/src/components/ledger/LedgerConfirmOperation.tsx index 365cd02b..c2b188b9 100644 --- a/src/components/ledger/LedgerConfirmOperation.tsx +++ b/src/components/ledger/LedgerConfirmOperation.tsx @@ -71,7 +71,9 @@ function LedgerConfirmOperation({ {text}
- +
diff --git a/src/components/ledger/LedgerConnect.tsx b/src/components/ledger/LedgerConnect.tsx index 9b4a98ff..8f1d6e35 100644 --- a/src/components/ledger/LedgerConnect.tsx +++ b/src/components/ledger/LedgerConnect.tsx @@ -1,19 +1,26 @@ import React, { - memo, useEffect, + memo, useEffect, useMemo, useState, } from '../../lib/teact/teact'; -import { getActions } from '../../global'; +import { getActions, withGlobal } from '../../global'; +import type { Account } from '../../global/types'; +import type { LedgerTransport } from '../../util/ledger/types'; import { HardwareConnectState } from '../../global/types'; +import { IS_CAPACITOR } from '../../config'; +import { selectAccounts } from '../../global/selectors'; import buildClassName from '../../util/buildClassName'; import { closeLedgerTab } from '../../util/ledger/tab'; import resolveModalTransitionName from '../../util/resolveModalTransitionName'; +import { IS_DELEGATING_BOTTOM_SHEET } from '../../util/windowEnvironment'; import useHistoryBack from '../../hooks/useHistoryBack'; import useLang from '../../hooks/useLang'; import useLastCallback from '../../hooks/useLastCallback'; +import useShowTransition from '../../hooks/useShowTransition'; import Button from '../ui/Button'; +import Dropdown from '../ui/Dropdown'; import ModalHeader from '../ui/ModalHeader'; import Transition from '../ui/Transition'; @@ -31,7 +38,17 @@ interface OwnProps { onClose: NoneToVoidFunction; } +interface StateProps { + availableTransports?: LedgerTransport[]; + lastUsedTransport?: LedgerTransport; + accounts?: Record; +} + const NEXT_SLIDE_DELAY = 500; +const TRANSPORT_NAMES: Record = { + usb: 'USB', + bluetooth: 'Bluetooth', +}; function LedgerConnect({ isActive, @@ -39,32 +56,65 @@ function LedgerConnect({ isLedgerConnected, isTonAppConnected, isRemoteTab, + availableTransports, + lastUsedTransport, + accounts, onConnected, onCancel, onClose, -}: OwnProps) { +}: OwnProps & StateProps) { const { connectHardwareWallet, resetHardwareWalletConnect, + initializeHardwareWalletModal, initializeHardwareWalletConnection, } = getActions(); + const lang = useLang(); + const [selectedTransport, setSelectedTransport] = useState(lastUsedTransport); const isLedgerFailed = isLedgerConnected === false; const isTonAppFailed = isTonAppConnected === false; const isConnected = state === HardwareConnectState.ConnectedWithSingleWallet || state === HardwareConnectState.ConnectedWithSeveralWallets; + const isConnecting = state === HardwareConnectState.Connecting || ( + state === HardwareConnectState.Connect && !isLedgerFailed && !isTonAppFailed && availableTransports?.length === 1 + ); const isWaitingForBrowser = state === HardwareConnectState.WaitingForBrowser; const title = isConnected ? lang('Ledger Connected!') : lang('Connect Ledger'); const shouldCloseOnCancel = !onCancel; + const hasAccounts = useMemo(() => Object.keys(accounts || {}).length > 0, [accounts]); + + const renderingAvailableTransports = useMemo(() => { + return (availableTransports || []).map((transport) => ({ + value: transport, + name: TRANSPORT_NAMES[transport], + })); + }, [availableTransports]); + const { + shouldRender: shouldRenderAvailableTransports, + transitionClassNames: availableTransportsClassNames, + } = useShowTransition(Boolean(renderingAvailableTransports.length > 0 && selectedTransport)); useHistoryBack({ isActive, onBack: onCancel ?? onClose, }); + useEffect(() => { + if (!selectedTransport && availableTransports?.length) { + setSelectedTransport(availableTransports[0]); + } + }, [availableTransports, selectedTransport]); + + useEffect(() => { + if (isRemoteTab || !isActive || (IS_DELEGATING_BOTTOM_SHEET && hasAccounts)) return; + + initializeHardwareWalletModal(); + }, [hasAccounts, isActive, isRemoteTab]); + const handleConnected = useLastCallback((isSingleWallet: boolean) => { - if (isRemoteTab) { + if (isRemoteTab || (IS_DELEGATING_BOTTOM_SHEET && hasAccounts)) { return; } @@ -77,12 +127,6 @@ function LedgerConnect({ }, NEXT_SLIDE_DELAY); }); - useEffect(() => { - if (isRemoteTab || !isActive) return; - - initializeHardwareWalletConnection(); - }, [isActive, isRemoteTab]); - useEffect(() => { if ( state === HardwareConnectState.ConnectedWithSingleWallet @@ -98,8 +142,32 @@ function LedgerConnect({ closeAction(); }); + const handleSubmit = useLastCallback(() => { + if (renderingAvailableTransports.length > 1) { + initializeHardwareWalletConnection({ transport: selectedTransport! }); + } else { + connectHardwareWallet({ transport: availableTransports?.[0] }); + } + }); + + function renderAvailableTransports() { + return ( +
+ void} + /> +
+ ); + } + function renderButtons() { - const isConnecting = state === HardwareConnectState.Connecting; const isFailed = state === HardwareConnectState.Failed; if (isRemoteTab && isConnected) { @@ -128,9 +196,10 @@ function LedgerConnect({ {!isConnected && ( @@ -207,7 +276,7 @@ function LedgerConnect({ className={buildClassName(styles.textIcon, isLedgerConnected ? 'icon-accept' : 'icon-dot')} aria-hidden /> - {lang('Connect your Ledger to PC')} + {IS_CAPACITOR ? lang('Connect your Ledger') : lang('Connect your Ledger to PC')} + + {shouldRenderAvailableTransports && renderAvailableTransports()} {renderButtons()} @@ -250,4 +321,13 @@ function LedgerConnect({ ); } -export default memo(LedgerConnect); +export default memo(withGlobal((global): StateProps => { + const { availableTransports, lastUsedTransport } = global.hardware; + const accounts = selectAccounts(global); + + return { + availableTransports, + lastUsedTransport, + accounts, + }; +})(LedgerConnect)); diff --git a/src/components/ledger/LedgerModal.module.scss b/src/components/ledger/LedgerModal.module.scss index 8d1108e4..c6356c1b 100644 --- a/src/components/ledger/LedgerModal.module.scss +++ b/src/components/ledger/LedgerModal.module.scss @@ -12,6 +12,10 @@ } } +:global(.is-native-bottom-sheet) .modalDialogInsideSettings { + height: 100% !important; +} + .slide { padding-top: 0 !important; } @@ -22,6 +26,10 @@ height: 100%; padding: 0 1rem 1rem; + + :global(html:not(.is-android-app)) & { + padding-bottom: max(var(--safe-area-bottom, 1rem), 1rem); + } } .content { @@ -135,6 +143,7 @@ margin-top: auto; &_single { + gap: 0; justify-content: center; } } @@ -340,3 +349,19 @@ .declinedLabelDetailed { text-align: left; } + +.dropdownBlock { + cursor: var(--custom-cursor, pointer); + + display: grid; + grid-template-areas: "content action"; + grid-template-columns: 1fr min-content; + gap: 0.75rem; + align-items: center; + + margin-top: 1.25rem; + padding: 1rem; + + background: var(--color-background-first); + border-radius: var(--border-radius-default); +} diff --git a/src/components/ledger/LedgerModal.tsx b/src/components/ledger/LedgerModal.tsx index 573f9812..3714ba3f 100644 --- a/src/components/ledger/LedgerModal.tsx +++ b/src/components/ledger/LedgerModal.tsx @@ -32,6 +32,7 @@ type StateProps = { isLedgerConnected?: boolean; isTonAppConnected?: boolean; isRemoteTab?: boolean; + areSettingsOpen?: boolean; }; enum LedgerModalState { @@ -49,6 +50,7 @@ function LedgerModal({ isLedgerConnected, isTonAppConnected, isRemoteTab, + areSettingsOpen, }: OwnProps & StateProps) { const { afterSelectHardwareWallets, @@ -113,7 +115,7 @@ function LedgerModal({ isOpen={isOpen} onClose={onClose} onCloseAnimationEnd={handleLedgerModalClose} - dialogClassName={styles.modalDialog} + dialogClassName={buildClassName(styles.modalDialog, areSettingsOpen && styles.modalDialogInsideSettings)} > ((global): StateProps => { isLedgerConnected, isTonAppConnected, isRemoteTab, + areSettingsOpen: global.areSettingsOpen, }; })(LedgerModal)); diff --git a/src/components/main/sections/Content/Content.module.scss b/src/components/main/sections/Content/Content.module.scss index ea7f9308..578700d8 100644 --- a/src/components/main/sections/Content/Content.module.scss +++ b/src/components/main/sections/Content/Content.module.scss @@ -168,6 +168,11 @@ flex-grow: 1; margin: 0 -0.75rem; + margin-inline-end: calc(-0.75rem + var(--scrollbar-width)); background: var(--color-background-first); + + @supports (margin-inline-end: max(0px, 1px)) { + margin-inline-end: max(-0.75rem, calc(-0.75rem + var(--scrollbar-width))); + } } diff --git a/src/components/main/sections/Content/Swap.tsx b/src/components/main/sections/Content/Swap.tsx index c010aa5c..eba531cf 100644 --- a/src/components/main/sections/Content/Swap.tsx +++ b/src/components/main/sections/Content/Swap.tsx @@ -106,38 +106,35 @@ function Swap({ ); return ( -
-
- - {formatCurrencyExtended( - Math.abs(fromAmount), - fromToken?.symbol || TONCOIN.symbol, - true, - )} - - - + + {formatCurrencyExtended( + Math.abs(fromAmount), + fromToken?.symbol || TONCOIN.symbol, + true, + )} + + - {formatCurrencyExtended( - Math.abs(toAmount), - toToken?.symbol || TONCOIN.symbol, - true, - )} - -
- {renderCurrency(activity, fromToken, toToken)} + aria-hidden + /> + + {formatCurrencyExtended( + Math.abs(toAmount), + toToken?.symbol || TONCOIN.symbol, + true, + )} +
); } @@ -164,11 +161,7 @@ function Swap({ } return ( -
+
{date}{state ? `${WHOLE_PART_DELIMITER}∙${WHOLE_PART_DELIMITER}${state}` : ''}
); @@ -196,7 +189,7 @@ function Swap({ onClick={handleClick} isSimple > - + {isPending && ( )} -
+
{renderTitle()}
+ {renderAmount()} +
+
{renderErrorMessage()} + {renderCurrency(activity, fromToken, toToken)}
- {renderAmount()} ); @@ -234,16 +229,12 @@ function renderCurrency(activity: ApiSwapActivity, fromToken?: ApiSwapAsset, toT const rate = getSwapRate(activity.fromAmount, activity.toAmount, fromToken, toToken); return ( -
+
{ rate && ( <> {rate.firstCurrencySymbol}{' ≈ '} - + {rate.price}{' '}{rate.secondCurrencySymbol} diff --git a/src/components/main/sections/Content/Transaction.module.scss b/src/components/main/sections/Content/Transaction.module.scss index f3d5f0e2..0f329c53 100644 --- a/src/components/main/sections/Content/Transaction.module.scss +++ b/src/components/main/sections/Content/Transaction.module.scss @@ -6,9 +6,9 @@ position: relative; display: grid; - grid-template-areas: "icon left right" "useless bottom bottom"; - grid-template-columns: 2.875rem auto 1fr; - grid-template-rows: 2.25rem auto; + grid-template-areas: "icon header" "icon subheader" "useless nft" "useless comment"; + grid-template-columns: 2.875rem 1fr; + grid-template-rows: 1.3125rem 0.9375rem auto auto; align-items: center; justify-content: center; @@ -18,15 +18,6 @@ color: var(--color-black); text-align: left; - &.withNft { - grid-template-areas: "icon left right" "useless center center"; - } - - &.withNftAndComment { - grid-template-areas: "icon left right" "useless center center" "useless bottom bottom"; - grid-template-rows: 2.25rem auto auto; - } - &::after { content: ''; @@ -168,39 +159,48 @@ color: var(--color-activity-purple-text); } -.leftBlock { - grid-area: left; +.header, +.subheader { + display: flex; + gap: 0.25rem; + align-items: baseline; + justify-content: space-between; + + min-width: 0; - margin-right: 1rem; + white-space: nowrap; } -.amount, -.operationName { +.header { + grid-area: header; + padding-bottom: 0.125rem; font-size: 0.9375rem; font-weight: 600; } +.subheader { + grid-area: subheader; + + font-size: 0.75rem; + font-weight: 500; + color: var(--color-gray-2); +} + +.operationName, .address { overflow: hidden; text-overflow: ellipsis; } -.address, -.swapPrice, -.date { - height: 0.9375rem; - - font-size: 0.75rem; - font-weight: 500; - color: var(--color-gray-2); +.amount, +.address { + text-align: right; } -.apyValue, -.addressValue, -.swapPriceValue { +.addressValue { font-weight: 650; } @@ -208,16 +208,6 @@ vertical-align: -0.125rem; } -.amountWrapper { - grid-area: right; - - min-width: 0; - max-width: 100%; - margin-left: auto; - - text-align: right; -} - .amount { &_operationPositive { color: var(--color-green); @@ -241,7 +231,7 @@ .comment { overflow: hidden; - grid-area: bottom; + grid-area: comment; max-width: 100%; margin-top: 0.4375rem; @@ -319,7 +309,7 @@ .nft { overflow: hidden; display: flex; - grid-area: center; + grid-area: nft; max-width: 100%; height: 3.5rem; @@ -356,7 +346,7 @@ } .nftData { - max-width: 100%; + min-width: 0; padding: 0.75rem 0.75rem 0.75rem 0.5rem; } diff --git a/src/components/main/sections/Content/Transaction.tsx b/src/components/main/sections/Content/Transaction.tsx index 2121323d..d3c46ab1 100644 --- a/src/components/main/sections/Content/Transaction.tsx +++ b/src/components/main/sections/Content/Transaction.tsx @@ -174,33 +174,36 @@ function Transaction({ ); return ( -
-
- {isNftTransfer ? 'NFT' : formatCurrencyExtended( - toDecimal(isStaking ? bigintAbs(amount) : amount, token!.decimals), - token?.symbol || TONCOIN.symbol, - isStaking, - )} -
-
- {!isStaking && lang(isIncoming ? '$transaction_from' : '$transaction_to', { - address: ( - - {withChainIcon && Boolean(chain) && ( - - )} - {addressName || shortenAddress(address, TRANSACTION_ADDRESS_SHIFT)} - - ), - })} - {isStake && lang('at %apy_value%', { - apy_value: APY {apyValue}%, - })} - {(isUnstake || isUnstakeRequest) && '\u00A0'} -
+
+ {isNftTransfer ? 'NFT' : formatCurrencyExtended( + toDecimal(isStaking ? bigintAbs(amount) : amount, token!.decimals), + token?.symbol || TONCOIN.symbol, + isStaking, + )} +
+ ); + } + + function renderAddress() { + return ( +
+ {!isStaking && lang(isIncoming ? '$transaction_from' : '$transaction_to', { + address: ( + + {withChainIcon && Boolean(chain) && ( + + )} + {addressName || shortenAddress(address, TRANSACTION_ADDRESS_SHIFT)} + + ), + })} + {isStake && lang('at %apy_value%', { + apy_value: APY {apyValue}%, + })} + {(isUnstake || isUnstakeRequest) && '\u00A0'}
); } @@ -211,17 +214,11 @@ function Transaction({ )}
- {isPortrait && ( + {isPortrait && !noAnimatedIcon && ( )} - {!isSmallHeight &&
{lang(title)}
} + {!isSmallHeight && title &&
{lang(title)}
} {children}
- + {!noAnimatedIcon && ( + + )} {children} {isBiometricAuthEnabled ? renderBiometricPrompt() : renderPasswordForm()} -
+
{onCancel && ( diff --git a/src/components/ui/PinPad.tsx b/src/components/ui/PinPad.tsx index 77cee930..072f4285 100644 --- a/src/components/ui/PinPad.tsx +++ b/src/components/ui/PinPad.tsx @@ -22,6 +22,7 @@ interface OwnProps { type?: 'error' | 'success'; value: string; length?: number; + resetStateDelayMs?: number; className?: string; isMinified?: boolean; onBiometricsClick?: NoneToVoidFunction; @@ -42,6 +43,7 @@ function PinPad({ title, type, value, + resetStateDelayMs = RESET_STATE_DELAY_MS, length = DEFAULT_PIN_LENGTH, onBiometricsClick, isPinAccepted, @@ -96,13 +98,13 @@ function PinPad({ onChange(''); } onClearError?.(); - }, RESET_STATE_DELAY_MS); + }, resetStateDelayMs); void vibrateOnError(); return () => { window.clearTimeout(timeoutId); }; - }, [length, onChange, onClearError, type, value.length]); + }, [length, onChange, onClearError, resetStateDelayMs, type, value.length]); const handleClick = useLastCallback((char: string) => { if (value.length === length || value.length === 0) { @@ -195,4 +197,9 @@ export default memo(withGlobal( isPinAccepted, }; }, + (global, _, stickToFirst) => { + const { isPinAccepted } = global; + + return stickToFirst(isPinAccepted); + }, )(PinPad)); diff --git a/src/components/ui/Transition.tsx b/src/components/ui/Transition.tsx index a57ada84..aeab2971 100644 --- a/src/components/ui/Transition.tsx +++ b/src/components/ui/Transition.tsx @@ -45,6 +45,7 @@ export type TransitionProps = { withSwipeControl?: boolean; onStart?: NoneToVoidFunction; onStop?: NoneToVoidFunction; + onContainerClick?: NoneToVoidFunction; children: React.ReactNode | ChildrenFn; }; @@ -79,6 +80,7 @@ function Transition({ withSwipeControl, onStart, onStop, + onContainerClick, children, }: TransitionProps) { const currentKeyRef = useRef(); @@ -363,6 +365,7 @@ function Transition({ return (
- {renderPage()} + {renderPage}
); diff --git a/src/giveaways/index.tsx b/src/giveaways/index.tsx index ae285236..107475e2 100644 --- a/src/giveaways/index.tsx +++ b/src/giveaways/index.tsx @@ -1,4 +1,4 @@ -import '../global/actions/ui/misc'; +import '../global/actions/ui/shared'; import '../util/handleError'; import '../util/bigintPatch'; diff --git a/src/global/actions/api/auth.ts b/src/global/actions/api/auth.ts index bdab7963..98cc419a 100644 --- a/src/global/actions/api/auth.ts +++ b/src/global/actions/api/auth.ts @@ -17,10 +17,17 @@ import { copyTextToClipboard } from '../../../util/clipboard'; import isMnemonicPrivateKey from '../../../util/isMnemonicPrivateKey'; import { cloneDeep, compact, omitUndefined } from '../../../util/iteratees'; import { getTranslation } from '../../../util/langProvider'; +import { isLedgerConnectionBroken } from '../../../util/ledger/utils'; +import { logDebugError } from '../../../util/logs'; import { callActionInMain } from '../../../util/multitab'; import { clearPoisoningCache } from '../../../util/poisoningHash'; import { pause } from '../../../util/schedulers'; -import { IS_BIOMETRIC_AUTH_SUPPORTED, IS_DELEGATED_BOTTOM_SHEET, IS_ELECTRON } from '../../../util/windowEnvironment'; +import { + IS_BIOMETRIC_AUTH_SUPPORTED, + IS_DELEGATED_BOTTOM_SHEET, + IS_DELEGATING_BOTTOM_SHEET, + IS_ELECTRON, +} from '../../../util/windowEnvironment'; import { callApi } from '../../../api'; import { addActionHandler, getGlobal, setGlobal } from '../..'; import { INITIAL_STATE } from '../../initialState'; @@ -360,7 +367,9 @@ addActionHandler('createAccount', async (global, actions, { }); addActionHandler('createHardwareAccounts', async (global, actions) => { - const isFirstAccount = !global.currentAccountId; + const accounts = selectAccounts(global) ?? {}; + const isFirstAccount = !Object.values(accounts).length; + setGlobal(updateAuth(global, { isLoading: true })); const { hardwareSelectedIndices = [] } = getGlobal().hardware; @@ -373,6 +382,21 @@ addActionHandler('createHardwareAccounts', async (global, actions) => { ), ); + if (IS_DELEGATED_BOTTOM_SHEET && !isFirstAccount) { + callActionInMain('addHardwareAccounts', { wallets }); + return; + } + + actions.addHardwareAccounts({ wallets }); +}); + +addActionHandler('addHardwareAccounts', (global, actions, { wallets }) => { + const isFirstAccount = !global.currentAccountId; + const nextActiveAccountId = wallets[0]?.accountId; + if (nextActiveAccountId) { + void callApi('activateAccount', nextActiveAccountId); + } + const updatedGlobal = wallets.reduce((currentGlobal, wallet) => { if (!wallet) { return currentGlobal; @@ -394,12 +418,14 @@ addActionHandler('createHardwareAccounts', async (global, actions) => { return currentGlobal; }, getGlobal()); - global = updateAuth(updatedGlobal, { isLoading: false }); + if (nextActiveAccountId) { + global = updateCurrentAccountId(updatedGlobal, nextActiveAccountId); + } + global = updateAuth(global, { isLoading: false }); global = { ...global, shouldForceAccountEdit: true, }; - setGlobal(global); if (getGlobal().areSettingsOpen) { @@ -605,7 +631,12 @@ addActionHandler('switchAccount', async (global, actions, payload) => { } }); -addActionHandler('connectHardwareWallet', async (global, actions) => { +addActionHandler('connectHardwareWallet', async (global, actions, params) => { + const accounts = selectAccounts(global) ?? {}; + const isFirstAccount = !Object.values(accounts).length; + + if (IS_DELEGATING_BOTTOM_SHEET && !isFirstAccount) return; + global = updateHardware(global, { hardwareState: HardwareConnectState.Connecting, hardwareWallets: undefined, @@ -617,7 +648,7 @@ addActionHandler('connectHardwareWallet', async (global, actions) => { const ledgerApi = await import('../../../util/ledger'); - const isLedgerConnected = await ledgerApi.connectLedger(); + const isLedgerConnected = await ledgerApi.connectLedger(params.transport); global = getGlobal(); if (!isLedgerConnected) { @@ -634,24 +665,24 @@ addActionHandler('connectHardwareWallet', async (global, actions) => { }); setGlobal(global); - const isTonAppConnected = await ledgerApi.waitLedgerTonApp(); - global = getGlobal(); + try { + const isTonAppConnected = await ledgerApi.waitLedgerTonApp(); + global = getGlobal(); + + if (!isTonAppConnected) { + global = updateHardware(global, { + isTonAppConnected: false, + hardwareState: HardwareConnectState.Failed, + }); + setGlobal(global); + return; + } - if (!isTonAppConnected) { global = updateHardware(global, { - isTonAppConnected: false, - hardwareState: HardwareConnectState.Failed, + isTonAppConnected: true, }); setGlobal(global); - return; - } - - global = updateHardware(global, { - isTonAppConnected: true, - }); - setGlobal(global); - try { global = getGlobal(); const { isRemoteTab } = global.hardware; const network = selectCurrentNetwork(global); @@ -678,14 +709,26 @@ addActionHandler('connectHardwareWallet', async (global, actions) => { global = updateHardware(global, { hardwareWallets, hardwareState: nextHardwareState, + lastUsedTransport: params.transport, }); setGlobal(global); - } catch (err) { + } catch (err: any) { + const isLedgerDisconnected = isLedgerConnectionBroken(err.name); + + if (isLedgerDisconnected && !params.noRetry) { + actions.connectHardwareWallet({ ...params, noRetry: true }); + return; + } + global = getGlobal(); global = updateHardware(global, { + isLedgerConnected: !isLedgerDisconnected, + isTonAppConnected: isLedgerDisconnected ? undefined : global.hardware.isTonAppConnected, hardwareState: HardwareConnectState.Failed, }); setGlobal(global); + + logDebugError('connectHardwareWallet', err); } }); diff --git a/src/global/actions/api/dapps.ts b/src/global/actions/api/dapps.ts index 15e7ab02..66f68db2 100644 --- a/src/global/actions/api/dapps.ts +++ b/src/global/actions/api/dapps.ts @@ -77,9 +77,7 @@ addActionHandler('submitDappConnectRequestConfirm', async (global, actions, { pa addActionHandler( 'submitDappConnectRequestConfirmHardware', async (global, actions, { accountId: connectAccountId }) => { - const { - promiseId, proof, - } = global.dappConnectRequest!; + const { proof } = global.dappConnectRequest!; global = getGlobal(); global = updateDappConnectRequest(global, { @@ -92,27 +90,35 @@ addActionHandler( try { const signature = await ledgerApi.signLedgerProof(connectAccountId!, proof!); - actions.switchAccount({ accountId: connectAccountId }); - await callApi('confirmDappRequestConnect', promiseId!, { - accountId: connectAccountId, - signature, - }); + if (IS_DELEGATED_BOTTOM_SHEET) { + callActionInMain('submitDappConnectHardware', { accountId: connectAccountId, signature }); + + return; + } + + actions.submitDappConnectHardware({ accountId: connectAccountId, signature }); } catch (err) { setGlobal(updateDappConnectRequest(getGlobal(), { error: 'Canceled by the user', })); - return; } - - global = getGlobal(); - global = clearDappConnectRequest(global); - setGlobal(global); - - await pause(GET_DAPPS_PAUSE); - actions.getDapps(); }, ); +addActionHandler('submitDappConnectHardware', async (global, actions, { accountId, signature }) => { + const { promiseId } = global.dappConnectRequest!; + + actions.switchAccount({ accountId }); + await callApi('confirmDappRequestConnect', promiseId!, { accountId, signature }); + + global = getGlobal(); + global = clearDappConnectRequest(global); + setGlobal(global); + + await pause(GET_DAPPS_PAUSE); + actions.getDapps(); +}); + addActionHandler('cancelDappConnectRequestConfirm', (global) => { const { promiseId } = global.dappConnectRequest || {}; if (!promiseId) { @@ -227,7 +233,14 @@ addActionHandler('submitDappTransferHardware', async (global, actions) => { isTonConnect: true, vestingAddress, }); - void callApi('confirmDappRequest', promiseId, signedMessages); + + if (IS_DELEGATED_BOTTOM_SHEET) { + callActionInMain('submitDappTransferHardware2', { signedMessages }); + + return; + } + + actions.submitDappTransferHardware2({ signedMessages }); } catch (err) { if (err instanceof ApiHardwareBlindSigningNotEnabled) { setGlobal(updateCurrentDappTransfer(getGlobal(), { @@ -249,8 +262,22 @@ addActionHandler('submitDappTransferHardware', async (global, actions) => { } else { void callApi('cancelDappRequest', promiseId, 'Unknown error.'); } + + global = getGlobal(); + global = clearCurrentDappTransfer(global); + setGlobal(global); + } +}); + +addActionHandler('submitDappTransferHardware2', (global, actions, { signedMessages }) => { + const { promiseId } = global.currentDappTransfer; + + if (!promiseId) { + return; } + void callApi('confirmDappRequest', promiseId, signedMessages); + global = getGlobal(); global = clearCurrentDappTransfer(global); setGlobal(global); @@ -444,3 +471,17 @@ addActionHandler('loadExploreSites', async (global) => { }; setGlobal(global); }); + +addActionHandler('loadDappOriginReplacements', async (global) => { + const replacements = await callApi('loadDappOriginReplacements') || {}; + global = getGlobal(); + if (areDeepEqual(replacements, global.dappOriginReplacements)) { + return; + } + + global = { + ...global, + dappOriginReplacements: replacements, + }; + setGlobal(global); +}); diff --git a/src/global/actions/api/swap.ts b/src/global/actions/api/swap.ts index 080f3d55..78edd527 100644 --- a/src/global/actions/api/swap.ts +++ b/src/global/actions/api/swap.ts @@ -2,6 +2,7 @@ import type { ApiSubmitTransferWithDieselResult } from '../../../api/chains/ton/ import type { ApiSubmitTransferOptions, ApiSubmitTransferResult } from '../../../api/methods/types'; import type { ApiChain, + ApiSwapActivity, ApiSwapBuildRequest, ApiSwapCexCreateTransactionRequest, ApiSwapHistoryItem, @@ -1001,19 +1002,38 @@ addActionHandler('updatePendingSwaps', async (global) => { .map(({ id }) => id); if (!ids.length) return; - const swaps = await callApi('fetchSwaps', accountId, ids); - if (!swaps?.length) return; + const result = await callApi('fetchSwaps', accountId, ids); + if (!result?.swaps.length) return; + + const { swaps, nonExistentIds } = result; global = getGlobal(); if (global.currentAccountId !== accountId) return; ({ activities } = selectAccountState(global, accountId) ?? {}); + for (const swap of result.swaps) { + if (swap.isCanceled) { + swap.shouldHide = true; + } + } + + const nonExistentSwaps: Record = {}; + + for (const id of nonExistentIds) { + nonExistentSwaps[id] = { + ...activities!.byId[id] as ApiSwapActivity, + status: 'expired', + shouldHide: true, + }; + } + global = updateAccountState(global, accountId, { activities: { ...activities, byId: { - ...activities?.byId, + ...activities!.byId, + ...nonExistentSwaps, ...buildCollectionByKey(swaps, 'id'), }, }, diff --git a/src/global/actions/index.ts b/src/global/actions/index.ts index 64fc4085..f21db7e1 100644 --- a/src/global/actions/index.ts +++ b/src/global/actions/index.ts @@ -18,3 +18,4 @@ import './ui/swap'; import './ui/nfts'; import './ui/vesting'; import './ui/tokens'; +import './ui/shared'; diff --git a/src/global/actions/ui/misc.ts b/src/global/actions/ui/misc.ts index 06dad446..e20af7e0 100644 --- a/src/global/actions/ui/misc.ts +++ b/src/global/actions/ui/misc.ts @@ -1,5 +1,6 @@ import { BarcodeScanner } from '@capacitor-mlkit/barcode-scanning'; +import type { LedgerTransport } from '../../../util/ledger/types'; import type { GlobalState } from '../../types'; import { AppState, AuthState, HardwareConnectState, SettingsState, SwapState, TransferState, @@ -23,13 +24,14 @@ import { isValidAddressOrDomain } from '../../../util/isValidAddressOrDomain'; import { omitUndefined, pick } from '../../../util/iteratees'; import { getTranslation } from '../../../util/langProvider'; import { onLedgerTabClose, openLedgerTab } from '../../../util/ledger/tab'; -import { callActionInMain } from '../../../util/multitab'; +import { callActionInMain, callActionInNative } from '../../../util/multitab'; import { openUrl } from '../../../util/openUrl'; import { pause } from '../../../util/schedulers'; import { IS_ANDROID_APP, IS_BIOMETRIC_AUTH_SUPPORTED, IS_DELEGATED_BOTTOM_SHEET, + IS_DELEGATING_BOTTOM_SHEET, } from '../../../util/windowEnvironment'; import { callApi } from '../../../api'; import { addActionHandler, getGlobal, setGlobal } from '../../index'; @@ -49,12 +51,15 @@ import { updateSettings, } from '../../reducers'; import { + selectAccounts, selectCurrentAccount, selectCurrentAccountSettings, selectCurrentAccountState, selectFirstNonHardwareAccount, } from '../../selectors'; +import { reportAppLockActivityEvent } from '../../../components/AppLocked'; + const OPEN_LEDGER_TAB_DELAY = 500; const APP_VERSION_URL = IS_ANDROID_APP ? `${IS_PRODUCTION ? PRODUCTION_URL : BETA_URL}/version.txt` : 'version.txt'; @@ -210,26 +215,6 @@ addActionHandler('closeAddAccountModal', (global) => { return { ...global, isAddAccountModalOpen: undefined }; }); -addActionHandler('setTheme', (global, actions, { theme }) => { - return { - ...global, - settings: { - ...global.settings, - theme, - }, - }; -}); - -addActionHandler('setAnimationLevel', (global, actions, { level }) => { - return { - ...global, - settings: { - ...global.settings, - animationLevel: level, - }, - }; -}); - addActionHandler('changeNetwork', (global, actions, { network }) => { return { ...global, @@ -281,51 +266,93 @@ addActionHandler('closeBackupWalletModal', (global) => { return { ...global, isBackupWalletModalOpen: undefined }; }); -addActionHandler('initializeHardwareWalletConnection', async (global, actions) => { - const startConnection = () => { +addActionHandler('initializeHardwareWalletModal', async (global, actions) => { + const ledgerApi = await import('../../../util/ledger'); + const { isBluetoothAvailable, isUsbAvailable } = await ledgerApi.detectAvailableTransports(); + const availableTransports: LedgerTransport[] = []; + if (isUsbAvailable) { + availableTransports.push('usb'); + } + if (isBluetoothAvailable) { + availableTransports.push('bluetooth'); + } + + if (availableTransports.length === 0) { + actions.showNotification({ + message: 'Ledger is not supported on this device.', + }); + } else if (availableTransports.length === 1) { + actions.initializeHardwareWalletConnection({ transport: availableTransports[0] }); + } else { global = updateHardware(getGlobal(), { - hardwareState: HardwareConnectState.Connecting, + availableTransports, }); setGlobal(global); + } +}); - actions.connectHardwareWallet(); +addActionHandler('initializeHardwareWalletConnection', async (global, actions, params) => { + const accountsAmount = Object.keys(selectAccounts(global) || {}).length; + + if (IS_DELEGATING_BOTTOM_SHEET && accountsAmount > 0) { + callActionInNative('initializeHardwareWalletConnection', params); + return; + } + + const setHardwareStateToConnecting = () => { + global = getGlobal(); + global = updateHardware(getGlobal(), { + hardwareState: HardwareConnectState.Connecting, + }); + setGlobal(global); }; + setHardwareStateToConnecting(); const ledgerApi = await import('../../../util/ledger'); - if (await ledgerApi.connectLedger()) { - startConnection(); + if (await ledgerApi.connectLedger(params.transport)) { + actions.connectHardwareWallet({ transport: params.transport }); return; } - if (IS_EXTENSION) { - global = updateHardware(getGlobal(), { - hardwareState: HardwareConnectState.WaitingForBrowser, + if (!IS_EXTENSION) { + global = getGlobal(); + global = updateHardware(global, { + isLedgerConnected: false, + hardwareState: HardwareConnectState.Failed, }); setGlobal(global); + return; + } + + global = updateHardware(getGlobal(), { + hardwareState: HardwareConnectState.WaitingForBrowser, + }); + setGlobal(global); - await pause(OPEN_LEDGER_TAB_DELAY); - const id = await openLedgerTab(); - const popup = await chrome.windows.getCurrent(); + await pause(OPEN_LEDGER_TAB_DELAY); + const id = await openLedgerTab(); + const popup = await chrome.windows.getCurrent(); - onLedgerTabClose(id, async () => { - await chrome.windows.update(popup.id!, { focused: true }); + onLedgerTabClose(id, async () => { + await chrome.windows.update(popup.id!, { focused: true }); - if (!await ledgerApi.connectLedger()) { - actions.closeHardwareWalletModal(); - return; - } + if (!await ledgerApi.connectLedger(params.transport)) { + actions.closeHardwareWalletModal(); + return; + } - startConnection(); - }); - } + setHardwareStateToConnecting(); + actions.connectHardwareWallet({ transport: params.transport }); + }); }); addActionHandler('openHardwareWalletModal', async (global) => { const ledgerApi = await import('../../../util/ledger'); let newHardwareState; - const isConnected = await ledgerApi.connectLedger(); + // If not running in the Capacitor environment, try to instantly connect to the Ledger + const isConnected = !IS_CAPACITOR ? await ledgerApi.connectLedger() : false; if (!isConnected && IS_EXTENSION) { newHardwareState = HardwareConnectState.WaitingForBrowser; @@ -671,3 +698,11 @@ addActionHandler('authorizeDiesel', (global) => { setGlobal(updateCurrentAccountState(global, { isDieselAuthorizationStarted: true })); openUrl(`https://t.me/${BOT_USERNAME}?start=auth-${address}`, true); }); + +addActionHandler('submitAppLockActivityEvent', () => { + if (IS_DELEGATED_BOTTOM_SHEET) { + callActionInMain('submitAppLockActivityEvent'); + return; + } + reportAppLockActivityEvent(); +}); diff --git a/src/global/actions/ui/settings.ts b/src/global/actions/ui/settings.ts index cef61aa0..5584941d 100644 --- a/src/global/actions/ui/settings.ts +++ b/src/global/actions/ui/settings.ts @@ -4,6 +4,7 @@ import type { GlobalState } from '../../types'; import { setLanguage } from '../../../util/langProvider'; import switchTheme from '../../../util/switchTheme'; +import { addActionHandler } from '../..'; let prevGlobal: GlobalState | undefined; @@ -26,3 +27,13 @@ addCallback((global: GlobalState) => { prevGlobal = global; }); + +addActionHandler('setAutolockValue', (global, actions, { value }) => { + return { + ...global, + settings: { + ...global.settings, + autolockValue: value, + }, + }; +}); diff --git a/src/global/actions/ui/shared.ts b/src/global/actions/ui/shared.ts new file mode 100644 index 00000000..a8cd7520 --- /dev/null +++ b/src/global/actions/ui/shared.ts @@ -0,0 +1,21 @@ +import { addActionHandler } from '../../index'; + +addActionHandler('setTheme', (global, actions, { theme }) => { + return { + ...global, + settings: { + ...global.settings, + theme, + }, + }; +}); + +addActionHandler('setAnimationLevel', (global, actions, { level }) => { + return { + ...global, + settings: { + ...global.settings, + animationLevel: level, + }, + }; +}); diff --git a/src/global/cache.ts b/src/global/cache.ts index fa0b4876..3bb5de75 100644 --- a/src/global/cache.ts +++ b/src/global/cache.ts @@ -488,6 +488,7 @@ function updateCache(force?: boolean) { 'currentAccountId', 'stateVersion', 'restrictions', + 'dappOriginReplacements', ]), accounts: { byId: global.accounts?.byId || {}, diff --git a/src/global/types.ts b/src/global/types.ts index 5b203719..f5e913f6 100644 --- a/src/global/types.ts +++ b/src/global/types.ts @@ -15,6 +15,7 @@ import type { ApiNft, ApiParsedPayload, ApiPriceHistoryPeriod, + ApiSignedTransfer, ApiSite, ApiStakingHistory, ApiStakingType, @@ -30,8 +31,9 @@ import type { ApiVestingInfo, ApiWalletInfo, } from '../api/types'; +import type { AUTOLOCK_OPTIONS_LIST } from '../config'; import type { AuthConfig } from '../util/authApi/types'; -import type { LedgerWalletInfo } from '../util/ledger/types'; +import type { LedgerTransport, LedgerWalletInfo } from '../util/ledger/types'; export type IAnchorPosition = { x: number; @@ -425,6 +427,8 @@ export type GlobalState = { isRemoteTab?: boolean; isLedgerConnected?: boolean; isTonAppConnected?: boolean; + availableTransports?: LedgerTransport[]; + lastUsedTransport?: LedgerTransport; }; currentTransfer: { @@ -595,6 +599,7 @@ export type GlobalState = { }; authConfig?: AuthConfig; baseCurrency?: ApiBaseCurrency; + autolockValue?: AutolockValueType; }; dialogs: DialogType[]; @@ -637,6 +642,8 @@ export type GlobalState = { countryCode?: ApiCountryCode; }; + dappOriginReplacements?: Record; + mediaViewer: { mediaId?: string; mediaType?: MediaType; @@ -682,9 +689,13 @@ export interface ActionPayloads { closeAbout: undefined; openAuthBackupWalletModal: undefined; closeAuthBackupWalletModal: { isBackupCreated?: boolean } | undefined; - initializeHardwareWalletConnection: undefined; - connectHardwareWallet: undefined; + initializeHardwareWalletModal: undefined; + initializeHardwareWalletConnection: { transport: LedgerTransport }; + connectHardwareWallet: { transport?: LedgerTransport; noRetry?: boolean }; createHardwareAccounts: undefined; + addHardwareAccounts: { + wallets: ({ accountId: string; address: string; walletInfo: LedgerWalletInfo } | undefined)[]; + }; loadMoreHardwareWallets: { lastIndex: number }; createAccount: { password: string; isImporting: boolean; isPasswordNumeric?: boolean; version?: ApiTonWalletVersion }; afterSelectHardwareWallets: { hardwareSelectedIndices: number[] }; @@ -867,10 +878,12 @@ export interface ActionPayloads { changeBaseCurrency: { currency: ApiBaseCurrency }; clearNativeBiometricsError: undefined; copyStorageData: undefined; + setAutolockValue: { value: AutolockValueType }; // TON Connect submitDappConnectRequestConfirm: { accountId: string; password?: string }; submitDappConnectRequestConfirmHardware: { accountId: string }; + submitDappConnectHardware: { accountId: string; signature: string }; clearDappConnectRequestError: undefined; cancelDappConnectRequestConfirm: undefined; setDappConnectRequestState: { state: DappConnectState }; @@ -880,6 +893,7 @@ export interface ActionPayloads { submitDappTransferConfirm: undefined; submitDappTransferPassword: { password: string }; submitDappTransferHardware: undefined; + submitDappTransferHardware2: { signedMessages: ApiSignedTransfer[] }; cancelDappTransfer: undefined; closeDappTransfer: undefined; @@ -887,6 +901,7 @@ export interface ActionPayloads { deleteAllDapps: undefined; deleteDapp: { origin: string }; loadExploreSites: undefined; + loadDappOriginReplacements: undefined; updateDappLastOpenedAt: { origin: string }; addSiteToBrowserHistory: { url: string }; @@ -963,9 +978,13 @@ export interface ActionPayloads { submitClaimingVestingHardware: undefined; clearVestingError: undefined; cancelClaimingVesting: undefined; + + submitAppLockActivityEvent: undefined; } export enum LoadMoreDirection { Forwards, Backwards, } + +export type AutolockValueType = (typeof AUTOLOCK_OPTIONS_LIST[number])['value']; diff --git a/src/hooks/useBackgroundMode.ts b/src/hooks/useBackgroundMode.ts index e17569b0..a37884ae 100644 --- a/src/hooks/useBackgroundMode.ts +++ b/src/hooks/useBackgroundMode.ts @@ -8,19 +8,20 @@ const focusCallbacks = createCallbackManager(); let isFocused = document.hasFocus(); -window.addEventListener('blur', () => { - if (!isFocused) { - return; - } +function handleBlur() { + if (!isFocused) return; isFocused = false; blurCallbacks.runCallbacks(); -}); +} -window.addEventListener('focus', () => { +function handleFocus() { isFocused = true; focusCallbacks.runCallbacks(); -}); +} + +window.addEventListener('blur', handleBlur); +window.addEventListener('focus', handleFocus); export default function useBackgroundMode( onBlur?: AnyToVoidFunction, diff --git a/src/i18n/de.yaml b/src/i18n/de.yaml index e9955561..7395730b 100644 --- a/src/i18n/de.yaml +++ b/src/i18n/de.yaml @@ -156,7 +156,6 @@ $nft_explore_offer: | Erkunden Sie einen Marktplatz, um bestehende NFT-Sammlungen zu entdecken. Open Getgems: Getgems öffnen -Transaction is not completed: Die Transaktion ist nicht abgeschlossen. Received: Empfangen Sent: Gesendet $transaction_from: von %address% @@ -506,7 +505,7 @@ Are you sure you want to disable Face ID?: Sind Sie sicher, dass Sie Face ID dea Are you sure you want to disable Touch ID?: Sind Sie sicher, dass Sie Touch ID deaktivieren möchten? Are you sure you want to disable biometrics?: Sind Sie sicher, dass Sie die Biometrie deaktivieren möchten? Yes: Ja -Declined. Please try to confirm transaction using biometrics again.: Abgelehnt. Versuchen Sie bitte, die Transaktion erneut mit Biometrie zu bestätigen. +Biometric confirmation failed: Biometrische Bestätigung fehlgeschlagen No Activity: Keine aktivität Add / Buy: Aufladen Buy with Card: Mit Karte kaufen @@ -516,7 +515,6 @@ Explore: Erkunden No partners yet: Noch keine Partner Clear: Löschen Get TON: TON erhalten -Declined. Please try to confirm operation using biometrics again.: Abgelehnt. Versuchen Sie bitte, die Vorgang erneut mit Biometrie zu bestätigen. $unstake_up_to_information: Sofort – bis zu %value% $all_balance: "Gesamt: %balance%" $unstaking_not_enough_balance: Sie müssen %value% auf Ihrem Hauptkonto haben, um mit dem Entsperrprozess fortzufahren. @@ -673,10 +671,13 @@ Enter Secret Words: Geheimwörter eingeben Turn this off so you can manually download updates and verify signatures.: Schalten Sie dies aus, damit Sie Updates manuell herunterladen und Signaturen überprüfen können. InvalidMnemonic: Ihre mnemonischen Wörter sind ungültig NFT Menu: NFT-Menü -Wallets will not be locked.: Wallets werden nicht gesperrt. -Wallets will be automatically locked after 30 seconds of inactivity.: Wallets werden nach 30 Sekunden Inaktivität automatisch gesperrt. -Wallets will be automatically locked after 3 minutes of inactivity.: Wallets werden nach 3 Minuten Inaktivität automatisch gesperrt. -Wallets will be automatically locked after 30 minutes of inactivity.: Wallets werden nach 30 Minuten Inaktivität automatisch gesperrt. +Connection Type: Verbindungstyp +Ledger is not supported on this device: Ledger wird auf diesem Gerät nicht unterstützt +Connect your Ledger: Verbinden Sie Ihr Ledger +App will not be locked.: App wird nicht gesperrt. +App will be automatically locked after 30 seconds of inactivity.: Die App wird nach 30 Sekunden Inaktivität automatisch gesperrt. +App will be automatically locked after 3 minutes of inactivity.: Die App wird nach 3 Minuten Inaktivität automatisch gesperrt. +App will be automatically locked after 30 minutes of inactivity.: Die App wird nach 30 Minuten Inaktivität automatisch gesperrt. Auto-Lock: Automatische Sperre Never: Niemals 30 seconds: 30 Sekunden diff --git a/src/i18n/en.yaml b/src/i18n/en.yaml index 053fc2c2..fa6b07dd 100644 --- a/src/i18n/en.yaml +++ b/src/i18n/en.yaml @@ -156,7 +156,6 @@ $nft_explore_offer: | Explore a marketplace to discover existing NFT collections. Open Getgems: Open Getgems -Transaction is not completed: Transaction is not completed Received: Received Sent: Sent $transaction_from: from %address% @@ -505,7 +504,7 @@ Are you sure you want to disable Face ID?: Are you sure you want to disable Face Are you sure you want to disable Touch ID?: Are you sure you want to disable Touch ID? Are you sure you want to disable biometrics?: Are you sure you want to disable biometrics? Yes: Yes -Declined. Please try to confirm transaction using biometrics again.: Declined. Please try to confirm transaction using biometrics again. +Biometric confirmation failed: Biometric confirmation failed No Activity: No Activity Add / Buy: Add / Buy Buy with Card: Buy with Card @@ -515,7 +514,6 @@ Explore: Explore No partners yet: No partners yet Clear: Clear Get TON: Get TON -Declined. Please try to confirm operation using biometrics again.: Declined. Please try to confirm operation using biometrics again. $unstake_up_to_information: Instantly – up to %value% $all_balance: "All: %balance%" $unstaking_not_enough_balance: You need to have %value% on your main balance to proceed with unstaking. @@ -673,10 +671,13 @@ Enter Secret Words: Enter Secret Words Turn this off so you can manually download updates and verify signatures.: Turn this off so you can manually download updates and verify signatures. InvalidMnemonic: Your mnemonic words are invalid NFT Menu: NFT Menu -Wallets will not be locked.: Wallets will not be locked. -Wallets will be automatically locked after 30 seconds of inactivity.: Wallets will be automatically locked after 30 seconds of inactivity. -Wallets will be automatically locked after 3 minutes of inactivity.: Wallets will be automatically locked after 3 minutes of inactivity. -Wallets will be automatically locked after 30 minutes of inactivity.: Wallets will be automatically locked after 30 minutes of inactivity. +Connection Type: Connection Type +Ledger is not supported on this device: Ledger is not supported on this device +Connect your Ledger: Connect your Ledger +App will not be locked.: App will not be locked. +App will be automatically locked after 30 seconds of inactivity.: App will be automatically locked after 30 seconds of inactivity. +App will be automatically locked after 3 minutes of inactivity.: App will be automatically locked after 3 minutes of inactivity. +App will be automatically locked after 30 minutes of inactivity.: App will be automatically locked after 30 minutes of inactivity. Auto-Lock: Auto-Lock Never: Never 30 seconds: 30 seconds diff --git a/src/i18n/es.yaml b/src/i18n/es.yaml index 039fb678..d56e7632 100644 --- a/src/i18n/es.yaml +++ b/src/i18n/es.yaml @@ -155,7 +155,6 @@ $nft_explore_offer: | Explore los mercados para descubrir nuevas colecciones NFT. Open Getgems: Abrir Getgems -Transaction is not completed: La transacción no se ha completado Received: Recibido Sent: Enviado $transaction_from: de %address% @@ -504,7 +503,7 @@ Are you sure you want to disable Face ID?: ¿Estás seguro de que quieres deshab Are you sure you want to disable Touch ID?: ¿Estás seguro de que quieres deshabilitar Touch ID? Are you sure you want to disable biometrics?: ¿Estás seguro de que quieres deshabilitar la biometría? Yes: Sí -Declined. Please try to confirm transaction using biometrics again.: Rechazado. Intente confirmar la transacción nuevamente utilizando datos biométricos. +Biometric confirmation failed: La confirmación biométrica ha fallado No Activity: Sin Actividad Add / Buy: Recargar Buy with Card: Comprar con Tarjeta @@ -514,7 +513,6 @@ Explore: Explorar No partners yet: No hay socios todavía Clear: Borrar Get TON: Obtener TON -Declined. Please try to confirm operation using biometrics again.: Rechazado. Intente confirmar la operación nuevamente utilizando datos biométricos. $unstake_up_to_information: Al instante – hasta %value% $all_balance: "Todo: %balance%" $unstaking_not_enough_balance: Necesitas tener %value% en tu saldo principal para proceder con el desbloqueo. @@ -671,10 +669,13 @@ Enter Secret Words: Ingresar Palabras Secretas Turn this off so you can manually download updates and verify signatures.: Desactiva esto para poder descargar manualmente las actualizaciones y verificar las firmas. InvalidMnemonic: Tus palabras mnemónicas son inválidas NFT Menu: Menú NFT -Wallets will not be locked.: Las billeteras no se bloquearán. -Wallets will be automatically locked after 30 seconds of inactivity.: Las billeteras se bloquearán automáticamente después de 30 segundos de inactividad. -Wallets will be automatically locked after 3 minutes of inactivity.: Las billeteras se bloquearán automáticamente después de 3 minutos de inactividad. -Wallets will be automatically locked after 30 minutes of inactivity.: Las billeteras se bloquearán automáticamente después de 30 minutos de inactividad. +Connection Type: Tipo de conexión +Ledger is not supported on this device: Ledger no es compatible con este dispositivo +Connect your Ledger: Conecta tu Ledger +App will not be locked.: La aplicación no se bloqueará. +App will be automatically locked after 30 seconds of inactivity.: La aplicación se bloqueará automáticamente después de 30 segundos de inactividad. +App will be automatically locked after 3 minutes of inactivity.: La aplicación se bloqueará automáticamente después de 3 minutos de inactividad. +App will be automatically locked after 30 minutes of inactivity.: La aplicación se bloqueará automáticamente después de 30 minutos de inactividad. Auto-Lock: Bloqueo automático Never: Nunca 30 seconds: 30 segundos diff --git a/src/i18n/pl.yaml b/src/i18n/pl.yaml index f2f659b2..6e6194ff 100644 --- a/src/i18n/pl.yaml +++ b/src/i18n/pl.yaml @@ -158,7 +158,6 @@ $nft_explore_offer: | Przeglądaj rynek, aby odkryć istniejące kolekcje NFT. Open Getgems: Otwórz Getgems -Transaction is not completed: Transakcja nie została zakończona Received: Otrzymano Sent: Wysłany $transaction_from: od %address% @@ -509,7 +508,7 @@ Are you sure you want to disable Face ID?: Czy na pewno chcesz wyłączyć Face Are you sure you want to disable Touch ID?: Czy na pewno chcesz wyłączyć Touch ID? Are you sure you want to disable biometrics?: Czy na pewno chcesz wyłączyć biometrię? Yes: Tak -Declined. Please try to confirm transaction using biometrics again.: Odrzucone. Proszę spróbować ponownie potwierdzić transakcję za pomocą biometrii. +Biometric confirmation failed: Potwierdzenie biometryczne nie powiodło się No Activity: Brak aktywności Add / Buy: Doładować Buy with Card: Kup za pomocą karty @@ -519,7 +518,6 @@ Explore: Badaj No partners yet: Jeszcze brak partnerów Clear: Wyczyść Get TON: Otrzymaj TON -Declined. Please try to confirm operation using biometrics again.: Odrzucone. Proszę spróbować ponownie potwierdzić operację za pomocą biometrii. $unstake_up_to_information: Natychmiastowo – do %value% $all_balance: "Wszystko: %balance%" $unstaking_not_enough_balance: Aby kontynuować wycofywanie stawki, musisz mieć %value% na głównym saldzie. @@ -678,10 +676,13 @@ Enter Secret Words: Wprowadź Tajne Słowa Turn this off so you can manually download updates and verify signatures.: Wyłącz to, aby móc ręcznie pobierać aktualizacje i weryfikować podpisy. InvalidMnemonic: Twoje słowa mnemoniczne są nieprawidłowe NFT Menu: Menu NFT -Wallets will not be locked.: Portfele nie będą zablokowane. -Wallets will be automatically locked after 30 seconds of inactivity.: Portfele zostaną automatycznie zablokowane po 30 sekundach braku aktywności. -Wallets will be automatically locked after 3 minutes of inactivity.: Portfele zostaną automatycznie zablokowane po 3 minutach braku aktywności. -Wallets will be automatically locked after 30 minutes of inactivity.: Portfele zostaną automatycznie zablokowane po 30 minutach braku aktywności. +Connection Type: Typ połączenia +Ledger is not supported on this device: Ledger nie jest obsługiwany na tym urządzeniu +Connect your Ledger: Podłącz swoje Ledger +App will not be locked.: Aplikacja nie zostanie zablokowana. +App will be automatically locked after 30 seconds of inactivity.: Aplikacja zostanie automatycznie zablokowana po 30 sekundach braku aktywności. +App will be automatically locked after 3 minutes of inactivity.: Aplikacja zostanie automatycznie zablokowana po 3 minutach braku aktywności. +App will be automatically locked after 30 minutes of inactivity.: Aplikacja zostanie automatycznie zablokowana po 30 minutach braku aktywności. Auto-Lock: Automatyczna blokada Never: Nigdy 30 seconds: 30 sekund diff --git a/src/i18n/ru.yaml b/src/i18n/ru.yaml index 8ed582ce..05902804 100644 --- a/src/i18n/ru.yaml +++ b/src/i18n/ru.yaml @@ -159,7 +159,6 @@ $nft_explore_offer: | Но вы можете просмотреть существующие коллекции. Open Getgems: Открыть Getgems -Transaction is not completed: Перевод не выполнен Received: Получено Sent: Отправлено $transaction_from: от %address% @@ -437,7 +436,7 @@ Enabling biometric confirmation will reset the password.: Включение б Biometric Registration: Подключение биометрии Step 1 of 2. Registration: Шаг 1 из 2. Регистрация Step 2 of 2. Verification: Шаг 2 из 2. Проверка -Download Logs: Загрузить журнал +Download Logs: Загрузить логи Biometric setup failed.: Настроить биометрию не удалось. Biometric confirmation failed.: Подтвердить биометрию не удалось. Failed to disable biometrics.: Не удалось отключить биометрию. @@ -508,7 +507,6 @@ Are you sure you want to disable Face ID?: Вы уверены, что хоти Are you sure you want to disable Touch ID?: Вы уверены, что хотите отключить Touch ID? Are you sure you want to disable biometrics?: Вы уверены, что хотите отключить биометрию? Yes: Да -Declined. Please try to confirm transaction using biometrics again.: Отклонено. Пожалуйста, попробуйте ещё раз подтвердить транзакцию с помощью биометрии. No Activity: Нет активности Add / Buy: Пополнить Buy with Card: Покупка с карты @@ -518,7 +516,7 @@ Explore: Обзор No partners yet: Ещё нет партнеров Clear: Очистить Get TON: Получить TON -Declined. Please try to confirm operation using biometrics again.: Отклонено. Пожалуйста, попробуйте ещё раз подтвердить операцию с помощью биометрии. +Biometric confirmation failed: Биометрическое подтверждение не удалось $unstake_up_to_information: Мгновенно – до %value% $all_balance: "Все: %balance%" $unstaking_not_enough_balance: Вам необходимо иметь %value% на вашем основном балансе, чтобы продолжить вывод. @@ -672,10 +670,13 @@ Enter Secret Words: Введите секретные слова Turn this off so you can manually download updates and verify signatures.: Отключите это, чтобы вручную загружать обновления и проверять подписи. InvalidMnemonic: Ваши введённые слова недействительны NFT Menu: Меню NFT -Wallets will not be locked.: Кошельки не будут заблокированы. -Wallets will be automatically locked after 30 seconds of inactivity.: Кошельки будут автоматически заблокированы через 30 секунд бездействия. -Wallets will be automatically locked after 3 minutes of inactivity.: Кошельки будут автоматически заблокированы через 3 минуты бездействия. -Wallets will be automatically locked after 30 minutes of inactivity.: Кошельки будут автоматически заблокированы через 30 минут бездействия. +Connection Type: Тип подключения +Ledger is not supported on this device: Ledger не поддерживается на этом устройстве +Connect your Ledger: Подключите ваш Ledger +App will not be locked.: Приложение не будет заблокировано. +App will be automatically locked after 30 seconds of inactivity.: Приложение будет автоматически заблокировано через 30 секунд бездействия. +App will be automatically locked after 3 minutes of inactivity.: Приложение будет автоматически заблокировано через 3 минуты бездействия. +App will be automatically locked after 30 minutes of inactivity.: Приложение будет автоматически заблокировано через 30 минут бездействия. Auto-Lock: Автоблокировка Never: Никогда 30 seconds: 30 секунд diff --git a/src/i18n/th.yaml b/src/i18n/th.yaml index 210ef44d..78629a90 100644 --- a/src/i18n/th.yaml +++ b/src/i18n/th.yaml @@ -156,7 +156,6 @@ $nft_explore_offer: | สำรวจมาร์เก็ตเพลสเพื่อค้นพบ แหล่งของ NFT คอลเลกชัน. Open Getgems: เปิด Getgems -Transaction is not completed: การทำรายการไม่สำเร็จ Received: ได้รับ Sent: ส่ง $transaction_from: จาก %address% @@ -506,7 +505,7 @@ Are you sure you want to disable Face ID?: คุณแน่ใจหรือ Are you sure you want to disable Touch ID?: คุณแน่ใจหรือไม่ว่าจะปิดใช้งาน Touch ID? Are you sure you want to disable biometrics?: คุณแน่ใจหรือไม่ว่าจะปิดใช้งาน Biometrics? Yes: ใช่ -Declined. Please try to confirm transaction using biometrics again.: ไม่อนุญาต. โปรดลองยืนยันการทำรายการด้วย Biometrics ของคุณใหม่อีกครั้ง. +Biometric confirmation failed: การยืนยันข้อมูลชีวภาพล้มเหลว No Activity: ไม่มีกิจกรรม Add / Buy: เพิ่ม / ซื้อ Buy with Card: ซื้อด้วยบัตร @@ -516,7 +515,6 @@ Explore: สำรวจ No partners yet: ยังไม่มีพาร์ทเนอร์ Clear: ล้าง Get TON: รับ TON -Declined. Please try to confirm operation using biometrics again.: ไม่อนุญาต. โปรดลองยืนยันการทำรายการด้วย Biometrics ของคุณใหม่อีกครั้ง. $unstake_up_to_information: ทันที – ถึง %value% $all_balance: "ทั้งหมด: %balance%" $unstaking_not_enough_balance: คุณจำเป็นต้องมี %value% ในบัญชีหลักของคุณเพื่อดำเนินการการถอน. @@ -675,10 +673,13 @@ Enter Secret Words: ป้อนคำลับ Turn this off so you can manually download updates and verify signatures.: ปิดสิ่งนี้เพื่อให้คุณสามารถดาวน์โหลดการอัปเดตด้วยตนเองและตรวจสอบลายเซ็น InvalidMnemonic: คำช่วยจำของคุณไม่ถูกต้อง NFT Menu: เมนู NFT -Wallets will not be locked.: กระเป๋าสตางค์จะไม่ถูกล็อค -Wallets will be automatically locked after 30 seconds of inactivity.: กระเป๋าสตางค์จะถูกล็อคโดยอัตโนมัติหลังจากไม่มีการใช้งาน 30 วินาที -Wallets will be automatically locked after 3 minutes of inactivity.: กระเป๋าสตางค์จะถูกล็อคโดยอัตโนมัติหลังจากไม่มีการใช้งาน 3 นาที -Wallets will be automatically locked after 30 minutes of inactivity.: กระเป๋าสตางค์จะถูกล็อคโดยอัตโนมัติหลังจากไม่มีการใช้งาน 30 นาที +Connection Type: ประเภทการเชื่อมต่อ +Ledger is not supported on this device: อุปกรณ์นี้ไม่รองรับ Ledger +Connect your Ledger: เชื่อมต่อ Ledger ของคุณ +App will not be locked.: แอปจะไม่ถูกล็อก +App will be automatically locked after 30 seconds of inactivity.: แอปจะถูกล็อกอัตโนมัติหลังจากไม่มีการใช้งานเป็นเวลา 30 วินาที +App will be automatically locked after 3 minutes of inactivity.: แอปจะถูกล็อกอัตโนมัติหลังจากไม่มีการใช้งานเป็นเวลา 3 นาที +App will be automatically locked after 30 minutes of inactivity.: แอปจะถูกล็อกอัตโนมัติหลังจากไม่มีการใช้งานเป็นเวลา 30 นาที Auto-Lock: การล็อคอัตโนมัติ Never: ไม่เคย 30 seconds: 30 วินาที diff --git a/src/i18n/tr.yaml b/src/i18n/tr.yaml index b1b8dbf5..d2e76790 100644 --- a/src/i18n/tr.yaml +++ b/src/i18n/tr.yaml @@ -156,7 +156,6 @@ $nft_explore_offer: | Mevcut NFT koleksiyonlarını görmek için NFT pazaryerini inceleyin. Open Getgems: Getgems'i aç -Transaction is not completed: İşlem tamamlanmadı Received: Alındı Sent: Gönder $transaction_from: "bu adresten = %address%" @@ -505,7 +504,7 @@ Are you sure you want to disable Face ID?: Face ID'yi devre dışı bırakmak is Are you sure you want to disable Touch ID?: Touch ID'yi devre dışı bırakmak istediğinizden emin misiniz? Are you sure you want to disable biometrics?: Biyometrik korumayı devre dışı bırakmak istediğinizden emin misiniz? Yes: Evet -Declined. Please try to confirm transaction using biometrics again.: Reddedildi. Lütfen biyometrik koruma kullanarak işlemi tekrar onaylamayı deneyin. +Biometric confirmation failed: Biyometrik doğrulama başarısız oldu No Activity: Etkinlik yok Add / Buy: Yükleme Buy with Card: Kart ile Satın Al @@ -515,7 +514,6 @@ Explore: Keşfetmek No partners yet: Henüz ortak yok Clear: Temizle Get TON: TON Almak -Declined. Please try to confirm operation using biometrics again.: Reddedildi. Lütfen biyometrik koruma kullanarak işlemi tekrar onaylamayı deneyin. $unstake_up_to_information: Anında – en fazla %value% $all_balance: "Tümü: %balance%" $unstaking_not_enough_balance: Stake iptal işlemine devam etmek için ana bakiyenizde %value% olması gerekmektedir. @@ -672,10 +670,13 @@ Enter Secret Words: Gizli Kelimeleri Girin Turn this off so you can manually download updates and verify signatures.: Bunu kapatın, böylece güncellemeleri manuel olarak indirip imzaları doğrulayabilirsiniz. InvalidMnemonic: Mnemonik kelimeleriniz geçersiz NFT Menu: NFT Menüsü -Wallets will not be locked.: Cüzdanlar kilitlenmeyecek. -Wallets will be automatically locked after 30 seconds of inactivity.: Cüzdanlar 30 saniye hareketsizlikten sonra otomatik olarak kilitlenecek. -Wallets will be automatically locked after 3 minutes of inactivity.: Cüzdanlar 3 dakika hareketsizlikten sonra otomatik olarak kilitlenecek. -Wallets will be automatically locked after 30 minutes of inactivity.: Cüzdanlar 30 dakika hareketsizlikten sonra otomatik olarak kilitlenecek. +Connection Type: Bağlantı Türü +Ledger is not supported on this device: Ledger bu cihazda desteklenmiyor +Connect your Ledger: Ledger'ınızı bağlayın +App will not be locked.: Uygulama kilitlenmeyecek. +App will be automatically locked after 30 seconds of inactivity.: Uygulama 30 saniye hareketsizlikten sonra otomatik olarak kilitlenecek. +App will be automatically locked after 3 minutes of inactivity.: Uygulama 3 dakika hareketsizlikten sonra otomatik olarak kilitlenecek. +App will be automatically locked after 30 minutes of inactivity.: Uygulama 30 dakika hareketsizlikten sonra otomatik olarak kilitlenecek. Auto-Lock: Otomatik Kilit Never: Asla 30 seconds: 30 saniye diff --git a/src/i18n/uk.yaml b/src/i18n/uk.yaml index eb5e333e..555f4249 100644 --- a/src/i18n/uk.yaml +++ b/src/i18n/uk.yaml @@ -439,7 +439,7 @@ Enabling biometric confirmation will reset the password.: Увімкнення Biometric Registration: Підключення біометрії Step 1 of 2. Registration: Крок 1 із 2. Реєстрація Step 2 of 2. Verification: Крок 2 з 2. Перевірка -Download Logs: Завантажити журнали +Download Logs: Завантажити логи Biometric setup failed.: Налаштувати біометрію не вдалося. Biometric confirmation failed.: Підтвердити біометрію не вдалося. Failed to disable biometrics.: Не вдалося відключити біометрію. @@ -510,7 +510,7 @@ Are you sure you want to disable Face ID?: Ви впевнені, що хоче Are you sure you want to disable Touch ID?: Ви впевнені, що хочете відключити Touch ID? Are you sure you want to disable biometrics?: Ви впевнені, що хочете відключити біометрію? Yes: Так -Declined. Please try to confirm transaction using biometrics again.: Відхилено. Будь ласка, спробуйте ще раз підтвердити транзакцію за допомогою біометрії. +Biometric confirmation failed: Біометричне підтвердження не вдалося No Activity: Немає активності Add / Buy: Поповнити Buy with Card: Купівля з картки @@ -520,7 +520,6 @@ Explore: Огляд No partners yet: Ще немає партнерів Clear: Очистити Get TON: Отримати TON -Declined. Please try to confirm operation using biometrics again.: Відхилено. Будь ласка, спробуйте ще раз підтвердити операцію за допомогою біометрії. $unstake_up_to_information: Миттєво - до %value% $all_balance: "Все: %balance%" $unstaking_not_enough_balance: Вам необхідно мати %value% на вашому основному балансі, щоб продовжити виведення. @@ -677,10 +676,13 @@ Enter Secret Words: Введіть секретні слова Turn this off so you can manually download updates and verify signatures.: Вимкніть це, щоб ви могли вручну завантажувати оновлення та перевіряти підписи. InvalidMnemonic: Ваші мнемонічні слова недійсні NFT Menu: Меню NFT -Wallets will not be locked.: Гаманці не будуть заблоковані. -Wallets will be automatically locked after 30 seconds of inactivity.: Гаманці будуть автоматично заблоковані через 30 секунд бездіяльності. -Wallets will be automatically locked after 3 minutes of inactivity.: Гаманці будуть автоматично заблоковані через 3 хвилини бездіяльності. -Wallets will be automatically locked after 30 minutes of inactivity.: Гаманці будуть автоматично заблоковані через 30 хвилин бездіяльності. +Connection Type: Тип підключення +Ledger is not supported on this device: Ledger не підтримується на цьому пристрої +Connect your Ledger: Підключіть ваш Ledger +App will not be locked.: Додаток не буде заблоковано. +App will be automatically locked after 30 seconds of inactivity.: Додаток буде автоматично заблоковано через 30 секунд бездіяльності. +App will be automatically locked after 3 minutes of inactivity.: Додаток буде автоматично заблоковано через 3 хвилини бездіяльності. +App will be automatically locked after 30 minutes of inactivity.: Додаток буде автоматично заблоковано через 30 хвилин бездіяльності. Auto-Lock: Автоблокування Never: Ніколи 30 seconds: 30 секунд diff --git a/src/i18n/zh-Hans.yaml b/src/i18n/zh-Hans.yaml index ff655eed..26704668 100644 --- a/src/i18n/zh-Hans.yaml +++ b/src/i18n/zh-Hans.yaml @@ -150,7 +150,6 @@ No NFTs yet: NFT 空空如也··· $nft_explore_offer: | 在市场中寻找心仪的 NFT 收藏。 Open Getgems: 打开 Getgems -Transaction is not completed: 交易未完成 Received: 已收到 Sent: 已发送 $transaction_from: 从 %address% @@ -493,7 +492,7 @@ Are you sure you want to disable Face ID?: 您确定要禁用Face ID吗? Are you sure you want to disable Touch ID?: 您确定要禁用Touch ID吗? Are you sure you want to disable biometrics?: 您确定要禁用生物识别技术吗? Yes: 是的 -Declined. Please try to confirm transaction using biometrics again.: 拒绝了。 请尝试再次使用生物识别技术确认交易。 +Biometric confirmation failed: 生物识别确认失败 No Activity: 无活动 Add / Buy: 添加 / 购买 Buy with Card: 用卡购买 @@ -503,7 +502,6 @@ Explore: 探索 No partners yet: 还没有伴侣 Clear: 清除 Get TON: 获取TON -Declined. Please try to confirm operation using biometrics again.: 拒绝了。 请再次尝试使用生物识别来确认操作。 $unstake_up_to_information: 立即 – 高达 %value% $all_balance: "全部: %balance%" $unstaking_not_enough_balance: 您需要在主要余额中有 %value% 才能继续解押。 @@ -659,10 +657,13 @@ Enter Secret Words: 输入密语 Turn this off so you can manually download updates and verify signatures.: 关闭此功能以便手动下载更新并验证签名。 InvalidMnemonic: 您的助记词无效 NFT Menu: NFT菜单 -Wallets will not be locked.: 钱包不会被锁定。 -Wallets will be automatically locked after 30 seconds of inactivity.: 钱包将在30秒不活动后自动锁定。 -Wallets will be automatically locked after 3 minutes of inactivity.: 钱包将在3分钟不活动后自动锁定。 -Wallets will be automatically locked after 30 minutes of inactivity.: 钱包将在30分钟不活动后自动锁定。 +Connection Type: 连接类型 +Ledger is not supported on this device: 此设备不支持 Ledger +Connect your Ledger: 连接您的 Ledger +App will not be locked.: 应用程序不会被锁定。 +App will be automatically locked after 30 seconds of inactivity.: 应用程序将在 30 秒无操作后自动锁定。 +App will be automatically locked after 3 minutes of inactivity.: 应用程序将在 3 分钟无操作后自动锁定。 +App will be automatically locked after 30 minutes of inactivity.: 应用程序将在 30 分钟无操作后自动锁定。 Auto-Lock: 自动锁定 Never: 从不 30 seconds: 30秒 diff --git a/src/i18n/zh-Hant.yaml b/src/i18n/zh-Hant.yaml index 487b1a04..495af892 100644 --- a/src/i18n/zh-Hant.yaml +++ b/src/i18n/zh-Hant.yaml @@ -150,7 +150,6 @@ No NFTs yet: 尚未有 NFT $nft_explore_offer: | 探索市場來尋找現有的 NFT 收藏. Open Getgems: 開啟 Getgems -Transaction is not completed: 交易未完成 Received: 已收到 Sent: 已送出 $transaction_from: 從 %address% @@ -493,7 +492,7 @@ Are you sure you want to disable Face ID?: 您確定要禁用Face ID嗎? Are you sure you want to disable Touch ID?: 您確定要禁用Touch ID嗎? Are you sure you want to disable biometrics?: 您確定要禁用生物識別技術嗎? Yes: 是的 -Declined. Please try to confirm transaction using biometrics again.: 拒絕了。 請嘗試再次使用生物辨識技術確認交易。 +Biometric confirmation failed: 生物辨識確認失敗 No Activity: 無活動 Add / Buy: 添加 / 購買 Buy with Card: 用卡購買 @@ -503,7 +502,6 @@ Explore: 探索 No partners yet: 還沒有伴侶 Clear: 清除 Get TON: 獲取TON -Declined. Please try to confirm operation using biometrics again.: 拒絕了。 請再次嘗試使用生物辨識來確認操作。 $unstake_up_to_information: 即時 – 高達 %value% $all_balance: "全部: %balance%" $unstaking_not_enough_balance: 您需要在您的主要余额中有 %value% 才能继续进行解押。 @@ -659,10 +657,13 @@ Enter Secret Words: 輸入密語 Turn this off so you can manually download updates and verify signatures.: 關閉此功能以手動下載更新並驗證簽名。 InvalidMnemonic: 您的助記詞無效 NFT Menu: NFT選單 -Wallets will not be locked.: 錢包不會被鎖定。 -Wallets will be automatically locked after 30 seconds of inactivity.: 錢包將在30秒不活動後自動鎖定。 -Wallets will be automatically locked after 3 minutes of inactivity.: 錢包將在3分鐘不活動後自動鎖定。 -Wallets will be automatically locked after 30 minutes of inactivity.: 錢包將在30分鐘不活動後自動鎖定。 +Connection Type: 連接類型 +Ledger is not supported on this device: 此設備不支持 Ledger +Connect your Ledger: 連接您的 Ledger +App will not be locked.: 應用程式不會被鎖定。 +App will be automatically locked after 30 seconds of inactivity.: 應用程式將在 30 秒無操作後自動鎖定。 +App will be automatically locked after 3 minutes of inactivity.: 應用程式將在 3 分鐘無操作後自動鎖定。 +App will be automatically locked after 30 minutes of inactivity.: 應用程式將在 30 分鐘無操作後自動鎖定。 Auto-Lock: 自動鎖定 Never: 從不 30 seconds: 30秒 diff --git a/src/lib/ledger-hw-transport-ble/BleTransport.ts b/src/lib/ledger-hw-transport-ble/BleTransport.ts new file mode 100644 index 00000000..d31eabc1 --- /dev/null +++ b/src/lib/ledger-hw-transport-ble/BleTransport.ts @@ -0,0 +1,811 @@ +import type { BleService } from '@capacitor-community/bluetooth-le'; +import { BleClient, ConnectionPriority } from '@capacitor-community/bluetooth-le'; +import type { BluetoothInfos, DeviceModel } from '@ledgerhq/devices'; +import { DeviceModelId, getBluetoothServiceUuids, getInfosForServiceUuid } from '@ledgerhq/devices'; +import { receiveAPDU } from '@ledgerhq/devices/lib/ble/receiveAPDU'; +// --------------------------------------------------------------------------------------------- +// Since this is a react-native library and metro bundler does not support +// package exports yet (see: https://github.com/facebook/metro/issues/670) +// we need to import the file directly from the lib folder. +// Otherwise it would force the consumer of the lib to manually "tell" metro to resolve to /lib. +// +// TLDR: /!\ Do not remove the /lib part in the import statements below (@ledgerhq/devices/lib) ! /!\ +// See: https://github.com/LedgerHQ/ledger-live/pull/879 +import { sendAPDU } from '@ledgerhq/devices/lib/ble/sendAPDU'; +import type { HwTransportError } from '@ledgerhq/errors'; +import { + CantOpenDevice, + DisconnectedDeviceDuringOperation, + PairingFailed, + TransportError, + TransportExchangeTimeoutError, +} from '@ledgerhq/errors'; +import type { Observer as TransportObserver, Subscription as TransportSubscription } from '@ledgerhq/hw-transport'; +import Transport from '@ledgerhq/hw-transport'; +import type { TraceContext } from '@ledgerhq/logs'; +import { LocalTracer, trace } from '@ledgerhq/logs'; +import type { Observable, SchedulerLike } from 'rxjs'; +import { + defer, firstValueFrom, from, merge, of, throwError, TimeoutError, +} from 'rxjs'; +import { + catchError, finalize, first, ignoreElements, map, share, tap, timeout, +} from 'rxjs/operators'; +import { v4 as uuid } from 'uuid'; +import type { BleCharacteristic } from '@capacitor-community/bluetooth-le/dist/esm/definitions'; + +import type { IOBleErrorRemap } from './remapErrors'; +import type { Characteristic, Device, ReconnectionConfig } from './types'; + +import { awaitsBleOn } from './awaitsBleOn'; +import { monitorCharacteristic } from './monitorCharacteristic'; +import { decoratePromiseErrors, remapError } from './remapErrors'; + +const LOG_TYPE = 'ble-verbose'; + +/** + * This is potentially not needed anymore, to be checked if the bug is still happening. + */ +const reconnectionConfig: ReconnectionConfig | null | undefined = { + pairingThreshold: 1000, + delayAfterFirstPairing: 4000, +}; + +// Allows us to give more granulary error messages +const bluetoothInfoCache: { [deviceUuid: string]: BluetoothInfos } = {}; + +function retrieveInfos(device: Device | null): BluetoothInfos | undefined { + if (!device || !device.uuids) return undefined; + const [serviceUUID] = device.uuids; + if (!serviceUUID) return undefined; + const infos = getInfosForServiceUuid(serviceUUID); + if (!infos) return undefined; + + // If we retrieved information, update the cache + bluetoothInfoCache[device.deviceId] = infos; + return infos; +} + +// eslint-disable-next-line no-promise-executor-return +const delay = (ms: number | undefined) => new Promise((success) => setTimeout(success, ms)); + +/** + * A cache of Bluetooth transport instances associated with device IDs. + * Allows efficient storage and retrieval of previously initialized transports. + * @type {Object.} + */ +const transportsCache: { [deviceId: string]: BleTransport } = {}; + +// `connectOptions` is actually used by `react-native-ble-plx` even if comment above `ConnectionOptions` says it's not used +const connectOptions: Record = { + // 156 bytes to max the iOS < 10 limit (158 bytes) + // (185 bytes for iOS >= 10)(up to 512 bytes for Android, but could be blocked at 23 bytes) + requestMTU: 156, + // Priority 1 = high. + connectionPriority: 1, +}; + +const clearDisconnectTimeout = (deviceId: string, context?: TraceContext): void => { + const cachedTransport = transportsCache[deviceId]; + if (cachedTransport && cachedTransport.disconnectTimeout) { + trace({ type: LOG_TYPE, message: 'Clearing queued disconnect', context }); + clearTimeout(cachedTransport.disconnectTimeout); + } +}; + +let currentDeviceService: string | undefined; + +/** + * React Native bluetooth BLE implementation + * @example + * import BleTransport from "@ledgerhq/react-native-hw-transport-ble"; + */ +export default class BleTransport extends Transport { + static disconnectTimeoutMs = 5000; + + static list = (): Promise => { + throw new Error('not implemented'); + }; + + /** + * Scan for bluetooth Ledger devices + * @param observer Device is partial in order to avoid the live-common/this dep + * @returns TransportSubscription + */ + static listen( + observer: TransportObserver, + ): TransportSubscription { + let unsubscribed: boolean = false; + const tracer = new LocalTracer(LOG_TYPE); + tracer.trace('Listening for devices ...'); + + BleClient.getConnectedDevices(getBluetoothServiceUuids()).then(async (devices) => { + if (unsubscribed) return; + for (const it of devices) { + observer.next({ + type: 'add', + device: it, + }); + } + await BleClient.stopLEScan(); + void BleClient.requestLEScan({ + services: getBluetoothServiceUuids(), + }, (result) => { + if (unsubscribed) return; + observer.next({ + type: 'add', + device: result.device, + }); + }); + }); + + return { + unsubscribe: async () => { + unsubscribed = true; + await BleClient.stopLEScan(); + }, + }; + } + + /** + * Opens a BLE transport + * + * @param {Device | string} deviceOrId + * @param timeoutMs Applied when trying to connect to a device + * @param context An optional context object for log/tracing strategy + * @param injectedDependencies Contains optional injected dependencies used by the transport implementation + * - rxjsScheduler: dependency injected RxJS scheduler to control time. Default AsyncScheduler. + */ + static async open( + deviceOrId: Device | string, + timeoutMs?: number, + context?: TraceContext, + { rxjsScheduler }: { rxjsScheduler?: SchedulerLike } = {}, + ): Promise { + return open(deviceOrId, true, timeoutMs, context, { rxjsScheduler }); + } + + /** + * Exposes method from the ble-plx library to disconnect a device + * + * Disconnects from {@link Device} if it's connected or cancels pending connection. + * A "disconnect" event will normally be emitted by the ble-plx lib once the device is disconnected. + * Errors are logged but silenced. + */ + static disconnectDevice = async (id: string, + onDisconnect?: (e?: Error) => void, + context?: TraceContext): Promise => { + const tracer = new LocalTracer(LOG_TYPE, context); + tracer.trace(`Trying to disconnect device ${id}`); + + try { + await BleClient.disconnect(id); + onDisconnect?.(); + } catch (error) { + // Only log, ignore if disconnect did not work + tracer + .withType('ble-error') + .trace('Error while trying to cancel device connection', { error }); + } + tracer.trace(`Device ${id} disconnected`); + }; + + device: Device; + + deviceModel: DeviceModel; + + // eslint-disable-next-line no-null/no-null + disconnectTimeout: null | ReturnType = null; + + id: string; + + isConnected = true; + + mtuSize = 20; + + // Observable emitting data received from the device via BLE + notifyObservable: Observable; + + notYetDisconnected = true; + + writableWithResponseCharacteristic: Characteristic; + + writableWithoutResponseCharacteristic: Characteristic | undefined; + + rxjsScheduler?: SchedulerLike; + + // Transaction ids of communication operations that are currently pending + currentTransactionIds: Array; + + onDisconnect: ((error?: Error) => void) | undefined; + + disconnectCallback: (() => void) | undefined; + + /** + * The static `open` function is used to handle `BleTransport` instantiation + * + * @param device + * @param writableWithResponseCharacteristic A BLE characteristic that we can write on, + * and that will be acknowledged in response from the device when it receives the written value. + * @param writableWithoutResponseCharacteristic A BLE characteristic that we can write on, + * and that will not be acknowledged in response from the device + * @param notifyObservable A multicast observable that emits messages received from the device + * @param deviceModel + * @param params Contains optional options and injected dependencies used by the transport implementation + * - abortTimeoutMs: stop the exchange after a given timeout. Another timeout exists + * to detect unresponsive device (see `unresponsiveTimeout`). This timeout aborts the exchange. + * - rxjsScheduler: dependency injected RxJS scheduler to control time. Default: AsyncScheduler. + */ + constructor( + device: Device, + writableWithResponseCharacteristic: Characteristic, + writableWithoutResponseCharacteristic: Characteristic | undefined, + notifyObservable: Observable, + deviceModel: DeviceModel, + { context, rxjsScheduler }: { context?: TraceContext; rxjsScheduler?: SchedulerLike } = {}, + ) { + super({ context, logType: LOG_TYPE }); + this.id = device.deviceId; + this.device = device; + this.writableWithResponseCharacteristic = writableWithResponseCharacteristic; + this.writableWithoutResponseCharacteristic = writableWithoutResponseCharacteristic; + this.notifyObservable = notifyObservable; + this.deviceModel = deviceModel; + this.rxjsScheduler = rxjsScheduler; + this.currentTransactionIds = []; + + clearDisconnectTimeout(this.id); + + this.tracer.trace(`New instance of BleTransport for device ${this.id}`); + } + + /** + * A message exchange (APDU request <-> response) with the device that can be aborted. + * + * The message will be BLE-encoded/framed before being sent, and the response will be BLE-decoded. + * + * @param message A buffer (u8 array) of a none BLE-encoded message (an APDU for ex) to be sent to the device + * as a request + * @param options Contains optional options for the exchange function + * - abortTimeoutMs: stop the exchange after a given timeout. Another timeout exists + * to detect unresponsive device (see `unresponsiveTimeout`). This timeout aborts the exchange. + * @returns A promise that resolves with the response data from the device. + */ + exchange = ( + message: Buffer, + { abortTimeoutMs }: { abortTimeoutMs?: number } = {}, + ): Promise => { + if (this.exchangeBusyPromise) { + void BleTransport.disconnectDevice(this.id, this.onDisconnect); + } + const tracer = this.tracer.withUpdatedContext({ + function: 'exchange', + }); + tracer.trace('Exchanging APDU ...', { abortTimeoutMs }); + tracer.withType('apdu').trace(`=> ${message.toString('hex')}`); + + return this.exchangeAtomicImpl(() => { + return firstValueFrom( + // `sendApdu` will only emit if an error occurred, otherwise it will complete, + // while `receiveAPDU` will emit the full response. + // Consequently, it monitors the response while being able to reject on an error from the send. + merge( + this.notifyObservable.pipe((data) => receiveAPDU(data, { context: tracer.getContext() })), + sendAPDU(this.write, message, this.mtuSize, { + context: tracer.getContext(), + }), + ).pipe( + abortTimeoutMs ? timeout(abortTimeoutMs, this.rxjsScheduler) : tap(), + tap((data) => { + tracer.withType('apdu').trace(`<= ${data.toString('hex')}`); + }), + catchError(async (error) => { + // Currently only 1 reason the exchange has been explicitly aborted (other than job and transport errors): a timeout + if (error instanceof TimeoutError) { + tracer.trace( + 'Aborting due to timeout and trying to cancel all communication write of the current exchange', + { + abortTimeoutMs, + transactionIds: this.currentTransactionIds, + }, + ); + + // No concurrent exchange should happen at the same time, so all pending operations are part of the same exchange + await this.cancelPendingOperations(); + + throw new TransportExchangeTimeoutError('Exchange aborted due to timeout'); + } + + tracer.withType('ble-error').trace('Error while exchanging APDU', { error }); + + if (this.notYetDisconnected) { + // In such case we will always disconnect because something is bad. + // This sends a `disconnect` event. + await BleTransport.disconnectDevice(this.id, this.onDisconnect); + } + + const mappedError = remapError(error as IOBleErrorRemap); + tracer.trace('Error while exchanging APDU, mapped and throws following error', { + mappedError, + }); + // eslint-disable-next-line @typescript-eslint/no-throw-literal + throw mappedError; + }), + finalize(() => { + tracer.trace('Clearing current transaction ids', { + currentTransactionIds: this.currentTransactionIds, + }); + this.clearCurrentTransactionIds(); + }), + ), + ); + }); + }; + + private async cancelPendingOperations() { + // BleTransport does not support cancellation + await BleTransport.disconnectDevice(this.id, this.onDisconnect); + } + + /** + * Sets the collection of current transaction ids to an empty array + */ + private clearCurrentTransactionIds() { + this.currentTransactionIds = []; + } + + /** + * Negotiate with the device the maximum transfer unit for the ble frames + * @returns Promise + */ + async inferMTU(): Promise { + let mtu = (await BleClient.getMtu(this.device.deviceId)); + this.tracer.trace('Inferring MTU ...', { currentDeviceMtu: mtu }); + + await this.exchangeAtomicImpl(async () => { + try { + mtu = await firstValueFrom( + merge( + this.notifyObservable.pipe( + map((maybeError) => { + // Catches the `PairingFailed` Error that has only been emitted + if (maybeError instanceof Error) { + throw maybeError; + } + + return maybeError; + }), + first((buffer) => buffer.readUInt8(0) === 0x08), + map((buffer) => buffer.readUInt8(5)), + ), + defer(() => from(this.write(Buffer.from([0x08, 0, 0, 0, 0])))).pipe(ignoreElements()), + ), + ); + } catch (error: any) { + this.tracer.withType('ble-error').trace('Error while inferring MTU', { mtu }); + + await BleTransport.disconnectDevice(this.id, this.onDisconnect); + + const mappedError = remapError(error); + this.tracer.trace('Error while inferring APDU, mapped and throws following error', { + mappedError, + }); + // eslint-disable-next-line @typescript-eslint/no-throw-literal + throw mappedError; + } finally { + // When negotiating the MTU, a message is sent/written to the device, and a transaction id was associated to this write + this.clearCurrentTransactionIds(); + } + }); + + this.tracer.trace('Successfully negotiated MTU with device', { + mtu, + mtuSize: this.mtuSize, + }); + if (mtu > 20) { + this.mtuSize = mtu; + } + + return this.mtuSize; + } + + /** + * Exposed method from the ble-plx library. + * Request the connection priority for the given device. + * @returns {Promise} + * @param connectionPriority + */ + async requestConnectionPriority( + connectionPriority: 'Balanced' | 'High' | 'LowPower', + ): Promise { + let connectionPriorityMapped: ConnectionPriority; + switch (connectionPriority) { + case 'High': + connectionPriorityMapped = ConnectionPriority.CONNECTION_PRIORITY_BALANCED; + break; + case 'LowPower': + connectionPriorityMapped = ConnectionPriority.CONNECTION_PRIORITY_LOW_POWER; + break; + case 'Balanced': + connectionPriorityMapped = ConnectionPriority.CONNECTION_PRIORITY_BALANCED; + break; + } + await decoratePromiseErrors( + BleClient.requestConnectionPriority(this.device.deviceId, connectionPriorityMapped), + ); + } + + /** + * Do not call this directly unless you know what you're doing. Communication + * with a Ledger device should be through the {@link exchange} method. + * + * For each call a transaction id is added to the current stack of transaction ids. + * With this transaction id, a pending BLE communication operations can be cancelled. + * Note: each frame/packet of a longer BLE-encoded message to be sent should have their unique transaction id. + * + * @param buffer BLE-encoded packet to send to the device + * @param frameId Frame id to make `write` aware of a bigger message that this frame/packet is part of. + * Helps to create related a collection of transaction ids + */ + write = async (buffer: Buffer): Promise => { + const transactionId = uuid(); + this.currentTransactionIds.push(transactionId); + + const tracer = this.tracer.withUpdatedContext({ transactionId }); + tracer.trace('Writing to device', { + willMessageBeAcked: !this.writableWithoutResponseCharacteristic, + }); + + try { + const uint8Array = new Uint8Array(buffer); + const dataView = new DataView(uint8Array.buffer, uint8Array.byteOffset, uint8Array.byteLength); + await BleClient.write( + this.device.deviceId, + currentDeviceService!, + this.writableWithResponseCharacteristic.uuid, + dataView, + ); + tracer.withType('ble-frame').trace(`=> ${buffer.toString('hex')}`); + } catch (error: unknown) { + tracer.trace('Error while writing APDU', { error }); + throw new DisconnectedDeviceDuringOperation( + error instanceof Error ? error.message : `${error}`, + ); + } + }; + + /** + * We intentionally do not immediately close a transport connection. + * Instead, we queue the disconnect and wait for a future connection to dismiss the event. + * This approach prevents unnecessary disconnects and reconnects. We use the isConnected + * flag to ensure that we do not trigger a disconnect if the current cached transport has + * already been disconnected. + * @returns {Promise} + */ + async close(): Promise { + const tracer = this.tracer.withUpdatedContext({ function: 'close' }); + tracer.trace('Closing, queuing a disconnect with a timeout ...'); + + let resolve: (value: void | PromiseLike) => void; + const disconnectPromise = new Promise((innerResolve) => { + resolve = innerResolve; + }); + + clearDisconnectTimeout(this.id); + + this.disconnectTimeout = setTimeout(() => { + tracer.trace('Disconnect timeout has been reached ...'); + if (this.isConnected) { + BleTransport.disconnectDevice(this.id, this.onDisconnect, tracer.getContext()) + .catch(() => {}) + .finally(resolve); + } else { + resolve(); + } + }, BleTransport.disconnectTimeoutMs); + + // The closure will occur no later than 5s, triggered either by disconnection + // or the actual response of the apdu. + await Promise.race([this.exchangeBusyPromise || Promise.resolve(), disconnectPromise]); + } +} + +/** + * Opens a BLE connection with a given device. Returns a Transport instance. + * + * @param deviceOrId + * @param needsReconnect + * @param timeoutMs Optional Timeout (in ms) applied during the connection with the device + * @param context Optional tracing/log context + * @param injectedDependencies Contains optional injected dependencies used by the transport implementation + * - rxjsScheduler: dependency injected RxJS scheduler to control time. Default AsyncScheduler. + * @returns A BleTransport instance + */ +async function open( + deviceOrId: Device | string, + needsReconnect: boolean, + timeoutMs?: number, + context?: TraceContext, + { rxjsScheduler }: { rxjsScheduler?: SchedulerLike } = {}, +) { + const tracer = new LocalTracer(LOG_TYPE, context); + let device: Device; + tracer.trace(`Opening ${deviceOrId}`, { needsReconnect }); + let deviceId: string; + let transport: BleTransport; + + if (typeof deviceOrId === 'string') { + deviceId = deviceOrId; + if (transportsCache[deviceOrId]) { + tracer.trace('Transport in cache, using it'); + clearDisconnectTimeout(deviceOrId); + + // The cached transport probably has an older trace/log context + transportsCache[deviceOrId].setTraceContext(context); + return transportsCache[deviceOrId]; + } + + tracer.trace(`Trying to open device: ${deviceOrId}`); + await awaitsBleOn(); + + // Returns a list of known devices by their identifiers + const devices = (await BleClient.getDevices([deviceOrId])); + tracer.trace(`Found ${devices.length} already known device(s) with given id`, { deviceOrId }); + [device] = devices; + + if (!device) { + // Returns a list of the peripherals currently connected to the system + // which have discovered services, connected to system doesn't mean + // connected to our app, we check that below. + const services = (await BleClient.getServices(deviceOrId)).map((it) => it.uuid); + const connectedDevices = (await BleClient.getConnectedDevices(services)); + const connectedDevicesFiltered = connectedDevices.filter((d) => d.deviceId === deviceOrId); + tracer.trace( + `No known device with given id. + Found ${connectedDevicesFiltered.length} devices from already connected devices`, + { deviceOrId }, + ); + [device] = connectedDevicesFiltered; + } + + if (!device) { + // We still don't have a device, so we attempt to connect to it. + tracer.trace('No known nor connected devices with given id. Trying to connect to device', { + deviceOrId, + timeoutMs, + }); + + // Nb ConnectionOptions dropped since it's not used internally by ble-plx. + try { + await BleClient.connect(deviceOrId, () => { + transport.onDisconnect?.(); + }, { + timeout: timeoutMs, + }); + } catch (e: any) { + tracer.trace(`Error code: ${e.errorCode}`); + throw e; + } + } + + if (!device) { + throw new CantOpenDevice(); + } + } else { + // It was already a Device + device = deviceOrId; + deviceId = deviceOrId.deviceId; + } + + const connectedDevices = await BleClient.getConnectedDevices(getBluetoothServiceUuids()); + + if (!connectedDevices.find((it) => it.deviceId === deviceId)) { + tracer.trace('Device found but not connected. connecting...', { timeoutMs, connectOptions }); + try { + await BleClient.connect(deviceId, () => { + transport.onDisconnect?.(); + }, { + timeout: timeoutMs, + }); + } catch (error: any) { + tracer.trace('Connect error', { error }); + // eslint-disable-next-line @typescript-eslint/no-throw-literal + throw remapError(error); + } + } + + tracer.trace('Device is connected now, getting services and characteristics'); + + let services: BleService[] = []; + + await BleClient.discoverServices(deviceId); + services = (await BleClient.getServices(deviceId)); + + let res: BluetoothInfos | undefined = retrieveInfos(device); + const characteristics: BleCharacteristic[] = []; + + if (!res) { + for (const serviceUUID of getBluetoothServiceUuids()) { + try { + const deviceService = services.find((it) => it.uuid === serviceUUID); + res = getInfosForServiceUuid(serviceUUID); + if (res && deviceService) { + characteristics.push(...deviceService.characteristics); + currentDeviceService = res.serviceUuid; + break; + } + } catch (e) { + // Attempt to connect to the next service + } + } + } + + if (!res) { + tracer.trace('Service not found'); + throw new TransportError('service not found', 'BLEServiceNotFound'); + } + + const { + deviceModel, writeUuid, writeCmdUuid, notifyUuid, + } = res; + + /* if (!characteristics) { + characteristics = await device.characteristicsForService(serviceUuid); + } */ + + if (!characteristics) { + tracer.trace('Characteristics not found'); + throw new TransportError('service not found', 'BLEServiceNotFound'); + } + + let writableWithResponseCharacteristic: Characteristic | null | undefined; + let writableWithoutResponseCharacteristic: Characteristic | undefined; + // A characteristic that can monitor value changes + let notifiableCharacteristic: Characteristic | null | undefined; + + for (const c of characteristics) { + if (c.uuid === writeUuid) { + writableWithResponseCharacteristic = c; + } else if (c.uuid === writeCmdUuid) { + writableWithoutResponseCharacteristic = c; + } else if (c.uuid === notifyUuid) { + notifiableCharacteristic = c; + } + } + + if (!writableWithResponseCharacteristic) { + throw new TransportError('write characteristic not found', 'BLECharacteristicNotFound'); + } + + if (!notifiableCharacteristic) { + throw new TransportError('notify characteristic not found', 'BLECharacteristicNotFound'); + } + + if (!writableWithResponseCharacteristic.properties.write) { + throw new TransportError( + 'The writable-with-response characteristic is not writable with response', + 'BLECharacteristicInvalid', + ); + } + + if (!notifiableCharacteristic.properties.notify) { + throw new TransportError('notify characteristic not notifiable', 'BLECharacteristicInvalid'); + } + + if (writableWithoutResponseCharacteristic) { + if (!writableWithoutResponseCharacteristic.properties.writeWithoutResponse) { + throw new TransportError( + 'The writable-without-response characteristic is not writable without response', + 'BLECharacteristicInvalid', + ); + } + } + + const deviceMtu = await BleClient.getMtu(device.deviceId); + tracer.trace(`device.mtu=${deviceMtu}`); + + // Inits the observable that will emit received data from the device via BLE + const notifyObservable = monitorCharacteristic(deviceId, + currentDeviceService!, + notifiableCharacteristic, + context).pipe( + catchError((e) => { + // LL-9033 fw 2.0.2 introduced this case, we silence the inner unhandled error. + // It will be handled when negotiating the MTU in `inferMTU` but will be ignored in other cases. + const msg = String(e); + return msg.includes('notify change failed') + ? of(new PairingFailed(msg)) + : throwError(() => e); + }), + tap((value) => { + if (value instanceof PairingFailed) return; + trace({ type: 'ble-frame', message: `<= ${value.toString('hex')}`, context }); + }), + // Returns a new Observable that multicasts (shares) the original Observable. + // As long as there is at least one Subscriber this Observable will be subscribed and emitting data. + share(), + ); + + // Keeps the input from the device observable alive (multicast observable) + const notif = notifyObservable.subscribe(); + + transport = new BleTransport( + device, + writableWithResponseCharacteristic, + writableWithoutResponseCharacteristic, + notifyObservable, + deviceModel, + { + context, + rxjsScheduler, + }, + ); + tracer.trace('New BleTransport created'); + + // Keeping it as a comment for now but if no new bluetooth issues occur, we will be able to remove it + // await transport.requestConnectionPriority("High"); + + // eslint-disable-next-line prefer-const + // let disconnectedSub: Subscription; + + // Callbacks on `react-native-ble-plx` notifying the device has been disconnected + transport.onDisconnect = (error?: Error) => { + transport.isConnected = false; + transport.notYetDisconnected = false; + notif.unsubscribe(); + // disconnectedSub?.remove(); + + clearDisconnectTimeout(transport.id); + delete transportsCache[transport.id]; + tracer.trace( + `On device disconnected callback: cleared cached transport for ${transport.id}, + emitting Transport event "disconnect. Error: ${error}"`, + { reason: error }, + ); + transport.emit('disconnect', error); + transport.disconnectCallback?.(); + }; + + // eslint-disable-next-line require-atomic-updates + transportsCache[transport.id] = transport; + const beforeMTUTime = Date.now(); + + /* disconnectedSub = device.onDisconnected((e) => { + if (!transport.notYetDisconnected) return; + onDisconnect(e); + }); */ + + try { + await transport.inferMTU(); + } finally { + const afterMTUTime = Date.now(); + + if (reconnectionConfig) { + // Refer to ledgerjs archived repo issue #279. + // All HW .v1 LNX have a bug that prevents us from communicating with the device right after pairing. + // When we connect for the first time we issue a disconnect and reconnect, this guarantees that we are + // in a good state. This is avoidable in some key scenarios ↓ + if (afterMTUTime - beforeMTUTime < reconnectionConfig.pairingThreshold) { + needsReconnect = false; + } else if (deviceModel.id === DeviceModelId.stax) { + tracer.trace('Skipping "needsReconnect" strategy for Stax'); + needsReconnect = false; + } + + if (needsReconnect) { + tracer.trace('Device needs reconnection. Triggering a disconnect'); + await BleTransport.disconnectDevice(transport.id, transport.onDisconnect); + await delay(reconnectionConfig.delayAfterFirstPairing); + } + } else { + needsReconnect = false; + } + } + + if (needsReconnect) { + tracer.trace('Reconnecting'); + return open(device, false, timeoutMs, context); + } + + return transport; +} diff --git a/src/lib/ledger-hw-transport-ble/awaitsBleOn.ts b/src/lib/ledger-hw-transport-ble/awaitsBleOn.ts new file mode 100644 index 00000000..084a8afa --- /dev/null +++ b/src/lib/ledger-hw-transport-ble/awaitsBleOn.ts @@ -0,0 +1,12 @@ +import { BluetoothLe } from '@capacitor-community/bluetooth-le'; +import { BluetoothRequired } from '@ledgerhq/errors'; + +export async function awaitsBleOn(): Promise { + await BluetoothLe.initialize(); + const isEnabled = await BluetoothLe.isEnabled(); + if (!isEnabled) { + throw new BluetoothRequired('', { + state: 'disable', + }); + } +} diff --git a/src/lib/ledger-hw-transport-ble/monitorCharacteristic.ts b/src/lib/ledger-hw-transport-ble/monitorCharacteristic.ts new file mode 100644 index 00000000..5a1ec5aa --- /dev/null +++ b/src/lib/ledger-hw-transport-ble/monitorCharacteristic.ts @@ -0,0 +1,35 @@ +import { BleClient } from '@capacitor-community/bluetooth-le'; +import type { TraceContext } from '@ledgerhq/logs'; +import { LocalTracer } from '@ledgerhq/logs'; +import { Observable } from 'rxjs'; + +import type { Characteristic } from './types'; + +const LOG_TYPE = 'ble-verbose'; + +export const monitorCharacteristic = ( + deviceId: string, + serviceId: string, + characteristic: Characteristic, + context?: TraceContext, +): Observable => new Observable((o) => { + const tracer = new LocalTracer(LOG_TYPE, context); + tracer.trace('Start monitoring BLE characteristics', { + characteristicUuid: characteristic.uuid, + }); + + void BleClient.startNotifications( + deviceId, + serviceId, + characteristic.uuid, + (value) => { + const uint8Array = new Uint8Array(value.buffer); + const buffer = Buffer.from(uint8Array); + o.next(buffer); + }, + ); + + return () => { + void BleClient.stopEnabledNotifications(); + }; +}); diff --git a/src/lib/ledger-hw-transport-ble/remapErrors.ts b/src/lib/ledger-hw-transport-ble/remapErrors.ts new file mode 100644 index 00000000..d14a20a3 --- /dev/null +++ b/src/lib/ledger-hw-transport-ble/remapErrors.ts @@ -0,0 +1,22 @@ +import { + DisconnectedDevice, +} from '@ledgerhq/errors'; + +export type IOBleErrorRemap = Error | null | undefined; + +export const remapError = (error: IOBleErrorRemap): IOBleErrorRemap => { + if (!error || !error.message) return error; + + if (error.message.includes('was disconnected') || error.message.includes('not found')) { + return new DisconnectedDevice(); + } + + return error; +}; + +export const rethrowError = (e: Error | null | undefined): never => { + // throw remapError(e); + throw e ?? new Error(); +}; + +export const decoratePromiseErrors =
(promise: Promise): Promise => promise.catch(rethrowError); diff --git a/src/lib/ledger-hw-transport-ble/types.ts b/src/lib/ledger-hw-transport-ble/types.ts new file mode 100644 index 00000000..630878f5 --- /dev/null +++ b/src/lib/ledger-hw-transport-ble/types.ts @@ -0,0 +1,9 @@ +import type { BleDevice } from '@capacitor-community/bluetooth-le'; + +export type Device = BleDevice; +export type Characteristic = any; + +export type ReconnectionConfig = { + pairingThreshold: number; + delayAfterFirstPairing: number; +}; diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss index 0f0894b5..5d800817 100644 --- a/src/styles/_variables.scss +++ b/src/styles/_variables.scss @@ -122,6 +122,7 @@ --color-interactive-drop-down-menu-active: #EFF2F6; --color-tint: rgba(12, 12, 12, 0.4); + --color-tint-lock: rgba(230, 236, 243, 0.4); --color-card-text: #FFFFFF; --color-card-text-hover: rgba(255, 255, 255, 0.7); @@ -181,6 +182,8 @@ --border-radius-card: 1.25rem; --border-radius-big: 1.5rem; + --blur-app-locked: 4rem; + --scrollbar-width: 0; --safe-area-bottom-value: var(--safe-area-bottom); --safe-area-top-value: var(--safe-area-top); @@ -197,6 +200,7 @@ --z-notification: 250; --z-tooltip: 300; --z-confetti: 400; + --z-autolock: 500; --no-animation-transition: 200ms opacity ease; --layer-transition: 300ms cubic-bezier(0.33, 1, 0.68, 1); @@ -320,6 +324,7 @@ --color-interactive-drop-down-menu-active: #202833; --color-tint: rgba(12, 12, 12, 0.6); + --color-tint-lock: rgba(20, 26, 33, 0.6); --color-add-wallet-text: #CBD5E3; --color-add-wallet-text-hover: #E1EEFF; diff --git a/src/util/deeplink/index.ts b/src/util/deeplink/index.ts index d2d69f32..52d2d81e 100644 --- a/src/util/deeplink/index.ts +++ b/src/util/deeplink/index.ts @@ -163,9 +163,11 @@ export function parseTonDeeplink(value: string | unknown) { } try { - const url = new URL(value); + // In some browsers URL module may handle non-standard protocols incorrectly + const adaptedDeeplink = value.replace(TON_PROTOCOL, 'https://'); + const url = new URL(adaptedDeeplink); - const toAddress = url.pathname.replace(/.*\//, ''); + const toAddress = url.pathname.replace(/\//g, ''); const amount = getDeeplinkSearchParam(url, 'amount'); const comment = getDeeplinkSearchParam(url, 'text'); const binPayload = getDeeplinkSearchParam(url, 'bin'); @@ -303,7 +305,7 @@ export function processSelfDeeplink(deeplink: string) { let tonDeeplink = deeplink; SELF_UNIVERSAL_URLS.forEach((prefix) => { if (tonDeeplink.startsWith(prefix)) { - tonDeeplink = tonDeeplink.replace(prefix, TON_PROTOCOL); + tonDeeplink = tonDeeplink.replace(`${prefix}/`, TON_PROTOCOL); } }); diff --git a/src/util/fetch.ts b/src/util/fetch.ts index bc1914df..1479c7b6 100644 --- a/src/util/fetch.ts +++ b/src/util/fetch.ts @@ -70,7 +70,7 @@ export async function fetchWithRetry(url: string | URL, init?: RequestInit, opti statusCode = response.status; if (statusCode >= 400) { - const { error } = await response.json().catch(() => undefined); + const { error } = await response.json().catch(() => {}); throw new Error(error ?? `HTTP Error ${statusCode}`); } diff --git a/src/util/ledger/bleConnector.ts b/src/util/ledger/bleConnector.ts new file mode 100644 index 00000000..28411cf0 --- /dev/null +++ b/src/util/ledger/bleConnector.ts @@ -0,0 +1,141 @@ +import { BleClient } from '@capacitor-community/bluetooth-le'; +import type { Subscription as TransportSubscription } from '@ledgerhq/hw-transport'; +import { v4 as uuid } from 'uuid'; +import type { BleDevice } from '@capacitor-community/bluetooth-le/dist/esm/definitions'; + +import { IS_CAPACITOR } from '../../config'; +import BleTransport from '../../lib/ledger-hw-transport-ble/BleTransport'; + +interface ScannedDevice { + identifier: string; + device: BleDevice; +} + +export interface LedgerConnection { + device: BleDevice; + bleTransport: BleTransport; +} + +let listeningSubscription: TransportSubscription | undefined; + +let scannedDevices: ScannedDevice[] = []; +let pairedDevice: LedgerConnection | undefined; +let onLedgerConnected: ((connection: LedgerConnection) => void) | undefined; + +function isConnecting() { + return !!listeningSubscription; +} + +function scannedDeviceIsValidYet(scannedDevice: ScannedDevice): boolean { + if (!scannedDevices.find((it) => it.identifier === scannedDevice.identifier)) { + // List is already cleared + return false; + } + + // A device is already paired + return !pairedDevice; +} + +async function tryConnectingLedgerDevice(scannedDevice: ScannedDevice) { + try { + // Check if stopped before retry + if (!scannedDeviceIsValidYet(scannedDevice)) return; + + const bleTransport = await BleTransport.open(scannedDevice.device); + // Check if stopped before connection establish + if (!scannedDeviceIsValidYet(scannedDevice)) return; + + const ledgerConnection = { + device: scannedDevice.device, + bleTransport, + }; + pairedDevice = ledgerConnection; + + bleTransport.disconnectCallback = () => { + pairedDevice = undefined; + if (isConnecting()) { + stop(); + start(); + } + }; + + setTimeout(() => { + // Make sure not disconnected yet + if (pairedDevice?.device.deviceId === ledgerConnection.device.deviceId) { + onLedgerConnected?.(ledgerConnection); + stop(); + } else if (isConnecting()) { + // Unexpectedly, disconnected before calling the callback, restart! + pairedDevice = undefined; + stop(); + void start(); + } + }, 1000); + } catch (error) { + setTimeout(() => { + tryConnectingLedgerDevice(scannedDevice); + }, 10000); + } +} + +async function isSupported() { + if (!IS_CAPACITOR) return false; + + try { + await BleClient.initialize({ + androidNeverForLocation: true, + }); + await BleClient.requestEnable(); + } catch (error) { /* empty */ } + + return BleClient.isEnabled(); +} + +function start() { + listeningSubscription = BleTransport.listen({ + next: (event: { type: string; device?: BleDevice }) => { + switch (event.type) { + case 'add': + if (event.device) { + if (!event.device.name) return; + if (scannedDevices.find((it) => it.device.deviceId === event.device?.deviceId)) return; + const scannedDevice = { identifier: uuid(), device: event.device }; + scannedDevices.push(scannedDevice); + void tryConnectingLedgerDevice(scannedDevice); + } + break; + } + }, + error: () => { + stop(); + }, + complete: () => { + stop(); + }, + }); +} + +function stop() { + scannedDevices = []; + listeningSubscription?.unsubscribe(); + listeningSubscription = undefined; +} + +function connect(): Promise { + return new Promise((resolve) => { + onLedgerConnected = resolve; + if (pairedDevice) { + onLedgerConnected(pairedDevice); + return; + } + + if (isConnecting()) return; + start(); + }); +} + +export const BleConnector = { + isSupported, + connect, + stop, +}; diff --git a/src/util/ledger/index.ts b/src/util/ledger/index.ts index 7e9e960a..71566864 100644 --- a/src/util/ledger/index.ts +++ b/src/util/ledger/index.ts @@ -5,26 +5,29 @@ import type { StateInit } from '@ton/core'; import { loadStateInit } from '@ton/core'; import type { TonPayloadFormat } from '@ton-community/ton-ledger'; import { KNOWN_JETTONS, parseMessage, TonTransport } from '@ton-community/ton-ledger'; +import type { HIDTransport } from 'mtw-capacitor-usb-hid'; import { Address } from '@ton/core/dist/address/Address'; import { Builder } from '@ton/core/dist/boc/Builder'; import { Cell } from '@ton/core/dist/boc/Cell'; import { SendMode } from '@ton/core/dist/types/SendMode'; -import type { Workchain } from '../../api/chains/ton/constants'; +import type { Workchain } from '../../api/chains/ton'; import type { ApiSubmitTransferOptions } from '../../api/methods/types'; import type { ApiTonConnectProof } from '../../api/tonConnect/types'; import type { ApiDappTransfer, ApiLocalTransactionParams, - ApiNetwork, ApiNft, + ApiNetwork, + ApiNft, ApiSignedTransfer, ApiStakingType, } from '../../api/types'; -import type { LedgerWalletInfo } from './types'; +import type BleTransport from '../../lib/ledger-hw-transport-ble/BleTransport'; +import type { LedgerTransport, LedgerWalletInfo } from './types'; import { ApiLiquidUnstakeMode, ApiTransactionError } from '../../api/types'; import { - BURN_ADDRESS, LIQUID_JETTON, LIQUID_POOL, + BURN_ADDRESS, IS_CAPACITOR, LIQUID_JETTON, LIQUID_POOL, NOTCOIN_EXCHANGERS, NOTCOIN_VOUCHERS_ADDRESS, ONE_TON, TONCOIN, } from '../../config'; import { callApi } from '../../api'; @@ -55,7 +58,12 @@ import { parseAccountId } from '../account'; import compareVersions from '../compareVersions'; import { logDebugError } from '../logs'; import { pause } from '../schedulers'; -import { isValidLedgerComment } from './utils'; +import { IS_ANDROID_APP } from '../windowEnvironment'; +import { isLedgerConnectionBroken, isValidLedgerComment } from './utils'; + +type BleConnectorClass = typeof import('./bleConnector').BleConnector; +type HIDTransportClass = typeof import('mtw-capacitor-usb-hid/dist/esm').HIDTransport; +type ListLedgerDevicesFunction = typeof import('mtw-capacitor-usb-hid/dist/esm').listLedgerDevices; type TransactionParams = { to: Address; @@ -93,8 +101,70 @@ const knownJettonAddresses = KNOWN_JETTONS.map( ({ masterAddress }) => masterAddress.toString({ bounceable: true, urlSafe: true }), ); -let transport: TransportWebHID | TransportWebUSB | undefined; +let transport: TransportWebHID | TransportWebUSB | BleTransport | HIDTransport | undefined; let tonTransport: TonTransport | undefined; +let isHidSupported = false; +let isWebUsbSupported = false; +let isBluetoothSupported = false; +let currentLedgerTransport: LedgerTransport | undefined; + +let hidImportPromise: Promise<{ + transport: HIDTransportClass; + listLedgerDevices: ListLedgerDevicesFunction; +}>; +let bleImportPromise: Promise; +let BleConnector: BleConnectorClass; +let MtwHidTransport: HIDTransportClass; +let listLedgerDevices: ListLedgerDevicesFunction; + +async function ensureBleConnector() { + if (!IS_CAPACITOR) return undefined; + + if (!bleImportPromise) { + bleImportPromise = import('./bleConnector').then((module) => { + return module.BleConnector; + }); + BleConnector = await bleImportPromise; + } + + return bleImportPromise; +} + +async function ensureHidTransport() { + if (!IS_ANDROID_APP) return undefined; + + if (!hidImportPromise) { + hidImportPromise = import('mtw-capacitor-usb-hid/dist/esm').then((module) => { + return { + transport: module.HIDTransport, + listLedgerDevices: module.listLedgerDevices, + }; + }); + const result = await hidImportPromise; + MtwHidTransport = result.transport; + listLedgerDevices = result.listLedgerDevices; + } + + return hidImportPromise; +} + +void ensureBleConnector(); +void ensureHidTransport(); + +export async function detectAvailableTransports() { + await ensureBleConnector(); + await ensureHidTransport(); + [isHidSupported, isBluetoothSupported, isWebUsbSupported] = await Promise.all([ + IS_ANDROID_APP ? MtwHidTransport.isSupported() : TransportWebHID.isSupported(), + BleConnector ? BleConnector.isSupported() : Promise.resolve(false), + TransportWebUSB.isSupported(), + ]); + + return { + isUsbAvailable: isHidSupported || isWebUsbSupported, + isBluetoothAvailable: isBluetoothSupported, + }; +} function getInternalWalletVersion(version: PossibleWalletVersion) { return LedgerWalletVersion[version]; @@ -107,26 +177,51 @@ export async function importLedgerWallet(network: ApiNetwork, accountIndex: numb export async function reconnectLedger() { try { - if (tonTransport && await tonTransport?.isAppOpen()) { + if (await tonTransport?.isAppOpen()) { return true; } } catch { - // do nothing + // Do nothing } - return await connectLedger() && await waitLedgerTonApp(); + const isLedgerConnected = await connectLedger(); + if (!isLedgerConnected) return false; + + try { + return await waitLedgerTonApp(); + } catch (err: any) { + if (isLedgerConnectionBroken(err.name)) { + return reconnectLedger(); + } + + throw err; + } } -export async function connectLedger() { +export async function connectLedger(preferredTransport?: LedgerTransport) { + if (preferredTransport) currentLedgerTransport = preferredTransport; + try { - if (await TransportWebHID.isSupported()) { - transport = await connectHID(); - } else if (await TransportWebUSB.isSupported()) { - transport = await connectUSB(); - } else { - logDebugError('connectLedger: HID and/or USB are not supported'); + switch (currentLedgerTransport) { + case 'bluetooth': + transport = await connectBLE(); + break; + + case 'usb': + default: + if (isHidSupported) { + transport = await connectHID(); + } else if (isWebUsbSupported) { + transport = await connectWebUsb(); + } + break; + } + + if (!transport) { + logDebugError('connectLedger: BLE and/or HID are not supported'); return false; } + tonTransport = new TonTransport(transport); return true; } catch (err) { @@ -146,18 +241,24 @@ function waitLedgerTonAppDeadline(): Promise { export async function checkTonApp() { for (let i = 0; i < ATTEMPTS; i++) { try { - const isTonOpen = await tonTransport!.isAppOpen(); + const isTonOpen = await tonTransport?.isAppOpen(); if (isTonOpen) { - // Workaround for Ledger S, this is a way to check if it is unlocked. - // There will be an error with code 0x530c - await tonTransport?.getAddress(getLedgerAccountPathByIndex(0), { - walletVersion: LedgerWalletVersion[DEFAULT_WALLET_VERSION], - }); + if (transport?.deviceModel?.id.startsWith('nanoS')) { + // Workaround for Ledger Nano S or Nano S Plus, this is a way to check if it is unlocked. + // There will be an error with code 0x530c. + await tonTransport?.getAddress(getLedgerAccountPathByIndex(0), { + walletVersion: LedgerWalletVersion[DEFAULT_WALLET_VERSION], + }); + } return true; } } catch (err: any) { + if (isLedgerConnectionBroken(err.name)) { + tonTransport = undefined; + throw err; + } if (!err?.message.includes('0x530c')) { logDebugError('waitLedgerTonApp', err); } @@ -176,7 +277,15 @@ export function waitLedgerTonApp() { ]); } -async function connectHID() { +function connectHID() { + if (IS_ANDROID_APP) { + return connectCapacitorHID(); + } + + return connectWebHID(); +} + +async function connectWebHID() { for (let i = 0; i < ATTEMPTS; i++) { const [device] = await TransportWebHID.list(); @@ -196,7 +305,7 @@ async function connectHID() { throw new Error('Failed to connect'); } -async function connectUSB() { +async function connectWebUsb() { for (let i = 0; i < ATTEMPTS; i++) { const [device] = await TransportWebUSB.list(); @@ -216,6 +325,30 @@ async function connectUSB() { throw new Error('Failed to connect'); } +async function connectCapacitorHID(): Promise { + for (let i = 0; i < ATTEMPTS; i++) { + const [device] = await listLedgerDevices(); + + if (!device) { + await pause(PAUSE); + continue; + } + + return MtwHidTransport.open(device); + } + + throw new Error('Failed to connect'); +} + +async function connectBLE(): Promise { + if (!BleConnector) { + throw new Error('BLE is not supported on this device.'); + } + + const connection = await BleConnector.connect(); + return connection.bleTransport; +} + export async function submitLedgerStake( accountId: string, amount: bigint, diff --git a/src/util/ledger/types.ts b/src/util/ledger/types.ts index 5591d360..96d2ebcd 100644 --- a/src/util/ledger/types.ts +++ b/src/util/ledger/types.ts @@ -12,3 +12,5 @@ export interface LedgerWalletInfo { deviceId?: string; deviceName?: string; } + +export type LedgerTransport = 'usb' | 'bluetooth'; diff --git a/src/util/ledger/utils.ts b/src/util/ledger/utils.ts index 29083237..34f95c78 100644 --- a/src/util/ledger/utils.ts +++ b/src/util/ledger/utils.ts @@ -1,6 +1,7 @@ import { isAscii } from '../stringFormat'; const MAX_COMMENT_SIZE = 120; +const BROKEN_CONNECTION_ERRORS = new Set(['DisconnectedDeviceDuringOperation', 'TransportRaceCondition']); export function isValidLedgerComment(comment: string) { return isAscii(comment) && isLedgerCommentLengthValid(comment); @@ -9,3 +10,7 @@ export function isValidLedgerComment(comment: string) { export function isLedgerCommentLengthValid(comment: string) { return comment.length <= MAX_COMMENT_SIZE; } + +export function isLedgerConnectionBroken(error: string) { + return BROKEN_CONNECTION_ERRORS.has(error); +} diff --git a/src/util/url.ts b/src/util/url.ts index b42cfba0..b3d4f07b 100644 --- a/src/util/url.ts +++ b/src/util/url.ts @@ -7,16 +7,18 @@ import { logDebugError } from './logs'; // Regexp from https://stackoverflow.com/a/3809435 const URL_REGEX = /[-a-z0-9@:%._+~#=]{1,256}\.[a-z0-9()]{1,6}\b([-a-z0-9()@:%_+.~#?&/=]*)/gi; const VALID_PROTOCOLS = new Set(['http:', 'https:']); + +// The configuration does not contain data for NFT addresses, they must be configured separately const EXPLORER_CONFIGURATIONS = { ton: { - name: 'Tonviewer', + name: 'Tonscan', base: { - mainnet: 'https://tonviewer.com/', - testnet: 'https://testnet.tonviewer.com/', + mainnet: 'https://tonscan.org/', + testnet: 'https://testnet.tonscan.org/', }, - address: '{base}{address}', - explorer: '{base}{address}?section=jetton', - transaction: '{base}transaction/{hash}', + address: '{base}address/{address}', + explorer: '{base}jetton/{address}', + transaction: '{base}tx/{hash}', }, tron: { name: 'Tronscan', @@ -90,13 +92,13 @@ export function getExplorerAddressUrl(chain: ApiChain, address?: string, isTestn export function getExplorerNftCollectionUrl(nftCollectionAddress?: string, isTestnet?: boolean) { if (!nftCollectionAddress) return undefined; - return `${getExplorerBaseUrl('ton', isTestnet)}${nftCollectionAddress}?section=overview`; + return `${getExplorerBaseUrl('ton', isTestnet)}nft/${nftCollectionAddress}`; } export function getExplorerNftUrl(nftAddress?: string, isTestnet?: boolean) { if (!nftAddress) return undefined; - return `${getExplorerBaseUrl('ton', isTestnet)}${nftAddress}?section=nft`; + return `${getExplorerBaseUrl('ton', isTestnet)}nft/${nftAddress}`; } export function getExplorerTokenUrl(chain: ApiChain, slug?: string, address?: string, isTestnet?: boolean) { diff --git a/src/util/windowEnvironment.ts b/src/util/windowEnvironment.ts index 3ba53aba..15c7287a 100644 --- a/src/util/windowEnvironment.ts +++ b/src/util/windowEnvironment.ts @@ -44,7 +44,7 @@ export const IS_WEB = !IS_CAPACITOR && !IS_ELECTRON && !IS_EXTENSION; export const DEFAULT_LANG_CODE = 'en'; export const USER_AGENT_LANG_CODE = getBrowserLanguage(); export const DPR = window.devicePixelRatio || 1; -export const IS_LEDGER_SUPPORTED = !(IS_IOS || (IS_ANDROID && IS_CAPACITOR) || IS_FIREFOX_EXTENSION); +export const IS_LEDGER_SUPPORTED = IS_CAPACITOR || !(IS_IOS || IS_FIREFOX_EXTENSION); export const IS_LEDGER_EXTENSION_TAB = global.location.hash.startsWith('#detached'); // Disable biometric auth on electron for now until this issue is fixed: // https://github.com/electron/electron/issues/24573