diff --git a/.github/workflows/macstadium-tests.yml b/.github/workflows/macstadium-tests.yml index 50cafb7e167..b458a442609 100644 --- a/.github/workflows/macstadium-tests.yml +++ b/.github/workflows/macstadium-tests.yml @@ -64,8 +64,16 @@ jobs: # Job for linting and unit tests linting-and-unit-tests: runs-on: ["self-hosted"] + permissions: + contents: read needs: install-deps steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up github keys + run: git config core.sshCommand "ssh -i ~/.ssh/id_ed25519 -F /dev/null" + - name: Set sanitized branch name id: sanitize run: echo "SANITIZED_REF_NAME=${GITHUB_REF_NAME//\//-}" >> $GITHUB_ENV @@ -74,6 +82,17 @@ jobs: with: name: yarn-cache-${{ env.SANITIZED_REF_NAME }} path: .yarn + - name: Set up ENV vars & scripts + env: + CI_SCRIPTS: ${{ secrets.CI_SCRIPTS }} + run: | + source ~/.zshrc + git clone git@github.com:rainbow-me/rainbow-env.git + mv rainbow-env/dotenv .env && rm -rf rainbow-env + eval $CI_SCRIPTS + - name: Install dependencies + run: | + yarn install && yarn setup - name: Check for frozen lockfile run: ./scripts/check-lockfile.sh @@ -93,9 +112,20 @@ jobs: # iOS build and e2e tests e2e-ios: runs-on: ["self-hosted"] - timeout-minutes: 60 + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + permissions: + contents: read + timeout-minutes: 90 needs: install-deps steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up github keys + run: git config core.sshCommand "ssh -i ~/.ssh/id_ed25519 -F /dev/null" + - name: Set sanitized branch name id: sanitize run: echo "SANITIZED_REF_NAME=${GITHUB_REF_NAME//\//-}" >> $GITHUB_ENV @@ -104,6 +134,17 @@ jobs: with: name: yarn-cache-${{ env.SANITIZED_REF_NAME }} path: .yarn + - name: Set up ENV vars & scripts + env: + CI_SCRIPTS: ${{ secrets.CI_SCRIPTS }} + run: | + source ~/.zshrc + git clone git@github.com:rainbow-me/rainbow-env.git + mv rainbow-env/dotenv .env && rm -rf rainbow-env + eval $CI_SCRIPTS + - name: Install dependencies + run: | + yarn install && yarn setup - name: Rebuild detox cache run: ./node_modules/.bin/detox clean-framework-cache && ./node_modules/.bin/detox build-framework-cache @@ -129,4 +170,4 @@ jobs: - name: Detox iOS e2e tests run: | - ./scripts/run-retry-tests.sh 3 \ No newline at end of file + ./scripts/run-retry-tests.sh 3 diff --git a/ios/Gemfile b/ios/Gemfile index 56d9ef00e81..683d9eeadb8 100644 --- a/ios/Gemfile +++ b/ios/Gemfile @@ -6,7 +6,7 @@ ruby '>= 2.7.0' gem "fastlane" # Exclude problematic versions of cocoapods and activesupport that causes build failures. gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1' -gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0' +gem 'activesupport', '>= 6.1.7.5', '<7.1.0' plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/ios/Gemfile.lock b/ios/Gemfile.lock index db0f6145d07..4ede621f5a6 100644 --- a/ios/Gemfile.lock +++ b/ios/Gemfile.lock @@ -5,7 +5,7 @@ GEM base64 nkf rexml - activesupport (7.0.5.1) + activesupport (7.0.8.6) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) @@ -18,28 +18,28 @@ GEM artifactory (3.0.17) atomos (0.1.3) aws-eventstream (1.3.0) - aws-partitions (1.969.0) - aws-sdk-core (3.202.0) + aws-partitions (1.998.0) + aws-sdk-core (3.211.0) aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) + aws-partitions (~> 1, >= 1.992.0) aws-sigv4 (~> 1.9) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.88.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-kms (1.95.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.159.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-s3 (1.169.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) - aws-sigv4 (1.9.1) + aws-sigv4 (1.10.1) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) base64 (0.2.0) claide (1.1.0) - cocoapods (1.14.3) + cocoapods (1.16.1) addressable (~> 2.8) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.14.3) + cocoapods-core (= 1.16.1) cocoapods-deintegrate (>= 1.0.3, < 2.0) cocoapods-downloader (>= 2.1, < 3.0) cocoapods-plugins (>= 1.0.0, < 2.0) @@ -53,8 +53,8 @@ GEM molinillo (~> 0.8.0) nap (~> 1.0) ruby-macho (>= 2.3.0, < 3.0) - xcodeproj (>= 1.23.0, < 2.0) - cocoapods-core (1.14.3) + xcodeproj (>= 1.26.0, < 2.0) + cocoapods-core (1.16.1) activesupport (>= 5.0, < 8) addressable (~> 2.8) algoliasearch (~> 1.0) @@ -77,7 +77,7 @@ GEM colored2 (3.1.2) commander (4.6.0) highline (~> 2.0.0) - concurrent-ruby (1.2.2) + concurrent-ruby (1.3.4) declarative (0.0.20) digest-crc (0.6.5) rake (>= 12.0.0, < 14.0.0) @@ -87,8 +87,8 @@ GEM escape (0.0.4) ethon (0.16.0) ffi (>= 1.15.0) - excon (0.111.0) - faraday (1.10.3) + excon (0.112.0) + faraday (1.10.4) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -114,10 +114,10 @@ GEM faraday-patron (1.0.0) faraday-rack (1.0.0) faraday-retry (1.0.3) - faraday_middleware (1.2.0) + faraday_middleware (1.2.1) faraday (~> 1.0) fastimage (2.3.1) - fastlane (2.222.0) + fastlane (2.225.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -133,6 +133,7 @@ GEM faraday-cookie_jar (~> 0.0.6) faraday_middleware (~> 1.0) fastimage (>= 2.1.0, < 3.0.0) + fastlane-sirp (>= 1.0.0) gh_inspector (>= 1.1.2, < 2.0.0) google-apis-androidpublisher_v3 (~> 0.3) google-apis-playcustomapp_v1 (~> 0.1) @@ -158,7 +159,9 @@ GEM xcodeproj (>= 1.13.0, < 2.0.0) xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) - ffi (1.16.3) + fastlane-sirp (1.0.0) + sysrandom (~> 1.0) + ffi (1.17.0) fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) @@ -202,19 +205,19 @@ GEM http-cookie (1.0.7) domain_name (~> 0.5) httpclient (2.8.3) - i18n (1.14.1) + i18n (1.14.6) concurrent-ruby (~> 1.0) jmespath (1.6.2) - json (2.7.2) - jwt (2.8.2) + json (2.7.4) + jwt (2.9.3) base64 mini_magick (4.13.2) mini_mime (1.1.5) - minitest (5.18.1) + minitest (5.25.1) molinillo (0.8.0) multi_json (1.15.0) multipart-post (2.4.1) - nanaimo (0.3.0) + nanaimo (0.4.0) nap (1.1.0) naturally (2.2.1) netrc (0.11.0) @@ -229,8 +232,7 @@ GEM trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.3.6) - strscan + rexml (3.3.9) rouge (2.0.7) ruby-macho (2.5.1) ruby2_keywords (0.0.5) @@ -244,7 +246,7 @@ GEM simctl (1.6.10) CFPropertyList naturally - strscan (3.1.0) + sysrandom (1.0.5) terminal-notifier (2.0.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) @@ -258,15 +260,15 @@ GEM tzinfo (2.0.6) concurrent-ruby (~> 1.0) uber (0.1.0) - unicode-display_width (2.5.0) + unicode-display_width (2.6.0) word_wrap (1.0.0) - xcodeproj (1.25.0) + xcodeproj (1.26.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) - nanaimo (~> 0.3.0) - rexml (>= 3.3.2, < 4.0) + nanaimo (~> 0.4.0) + rexml (>= 3.3.6, < 4.0) xcpretty (0.3.0) rouge (~> 2.0.7) xcpretty-travis-formatter (1.0.1) @@ -276,7 +278,7 @@ PLATFORMS ruby DEPENDENCIES - activesupport (>= 6.1.7.5, != 7.1.0) + activesupport (>= 6.1.7.5, < 7.1.0) cocoapods (>= 1.13, != 1.15.1, != 1.15.0) fastlane diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 10d554bc320..af7eb5f5407 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -2525,7 +2525,7 @@ SPEC CHECKSUMS: TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654 ToolTipMenu: 8ac61aded0fbc4acfe7e84a7d0c9479d15a9a382 VisionCamera: 2af28201c3de77245f8c58b7a5274d5979df70df - Yoga: 88480008ccacea6301ff7bf58726e27a72931c8d + Yoga: 04f1db30bb810187397fa4c37dd1868a27af229c PODFILE CHECKSUM: 98c3fc206d7041ac7388693bb0753109d1884b57 diff --git a/ios/Rainbow.xcodeproj/project.pbxproj b/ios/Rainbow.xcodeproj/project.pbxproj index 5428d264f73..2f856e3579c 100644 --- a/ios/Rainbow.xcodeproj/project.pbxproj +++ b/ios/Rainbow.xcodeproj/project.pbxproj @@ -34,6 +34,9 @@ 66A1FEB624AB641100C3F539 /* RNCMScreen.m in Sources */ = {isa = PBXBuildFile; fileRef = 66A1FEB324AB641100C3F539 /* RNCMScreen.m */; }; 66A1FEBC24ACBBE600C3F539 /* RNCMPortal.m in Sources */ = {isa = PBXBuildFile; fileRef = 66A1FEBB24ACBBE600C3F539 /* RNCMPortal.m */; }; 66A28EB024CAF1B500410A88 /* TestFlight.m in Sources */ = {isa = PBXBuildFile; fileRef = 66A28EAF24CAF1B500410A88 /* TestFlight.m */; }; + 7A797FC31B46D54B061D99E5 /* libPods-SelectTokenIntent.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AB8606AB30216E96A75779E /* libPods-SelectTokenIntent.a */; }; + 8AB133C6A6757CF6A2AA5009 /* libPods-ImageNotification.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 33486EF423B2585D9BBA09F4 /* libPods-ImageNotification.a */; }; + 91C397E1FD7D8F76707B8740 /* libPods-PriceWidgetExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D8779D7AB4A3E9DD8487D527 /* libPods-PriceWidgetExtension.a */; }; A4277D9F23CBD1910042BAF4 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4277D9E23CBD1910042BAF4 /* Extensions.swift */; }; A4277DA323CFE85F0042BAF4 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4277DA223CFE85F0042BAF4 /* Theme.swift */; }; A4D04BA923D12F99008C1DEC /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4D04BA823D12F99008C1DEC /* Button.swift */; }; @@ -137,6 +140,7 @@ C9B378BE2C515A860085E5D0 /* Base in Resources */ = {isa = PBXBuildFile; fileRef = C9B378BD2C515A860085E5D0 /* Base */; }; C9B378C22C515A860085E5D0 /* ShareWithRainbow.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = C9B378B82C515A860085E5D0 /* ShareWithRainbow.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; E8D2945956C9619768A8D361 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C92E589571C27FFE89AB10 /* ExpoModulesProvider.swift */; }; + E9A0F69C0DB42A80F16CDBD5 /* libPods-Rainbow.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C0DE5565B77A45B108F28413 /* libPods-Rainbow.a */; }; ED2971652150620600B7C4FE /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED2971642150620600B7C4FE /* JavaScriptCore.framework */; }; /* End PBXBuildFile section */ @@ -230,6 +234,7 @@ 15E531D4242B28EF00797B89 /* UIImageViewWithPersistentAnimations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImageViewWithPersistentAnimations.swift; sourceTree = ""; }; 15E531D8242DAB7100797B89 /* NotificationManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NotificationManager.h; sourceTree = ""; }; 15E531D9242DAB7100797B89 /* NotificationManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NotificationManager.m; sourceTree = ""; }; + 208F04ABC0ACD249A8EDFA1A /* Pods-ImageNotification.staging.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageNotification.staging.xcconfig"; path = "Target Support Files/Pods-ImageNotification/Pods-ImageNotification.staging.xcconfig"; sourceTree = ""; }; 24979E3620F84003007EB0DA /* Protobuf.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Protobuf.framework; path = Frameworks/Protobuf.framework; sourceTree = ""; }; 24979E7420F84004007EB0DA /* FirebaseAnalytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FirebaseAnalytics.framework; path = Frameworks/FirebaseAnalytics.framework; sourceTree = ""; }; 24979E7520F84004007EB0DA /* FirebaseCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FirebaseCore.framework; path = Frameworks/FirebaseCore.framework; sourceTree = ""; }; @@ -242,11 +247,16 @@ 24979E7C20F84004007EB0DA /* FirebaseCoreDiagnostics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FirebaseCoreDiagnostics.framework; path = Frameworks/FirebaseCoreDiagnostics.framework; sourceTree = ""; }; 24979E7D20F84005007EB0DA /* module.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; name = module.modulemap; path = Frameworks/module.modulemap; sourceTree = ""; }; 24979E7E20F84005007EB0DA /* nanopb.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = nanopb.framework; path = Frameworks/nanopb.framework; sourceTree = ""; }; + 2FA1FCE6D3B56B8902BEFC47 /* Pods-SelectTokenIntent.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SelectTokenIntent.release.xcconfig"; path = "Target Support Files/Pods-SelectTokenIntent/Pods-SelectTokenIntent.release.xcconfig"; sourceTree = ""; }; + 33486EF423B2585D9BBA09F4 /* libPods-ImageNotification.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ImageNotification.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 390E0879CC9C88D0DBF50690 /* Pods-SelectTokenIntent.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SelectTokenIntent.debug.xcconfig"; path = "Target Support Files/Pods-SelectTokenIntent/Pods-SelectTokenIntent.debug.xcconfig"; sourceTree = ""; }; + 3AB8606AB30216E96A75779E /* libPods-SelectTokenIntent.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SelectTokenIntent.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3C379D5D20FD1F92009AF81F /* Rainbow.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Rainbow.entitlements; path = Rainbow/Rainbow.entitlements; sourceTree = ""; }; 3CBE29CB2381E43800BE05AC /* Rainbow-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Rainbow-Bridging-Header.h"; sourceTree = ""; }; 43C92E589571C27FFE89AB10 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-Rainbow/ExpoModulesProvider.swift"; sourceTree = ""; }; 4D098C2D2811A979006A801A /* RNStartTime.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNStartTime.h; sourceTree = ""; }; 4D098C2E2811A9A5006A801A /* RNStartTime.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNStartTime.m; sourceTree = ""; }; + 59D20A56C5C8F6DD51C60C16 /* Pods-Rainbow.localrelease.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Rainbow.localrelease.xcconfig"; path = "Target Support Files/Pods-Rainbow/Pods-Rainbow.localrelease.xcconfig"; sourceTree = ""; }; 6630540824A38A1900E5B030 /* RainbowText.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RainbowText.m; sourceTree = ""; }; 6635730524939991006ACFA6 /* SafeStoreReview.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SafeStoreReview.m; sourceTree = ""; }; 664612EC2748489B00B43F5A /* PriceWidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PriceWidgetExtension.entitlements; sourceTree = ""; }; @@ -262,12 +272,17 @@ 66A1FEBB24ACBBE600C3F539 /* RNCMPortal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNCMPortal.m; path = "../src/react-native-cool-modals/ios/RNCMPortal.m"; sourceTree = ""; }; 66A28EAF24CAF1B500410A88 /* TestFlight.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestFlight.m; sourceTree = ""; }; 66A29CCA2511074500481F4A /* ReaHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaHeader.h; sourceTree = SOURCE_ROOT; }; + 71C4AACC71462B12A43FDDE5 /* Pods-PriceWidgetExtension.localrelease.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PriceWidgetExtension.localrelease.xcconfig"; path = "Target Support Files/Pods-PriceWidgetExtension/Pods-PriceWidgetExtension.localrelease.xcconfig"; sourceTree = ""; }; 98AED33BAB4247CEBEF8464D /* libz.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; 9DEADFA4826D4D0BAA950D21 /* libRNFIRMessaging.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFIRMessaging.a; sourceTree = ""; }; + 9E8BEFB6422BEB906DEB3C7E /* Pods-SelectTokenIntent.staging.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SelectTokenIntent.staging.xcconfig"; path = "Target Support Files/Pods-SelectTokenIntent/Pods-SelectTokenIntent.staging.xcconfig"; sourceTree = ""; }; + A0B1D87220C90A9004D035C7 /* Pods-PriceWidgetExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PriceWidgetExtension.release.xcconfig"; path = "Target Support Files/Pods-PriceWidgetExtension/Pods-PriceWidgetExtension.release.xcconfig"; sourceTree = ""; }; A4277D9E23CBD1910042BAF4 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; A4277DA223CFE85F0042BAF4 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = ""; }; A4D04BA823D12F99008C1DEC /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = ""; }; A4D04BAB23D12FD5008C1DEC /* ButtonManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ButtonManager.m; sourceTree = ""; }; + A5ACEBEDC5DFEC3A33504769 /* Pods-ImageNotification.localrelease.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageNotification.localrelease.xcconfig"; path = "Target Support Files/Pods-ImageNotification/Pods-ImageNotification.localrelease.xcconfig"; sourceTree = ""; }; + A6402205793DFA897132D7A9 /* Pods-ImageNotification.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageNotification.release.xcconfig"; path = "Target Support Files/Pods-ImageNotification/Pods-ImageNotification.release.xcconfig"; sourceTree = ""; }; AA0B1CB82B00C5E100EAF77D /* SF-Mono-Semibold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "SF-Mono-Semibold.otf"; path = "../src/assets/fonts/SF-Mono-Semibold.otf"; sourceTree = ""; }; AA0B1CB92B00C5E100EAF77D /* SF-Mono-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "SF-Mono-Bold.otf"; path = "../src/assets/fonts/SF-Mono-Bold.otf"; sourceTree = ""; }; AA0B1CBA2B00C5E100EAF77D /* SF-Pro-Rounded-Black.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "SF-Pro-Rounded-Black.otf"; path = "../src/assets/fonts/SF-Pro-Rounded-Black.otf"; sourceTree = ""; }; @@ -298,7 +313,10 @@ B5CE8FFD29A5758100EB1EFA /* pooly@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "pooly@3x.png"; sourceTree = ""; }; B5D7F2EE29E8D41D003D6A54 /* finiliar@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "finiliar@3x.png"; sourceTree = ""; }; B5D7F2EF29E8D41E003D6A54 /* finiliar@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "finiliar@2x.png"; sourceTree = ""; }; + B726AE9AC9612098B9AE1E8C /* Pods-Rainbow.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Rainbow.release.xcconfig"; path = "Target Support Files/Pods-Rainbow/Pods-Rainbow.release.xcconfig"; sourceTree = ""; }; + B72F672E1C1931C1EFA69240 /* Pods-Rainbow.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Rainbow.debug.xcconfig"; path = "Target Support Files/Pods-Rainbow/Pods-Rainbow.debug.xcconfig"; sourceTree = ""; }; C04D10EF25AFC8C1003BEF7A /* Extras.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Extras.json; sourceTree = ""; }; + C0DE5565B77A45B108F28413 /* libPods-Rainbow.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Rainbow.a"; sourceTree = BUILT_PRODUCTS_DIR; }; C11640E7274DC10B00C9120A /* UIColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = ""; }; C1272389274EBBB6006AC743 /* CurrencyDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyDetails.swift; sourceTree = ""; }; C151287D2739F676006517AB /* IconProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconProvider.swift; sourceTree = ""; }; @@ -337,9 +355,15 @@ C9B378BA2C515A860085E5D0 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = ""; }; C9B378BD2C515A860085E5D0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; C9B378BF2C515A860085E5D0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + CE6C46FE0C9D0C9504C025AC /* Pods-ImageNotification.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageNotification.debug.xcconfig"; path = "Target Support Files/Pods-ImageNotification/Pods-ImageNotification.debug.xcconfig"; sourceTree = ""; }; D755E71324B04FEE9C691D14 /* libRNFirebase.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFirebase.a; sourceTree = ""; }; + D8779D7AB4A3E9DD8487D527 /* libPods-PriceWidgetExtension.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-PriceWidgetExtension.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + D8FA6FF97E16AF1841C65403 /* Pods-PriceWidgetExtension.staging.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PriceWidgetExtension.staging.xcconfig"; path = "Target Support Files/Pods-PriceWidgetExtension/Pods-PriceWidgetExtension.staging.xcconfig"; sourceTree = ""; }; + DF978C7B693121BE46F2053C /* Pods-Rainbow.staging.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Rainbow.staging.xcconfig"; path = "Target Support Files/Pods-Rainbow/Pods-Rainbow.staging.xcconfig"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; }; + EEDFB87C7DC0A55503B643C5 /* Pods-PriceWidgetExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PriceWidgetExtension.debug.xcconfig"; path = "Target Support Files/Pods-PriceWidgetExtension/Pods-PriceWidgetExtension.debug.xcconfig"; sourceTree = ""; }; + FD9E17465EF9D8CDA62027EE /* Pods-SelectTokenIntent.localrelease.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SelectTokenIntent.localrelease.xcconfig"; path = "Target Support Files/Pods-SelectTokenIntent/Pods-SelectTokenIntent.localrelease.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -347,6 +371,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 8AB133C6A6757CF6A2AA5009 /* libPods-ImageNotification.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -356,6 +381,7 @@ files = ( ED2971652150620600B7C4FE /* JavaScriptCore.framework in Frameworks */, C72F456C99A646399192517D /* libz.tbd in Frameworks */, + E9A0F69C0DB42A80F16CDBD5 /* libPods-Rainbow.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -365,6 +391,7 @@ files = ( C16DCF60272BA6EF00FF5C78 /* SwiftUI.framework in Frameworks */, C16DCF5E272BA6EF00FF5C78 /* WidgetKit.framework in Frameworks */, + 91C397E1FD7D8F76707B8740 /* libPods-PriceWidgetExtension.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -373,6 +400,7 @@ buildActionMask = 2147483647; files = ( C16DCF81272BAB9500FF5C78 /* Intents.framework in Frameworks */, + 7A797FC31B46D54B061D99E5 /* libPods-SelectTokenIntent.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -549,6 +577,10 @@ C16DCF80272BAB9500FF5C78 /* Intents.framework */, C16DCF8B272BAB9600FF5C78 /* IntentsUI.framework */, C9B378A12C5159880085E5D0 /* UniformTypeIdentifiers.framework */, + 33486EF423B2585D9BBA09F4 /* libPods-ImageNotification.a */, + D8779D7AB4A3E9DD8487D527 /* libPods-PriceWidgetExtension.a */, + C0DE5565B77A45B108F28413 /* libPods-Rainbow.a */, + 3AB8606AB30216E96A75779E /* libPods-SelectTokenIntent.a */, ); name = Frameworks; sourceTree = ""; @@ -712,6 +744,22 @@ C640359C0E6575CE0A7ECD73 /* Pods */ = { isa = PBXGroup; children = ( + CE6C46FE0C9D0C9504C025AC /* Pods-ImageNotification.debug.xcconfig */, + A6402205793DFA897132D7A9 /* Pods-ImageNotification.release.xcconfig */, + A5ACEBEDC5DFEC3A33504769 /* Pods-ImageNotification.localrelease.xcconfig */, + 208F04ABC0ACD249A8EDFA1A /* Pods-ImageNotification.staging.xcconfig */, + EEDFB87C7DC0A55503B643C5 /* Pods-PriceWidgetExtension.debug.xcconfig */, + A0B1D87220C90A9004D035C7 /* Pods-PriceWidgetExtension.release.xcconfig */, + 71C4AACC71462B12A43FDDE5 /* Pods-PriceWidgetExtension.localrelease.xcconfig */, + D8FA6FF97E16AF1841C65403 /* Pods-PriceWidgetExtension.staging.xcconfig */, + B72F672E1C1931C1EFA69240 /* Pods-Rainbow.debug.xcconfig */, + B726AE9AC9612098B9AE1E8C /* Pods-Rainbow.release.xcconfig */, + 59D20A56C5C8F6DD51C60C16 /* Pods-Rainbow.localrelease.xcconfig */, + DF978C7B693121BE46F2053C /* Pods-Rainbow.staging.xcconfig */, + 390E0879CC9C88D0DBF50690 /* Pods-SelectTokenIntent.debug.xcconfig */, + 2FA1FCE6D3B56B8902BEFC47 /* Pods-SelectTokenIntent.release.xcconfig */, + FD9E17465EF9D8CDA62027EE /* Pods-SelectTokenIntent.localrelease.xcconfig */, + 9E8BEFB6422BEB906DEB3C7E /* Pods-SelectTokenIntent.staging.xcconfig */, ); path = Pods; sourceTree = ""; @@ -767,9 +815,11 @@ isa = PBXNativeTarget; buildConfigurationList = 0299CE842886202800B5C7E7 /* Build configuration list for PBXNativeTarget "ImageNotification" */; buildPhases = ( + 078827AE1BEEEA04DAC20404 /* [CP] Check Pods Manifest.lock */, 0299CE732886202800B5C7E7 /* Sources */, 0299CE742886202800B5C7E7 /* Frameworks */, 0299CE752886202800B5C7E7 /* Resources */, + C7D37804C0351A4706E50397 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -784,12 +834,16 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Rainbow" */; buildPhases = ( + 605B2B3AD17EEB5D331577A2 /* [CP] Check Pods Manifest.lock */, AEAD56A917FF4051986EE3E6 /* [Expo] Configure project */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, 668ADB3225A4E3A40050859D /* Embed App Extensions */, + DF087C9CDB1CD1620D06CFDD /* [CP] Embed Pods Frameworks */, + 88311050F78AB0C097772AD8 /* [CP] Copy Pods Resources */, + 44BD1D1BE00D0E7C7327AD89 /* [CP-User] [RNFB] Core Configuration */, ); buildRules = ( ); @@ -809,9 +863,11 @@ isa = PBXNativeTarget; buildConfigurationList = C16DCF6E272BA6F100FF5C78 /* Build configuration list for PBXNativeTarget "PriceWidgetExtension" */; buildPhases = ( + 559EA91D42B3296394BED964 /* [CP] Check Pods Manifest.lock */, C16DCF58272BA6EF00FF5C78 /* Sources */, C16DCF59272BA6EF00FF5C78 /* Frameworks */, C16DCF5A272BA6EF00FF5C78 /* Resources */, + C69A614502B03804F13C83B4 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -826,9 +882,11 @@ isa = PBXNativeTarget; buildConfigurationList = C16DCF9F272BAB9600FF5C78 /* Build configuration list for PBXNativeTarget "SelectTokenIntent" */; buildPhases = ( + DB9668693166D9CD6F405FF2 /* [CP] Check Pods Manifest.lock */, C16DCF7B272BAB9500FF5C78 /* Sources */, C16DCF7C272BAB9500FF5C78 /* Frameworks */, C16DCF7D272BAB9500FF5C78 /* Resources */, + AD4DD19E5F95A8F47FFFFDDE /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -1059,6 +1117,183 @@ shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" \"$PROJECT_ROOT\" ios absolute | tail -n 1)\"\nfi\n\nif [[ -z \"$CLI_PATH\" ]]; then\n # Use Expo CLI\n export CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require.resolve('@expo/cli')\")\"\nfi\nif [[ -z \"$BUNDLE_COMMAND\" ]]; then\n # Default Expo CLI command for bundling\n export BUNDLE_COMMAND=\"export:embed\"\nfi\n\n`\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n"; showEnvVarsInLog = 0; }; + 078827AE1BEEEA04DAC20404 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-ImageNotification-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 44BD1D1BE00D0E7C7327AD89 /* [CP-User] [RNFB] Core Configuration */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)", + ); + name = "[CP-User] [RNFB] Core Configuration"; + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##########################################################################\n##########################################################################\n#\n# NOTE THAT IF YOU CHANGE THIS FILE YOU MUST RUN pod install AFTERWARDS\n#\n# This file is installed as an Xcode build script in the project file\n# by cocoapods, and you will not see your changes until you pod install\n#\n##########################################################################\n##########################################################################\n\nset -e\n\n_MAX_LOOKUPS=2;\n_SEARCH_RESULT=''\n_RN_ROOT_EXISTS=''\n_CURRENT_LOOKUPS=1\n_JSON_ROOT=\"'react-native'\"\n_JSON_FILE_NAME='firebase.json'\n_JSON_OUTPUT_BASE64='e30=' # { }\n_CURRENT_SEARCH_DIR=${PROJECT_DIR}\n_PLIST_BUDDY=/usr/libexec/PlistBuddy\n_TARGET_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n_DSYM_PLIST=\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"\n\n# plist arrays\n_PLIST_ENTRY_KEYS=()\n_PLIST_ENTRY_TYPES=()\n_PLIST_ENTRY_VALUES=()\n\nfunction setPlistValue {\n echo \"info: setting plist entry '$1' of type '$2' in file '$4'\"\n ${_PLIST_BUDDY} -c \"Add :$1 $2 '$3'\" $4 || echo \"info: '$1' already exists\"\n}\n\nfunction getFirebaseJsonKeyValue () {\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n ruby -Ku -e \"require 'rubygems';require 'json'; output=JSON.parse('$1'); puts output[$_JSON_ROOT]['$2']\"\n else\n echo \"\"\n fi;\n}\n\nfunction jsonBoolToYesNo () {\n if [[ $1 == \"false\" ]]; then\n echo \"NO\"\n elif [[ $1 == \"true\" ]]; then\n echo \"YES\"\n else echo \"NO\"\n fi\n}\n\necho \"info: -> RNFB build script started\"\necho \"info: 1) Locating ${_JSON_FILE_NAME} file:\"\n\nif [[ -z ${_CURRENT_SEARCH_DIR} ]]; then\n _CURRENT_SEARCH_DIR=$(pwd)\nfi;\n\nwhile true; do\n _CURRENT_SEARCH_DIR=$(dirname \"$_CURRENT_SEARCH_DIR\")\n if [[ \"$_CURRENT_SEARCH_DIR\" == \"/\" ]] || [[ ${_CURRENT_LOOKUPS} -gt ${_MAX_LOOKUPS} ]]; then break; fi;\n echo \"info: ($_CURRENT_LOOKUPS of $_MAX_LOOKUPS) Searching in '$_CURRENT_SEARCH_DIR' for a ${_JSON_FILE_NAME} file.\"\n _SEARCH_RESULT=$(find \"$_CURRENT_SEARCH_DIR\" -maxdepth 2 -name ${_JSON_FILE_NAME} -print | /usr/bin/head -n 1)\n if [[ ${_SEARCH_RESULT} ]]; then\n echo \"info: ${_JSON_FILE_NAME} found at $_SEARCH_RESULT\"\n break;\n fi;\n _CURRENT_LOOKUPS=$((_CURRENT_LOOKUPS+1))\ndone\n\nif [[ ${_SEARCH_RESULT} ]]; then\n _JSON_OUTPUT_RAW=$(cat \"${_SEARCH_RESULT}\")\n _RN_ROOT_EXISTS=$(ruby -Ku -e \"require 'rubygems';require 'json'; output=JSON.parse('$_JSON_OUTPUT_RAW'); puts output[$_JSON_ROOT]\" || echo '')\n\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n if ! python3 --version >/dev/null 2>&1; then echo \"python3 not found, firebase.json file processing error.\" && exit 1; fi\n _JSON_OUTPUT_BASE64=$(python3 -c 'import json,sys,base64;print(base64.b64encode(bytes(json.dumps(json.loads(open('\"'${_SEARCH_RESULT}'\"', '\"'rb'\"').read())['${_JSON_ROOT}']), '\"'utf-8'\"')).decode())' || echo \"e30=\")\n fi\n\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n\n # config.app_data_collection_default_enabled\n _APP_DATA_COLLECTION_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_data_collection_default_enabled\")\n if [[ $_APP_DATA_COLLECTION_ENABLED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseDataCollectionDefaultEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_DATA_COLLECTION_ENABLED\")\")\n fi\n\n # config.analytics_auto_collection_enabled\n _ANALYTICS_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_auto_collection_enabled\")\n if [[ $_ANALYTICS_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_COLLECTION\")\")\n fi\n\n # config.analytics_collection_deactivated\n _ANALYTICS_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_collection_deactivated\")\n if [[ $_ANALYTICS_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_DEACTIVATED\")\")\n fi\n\n # config.analytics_idfv_collection_enabled\n _ANALYTICS_IDFV_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_idfv_collection_enabled\")\n if [[ $_ANALYTICS_IDFV_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_IDFV_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_IDFV_COLLECTION\")\")\n fi\n\n # config.analytics_default_allow_analytics_storage\n _ANALYTICS_STORAGE=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_analytics_storage\")\n if [[ $_ANALYTICS_STORAGE ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_ANALYTICS_STORAGE\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_STORAGE\")\")\n fi\n\n # config.analytics_default_allow_ad_storage\n _ANALYTICS_AD_STORAGE=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_storage\")\n if [[ $_ANALYTICS_AD_STORAGE ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_STORAGE\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AD_STORAGE\")\")\n fi\n\n # config.analytics_default_allow_ad_user_data\n _ANALYTICS_AD_USER_DATA=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_user_data\")\n if [[ $_ANALYTICS_AD_USER_DATA ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_USER_DATA\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AD_USER_DATA\")\")\n fi\n\n # config.analytics_default_allow_ad_personalization_signals\n _ANALYTICS_PERSONALIZATION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_personalization_signals\")\n if [[ $_ANALYTICS_PERSONALIZATION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_PERSONALIZATION_SIGNALS\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_PERSONALIZATION\")\")\n fi\n\n # config.analytics_registration_with_ad_network_enabled\n _ANALYTICS_REGISTRATION_WITH_AD_NETWORK=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"google_analytics_registration_with_ad_network_enabled\")\n if [[ $_ANALYTICS_REGISTRATION_WITH_AD_NETWORK ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_REGISTRATION_WITH_AD_NETWORK_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_REGISTRATION_WITH_AD_NETWORK\")\")\n fi\n\n # config.google_analytics_automatic_screen_reporting_enabled\n _ANALYTICS_AUTO_SCREEN_REPORTING=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"google_analytics_automatic_screen_reporting_enabled\")\n if [[ $_ANALYTICS_AUTO_SCREEN_REPORTING ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAutomaticScreenReportingEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_SCREEN_REPORTING\")\")\n fi\n\n # config.perf_auto_collection_enabled\n _PERF_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_auto_collection_enabled\")\n if [[ $_PERF_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_enabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_AUTO_COLLECTION\")\")\n fi\n\n # config.perf_collection_deactivated\n _PERF_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_collection_deactivated\")\n if [[ $_PERF_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_deactivated\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_DEACTIVATED\")\")\n fi\n\n # config.messaging_auto_init_enabled\n _MESSAGING_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"messaging_auto_init_enabled\")\n if [[ $_MESSAGING_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseMessagingAutoInitEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_MESSAGING_AUTO_INIT\")\")\n fi\n\n # config.in_app_messaging_auto_colllection_enabled\n _FIAM_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"in_app_messaging_auto_collection_enabled\")\n if [[ $_FIAM_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseInAppMessagingAutomaticDataCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_FIAM_AUTO_INIT\")\")\n fi\n\n # config.app_check_token_auto_refresh\n _APP_CHECK_TOKEN_AUTO_REFRESH=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_check_token_auto_refresh\")\n if [[ $_APP_CHECK_TOKEN_AUTO_REFRESH ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAppCheckTokenAutoRefreshEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_CHECK_TOKEN_AUTO_REFRESH\")\")\n fi\n\n # config.crashlytics_disable_auto_disabler - undocumented for now - mainly for debugging, document if becomes useful\n _CRASHLYTICS_AUTO_DISABLE_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"crashlytics_disable_auto_disabler\")\n if [[ $_CRASHLYTICS_AUTO_DISABLE_ENABLED == \"true\" ]]; then\n echo \"Disabled Crashlytics auto disabler.\" # do nothing\n else\n _PLIST_ENTRY_KEYS+=(\"FirebaseCrashlyticsCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"NO\")\n fi\nelse\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n echo \"warning: A firebase.json file was not found, whilst this file is optional it is recommended to include it to configure firebase services in React Native Firebase.\"\nfi;\n\necho \"info: 2) Injecting Info.plist entries: \"\n\n# Log out the keys we're adding\nfor i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n echo \" -> $i) ${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\"\ndone\n\nfor plist in \"${_TARGET_PLIST}\" \"${_DSYM_PLIST}\" ; do\n if [[ -f \"${plist}\" ]]; then\n\n # paths with spaces break the call to setPlistValue. temporarily modify\n # the shell internal field separator variable (IFS), which normally\n # includes spaces, to consist only of line breaks\n oldifs=$IFS\n IFS=\"\n\"\n\n for i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n setPlistValue \"${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\" \"${plist}\"\n done\n\n # restore the original internal field separator value\n IFS=$oldifs\n else\n echo \"warning: A Info.plist build output file was not found (${plist})\"\n fi\ndone\n\necho \"info: <- RNFB build script finished\"\n"; + }; + 559EA91D42B3296394BED964 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-PriceWidgetExtension-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 605B2B3AD17EEB5D331577A2 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Rainbow-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 88311050F78AB0C097772AD8 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Rainbow/Pods-Rainbow-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseABTesting/FirebaseABTesting_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreExtension/FirebaseCoreExtension_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal/FirebaseCoreInternal_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations/FirebaseInstallations_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseRemoteConfig/FirebaseRemoteConfig_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/GoogleDataTransport/GoogleDataTransport_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseMessaging/FirebaseMessaging_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/RNImageCropPickerPrivacyInfo.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/QBImagePicker.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/Rudder/Rudder.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/Sentry/Sentry.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/TOCropViewController/TOCropViewControllerBundle.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/react-native-cameraroll/RNCameraRollPrivacyInfo.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseABTesting_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCore_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCoreExtension_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCoreInternal_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseInstallations_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseRemoteConfig_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleDataTransport_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleUtilities_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FBLPromises_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/nanopb_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseMessaging_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNImageCropPickerPrivacyInfo.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/QBImagePicker.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Rudder.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SDWebImage.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Sentry.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/TOCropViewControllerBundle.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCameraRollPrivacyInfo.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Rainbow/Pods-Rainbow-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + AD4DD19E5F95A8F47FFFFDDE /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-SelectTokenIntent/Pods-SelectTokenIntent-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseABTesting/FirebaseABTesting_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreExtension/FirebaseCoreExtension_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal/FirebaseCoreInternal_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations/FirebaseInstallations_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseRemoteConfig/FirebaseRemoteConfig_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/GoogleDataTransport/GoogleDataTransport_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb_Privacy.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseABTesting_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCore_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCoreExtension_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCoreInternal_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseInstallations_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseRemoteConfig_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleDataTransport_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleUtilities_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FBLPromises_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/nanopb_Privacy.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SelectTokenIntent/Pods-SelectTokenIntent-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; AEAD56A917FF4051986EE3E6 /* [Expo] Configure project */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -1078,6 +1313,120 @@ shellPath = /bin/sh; shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-Rainbow/expo-configure-project.sh\"\n"; }; + C69A614502B03804F13C83B4 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-PriceWidgetExtension/Pods-PriceWidgetExtension-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseABTesting/FirebaseABTesting_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreExtension/FirebaseCoreExtension_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal/FirebaseCoreInternal_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations/FirebaseInstallations_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseRemoteConfig/FirebaseRemoteConfig_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/GoogleDataTransport/GoogleDataTransport_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb_Privacy.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseABTesting_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCore_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCoreExtension_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCoreInternal_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseInstallations_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseRemoteConfig_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleDataTransport_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleUtilities_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FBLPromises_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/nanopb_Privacy.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PriceWidgetExtension/Pods-PriceWidgetExtension-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + C7D37804C0351A4706E50397 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ImageNotification/Pods-ImageNotification-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseABTesting/FirebaseABTesting_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreExtension/FirebaseCoreExtension_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal/FirebaseCoreInternal_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations/FirebaseInstallations_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseRemoteConfig/FirebaseRemoteConfig_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/GoogleDataTransport/GoogleDataTransport_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseMessaging/FirebaseMessaging_Privacy.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseABTesting_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCore_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCoreExtension_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCoreInternal_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseInstallations_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseRemoteConfig_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleDataTransport_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleUtilities_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FBLPromises_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/nanopb_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseMessaging_Privacy.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ImageNotification/Pods-ImageNotification-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + DB9668693166D9CD6F405FF2 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-SelectTokenIntent-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + DF087C9CDB1CD1620D06CFDD /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Rainbow/Pods-Rainbow-frameworks.sh", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Rainbow/Pods-Rainbow-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -1264,6 +1613,7 @@ /* Begin XCBuildConfiguration section */ 0299CE802886202800B5C7E7 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = CE6C46FE0C9D0C9504C025AC /* Pods-ImageNotification.debug.xcconfig */; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ANALYZER_NONNULL = YES; @@ -1314,6 +1664,7 @@ }; 0299CE812886202800B5C7E7 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = A6402205793DFA897132D7A9 /* Pods-ImageNotification.release.xcconfig */; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ANALYZER_NONNULL = YES; @@ -1365,6 +1716,7 @@ }; 0299CE822886202800B5C7E7 /* LocalRelease */ = { isa = XCBuildConfiguration; + baseConfigurationReference = A5ACEBEDC5DFEC3A33504769 /* Pods-ImageNotification.localrelease.xcconfig */; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ANALYZER_NONNULL = YES; @@ -1413,6 +1765,7 @@ }; 0299CE832886202800B5C7E7 /* Staging */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 208F04ABC0ACD249A8EDFA1A /* Pods-ImageNotification.staging.xcconfig */; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ANALYZER_NONNULL = YES; @@ -1461,6 +1814,7 @@ }; 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = B72F672E1C1931C1EFA69240 /* Pods-Rainbow.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; APPLICATION_EXTENSION_API_ONLY = NO; @@ -1538,6 +1892,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = B726AE9AC9612098B9AE1E8C /* Pods-Rainbow.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; APPLICATION_EXTENSION_API_ONLY = NO; @@ -1658,6 +2013,7 @@ }; 2C6A799821127ED9003AFB37 /* Staging */ = { isa = XCBuildConfiguration; + baseConfigurationReference = DF978C7B693121BE46F2053C /* Pods-Rainbow.staging.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; APPLICATION_EXTENSION_API_ONLY = NO; @@ -1774,6 +2130,7 @@ }; 2C87B79A2197FA1900682EC4 /* LocalRelease */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 59D20A56C5C8F6DD51C60C16 /* Pods-Rainbow.localrelease.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; APPLICATION_EXTENSION_API_ONLY = NO; @@ -1947,6 +2304,7 @@ }; C16DCF6A272BA6F100FF5C78 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = EEDFB87C7DC0A55503B643C5 /* Pods-PriceWidgetExtension.debug.xcconfig */; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -1996,6 +2354,7 @@ }; C16DCF6B272BA6F100FF5C78 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = A0B1D87220C90A9004D035C7 /* Pods-PriceWidgetExtension.release.xcconfig */; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -2046,6 +2405,7 @@ }; C16DCF6C272BA6F100FF5C78 /* LocalRelease */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 71C4AACC71462B12A43FDDE5 /* Pods-PriceWidgetExtension.localrelease.xcconfig */; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -2093,6 +2453,7 @@ }; C16DCF6D272BA6F100FF5C78 /* Staging */ = { isa = XCBuildConfiguration; + baseConfigurationReference = D8FA6FF97E16AF1841C65403 /* Pods-PriceWidgetExtension.staging.xcconfig */; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -2140,6 +2501,7 @@ }; C16DCFA0272BAB9600FF5C78 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 390E0879CC9C88D0DBF50690 /* Pods-SelectTokenIntent.debug.xcconfig */; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ANALYZER_NONNULL = YES; @@ -2187,6 +2549,7 @@ }; C16DCFA1272BAB9600FF5C78 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 2FA1FCE6D3B56B8902BEFC47 /* Pods-SelectTokenIntent.release.xcconfig */; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ANALYZER_NONNULL = YES; @@ -2235,6 +2598,7 @@ }; C16DCFA2272BAB9600FF5C78 /* LocalRelease */ = { isa = XCBuildConfiguration; + baseConfigurationReference = FD9E17465EF9D8CDA62027EE /* Pods-SelectTokenIntent.localrelease.xcconfig */; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ANALYZER_NONNULL = YES; @@ -2280,6 +2644,7 @@ }; C16DCFA3272BAB9600FF5C78 /* Staging */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 9E8BEFB6422BEB906DEB3C7E /* Pods-SelectTokenIntent.staging.xcconfig */; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ANALYZER_NONNULL = YES; diff --git a/reset.d.ts b/reset.d.ts new file mode 100644 index 00000000000..fa23a2616ae --- /dev/null +++ b/reset.d.ts @@ -0,0 +1,9 @@ +type NonFalsy = T extends false | 0 | '' | null | undefined | 0n ? never : T; + +interface Array { + filter(predicate: BooleanConstructor, thisArg?: any): NonFalsy[]; +} + +interface ReadonlyArray { + filter(predicate: BooleanConstructor, thisArg?: any): NonFalsy[]; +} diff --git a/src/App.tsx b/src/App.tsx index 0e9ff68698e..d59b7ed7296 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,7 +5,6 @@ import { AppRegistry, Dimensions, LogBox, StyleSheet, View } from 'react-native' import { Toaster } from 'sonner-native'; import { MobileWalletProtocolProvider } from '@coinbase/mobile-wallet-protocol-host'; import { DeeplinkHandler } from '@/components/DeeplinkHandler'; -import { AppStateChangeHandler } from '@/components/AppStateChangeHandler'; import { useApplicationSetup } from '@/hooks/useApplicationSetup'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context'; @@ -84,7 +83,6 @@ function App({ walletReady }: AppProps) { - ); diff --git a/src/__swaps__/screens/Swap/components/AnimatedBlurView.tsx b/src/__swaps__/screens/Swap/components/AnimatedBlurView.tsx deleted file mode 100644 index 11f4a44f76a..00000000000 --- a/src/__swaps__/screens/Swap/components/AnimatedBlurView.tsx +++ /dev/null @@ -1,4 +0,0 @@ -import { BlurView } from '@react-native-community/blur'; -import Animated from 'react-native-reanimated'; - -export const AnimatedBlurView = Animated.createAnimatedComponent(BlurView); diff --git a/src/__swaps__/screens/Swap/components/CoinRow.tsx b/src/__swaps__/screens/Swap/components/CoinRow.tsx index a02a3ba283b..c3fb98919b1 100644 --- a/src/__swaps__/screens/Swap/components/CoinRow.tsx +++ b/src/__swaps__/screens/Swap/components/CoinRow.tsx @@ -8,7 +8,7 @@ import { ContextMenuButton } from '@/components/context-menu'; import { Box, Column, Columns, HitSlop, Inline, Text } from '@/design-system'; import { setClipboard } from '@/hooks/useClipboard'; import * as i18n from '@/languages'; -import { BASE_DEGEN_ADDRESS, DEGEN_CHAIN_DEGEN_ADDRESS, ETH_ADDRESS } from '@/references'; +import { ETH_ADDRESS } from '@/references'; import { toggleFavorite } from '@/resources/favorites'; import { useUserAssetsStore } from '@/state/assets/userAssets'; import { ethereumUtils, haptics, showActionSheetWithOptions } from '@/utils'; @@ -22,13 +22,6 @@ import { useBackendNetworksStore } from '@/state/backendNetworks/backendNetworks export const COIN_ROW_WITH_PADDING_HEIGHT = 56; function determineFavoriteAddressAndChain(address: AddressOrEth, mainnetAddress: AddressOrEth | undefined, chainId: ChainId | undefined) { - if (address === BASE_DEGEN_ADDRESS && chainId === ChainId.base) { - return { - addressToFetch: DEGEN_CHAIN_DEGEN_ADDRESS, - chainToFetchOn: ChainId.degen, - }; - } - // if no mainnet address, default to fetch the favorite for the address we have and chain we have if (!mainnetAddress) { return { diff --git a/src/__swaps__/screens/Swap/components/EstimatedSwapGasFee.tsx b/src/__swaps__/screens/Swap/components/EstimatedSwapGasFee.tsx index 5f510346f37..b4ff054d2e1 100644 --- a/src/__swaps__/screens/Swap/components/EstimatedSwapGasFee.tsx +++ b/src/__swaps__/screens/Swap/components/EstimatedSwapGasFee.tsx @@ -5,10 +5,10 @@ import { SharedValue, useAnimatedStyle, useDerivedValue, withRepeat, withSequenc import { opacity } from '@/__swaps__/utils/swaps'; import { TIMING_CONFIGS } from '@/components/animations/animationConfigs'; import { useDelayedValue } from '@/hooks/reanimated/useDelayedValue'; -import { pulsingConfig, sliderConfig } from '../constants'; -import { GasSettings } from '../hooks/useCustomGas'; -import { useSwapEstimatedGasFee } from '../hooks/useEstimatedGasFee'; -import { useSwapContext } from '../providers/swap-provider'; +import { pulsingConfig, sliderConfig } from '@/__swaps__/screens/Swap/constants'; +import { GasSettings } from '@/__swaps__/screens/Swap/hooks/useCustomGas'; +import { useSwapEstimatedGasFee } from '@/__swaps__/screens/Swap/hooks/useEstimatedGasFee'; +import { useSwapContext } from '@/__swaps__/screens/Swap/providers/swap-provider'; type EstimatedSwapGasFeeProps = { gasSettings?: GasSettings } & Partial< Pick diff --git a/src/__swaps__/screens/Swap/components/ExchangeRateBubble.tsx b/src/__swaps__/screens/Swap/components/ExchangeRateBubble.tsx index 17a14866819..556ac371cee 100644 --- a/src/__swaps__/screens/Swap/components/ExchangeRateBubble.tsx +++ b/src/__swaps__/screens/Swap/components/ExchangeRateBubble.tsx @@ -12,7 +12,7 @@ import { AddressZero } from '@ethersproject/constants'; import { ETH_ADDRESS } from '@/references'; import { DEVICE_WIDTH } from '@/utils/deviceUtils'; import { GestureHandlerButton } from './GestureHandlerButton'; -import { convertAmountToNativeDisplayWorklet } from '@/__swaps__/utils/numbers'; +import { convertAmountToNativeDisplayWorklet } from '@/helpers/utilities'; import { useAccountSettings } from '@/hooks'; export const ExchangeRateBubble = () => { diff --git a/src/__swaps__/screens/Swap/components/FlipButton.tsx b/src/__swaps__/screens/Swap/components/FlipButton.tsx index 8298b6aacc7..b31fa07eb52 100644 --- a/src/__swaps__/screens/Swap/components/FlipButton.tsx +++ b/src/__swaps__/screens/Swap/components/FlipButton.tsx @@ -8,7 +8,7 @@ import { Bleed, Box, IconContainer, Text, globalColors, useColorMode } from '@/d import { SEPARATOR_COLOR } from '@/__swaps__/screens/Swap/constants'; import { getColorValueForThemeWorklet, opacity } from '@/__swaps__/utils/swaps'; import { IS_ANDROID, IS_IOS } from '@/env'; -import { AnimatedBlurView } from '@/__swaps__/screens/Swap/components/AnimatedBlurView'; +import { AnimatedBlurView } from '@/components/AnimatedComponents/AnimatedBlurView'; import { useSwapContext } from '@/__swaps__/screens/Swap/providers/swap-provider'; import { TIMING_CONFIGS } from '@/components/animations/animationConfigs'; import { SwapAssetType } from '@/__swaps__/types/swap'; @@ -90,7 +90,7 @@ export const FlipButton = () => { return { shadowColor: isDarkMode ? globalColors.grey100 - : getColorValueForThemeWorklet(internalSelectedOutputAsset.value?.mixedShadowColor, false, true), + : getColorValueForThemeWorklet(internalSelectedOutputAsset.value?.mixedShadowColor, false), }; }); @@ -162,7 +162,7 @@ const SpinnerComponent = () => { const animatedColor = useDerivedValue(() => { return withTiming( - getColorValueForThemeWorklet(internalSelectedOutputAsset.value?.highContrastColor, isDarkMode, true), + getColorValueForThemeWorklet(internalSelectedOutputAsset.value?.highContrastColor, isDarkMode), TIMING_CONFIGS.slowFadeConfig ); }); diff --git a/src/__swaps__/screens/Swap/components/GasButton.tsx b/src/__swaps__/screens/Swap/components/GasButton.tsx index 70ab401d621..eb5ccbd7638 100644 --- a/src/__swaps__/screens/Swap/components/GasButton.tsx +++ b/src/__swaps__/screens/Swap/components/GasButton.tsx @@ -1,7 +1,7 @@ import { GasSpeed } from '@/__swaps__/types/gas'; -import { weiToGwei } from '@/__swaps__/utils/ethereum'; +import { weiToGwei } from '@/parsers'; import { getCachedCurrentBaseFee, useMeteorologySuggestions } from '@/__swaps__/utils/meteorology'; -import { add, formatNumber } from '@/__swaps__/utils/numbers'; +import { add, formatNumber } from '@/helpers/utilities'; import { getColorValueForThemeWorklet } from '@/__swaps__/utils/swaps'; import { ButtonPressAnimation } from '@/components/animations'; import { ContextMenu } from '@/components/context-menu'; @@ -110,10 +110,10 @@ const GasMenu = ({ backToReview = false, children }: { backToReview?: boolean; c const preferredNetwork = swapsStore(s => s.preferredNetwork); const chainId = swapsStore(s => s.inputAsset?.chainId || preferredNetwork || ChainId.mainnet); - const metereologySuggestions = useMeteorologySuggestions({ chainId }); + const { data: metereologySuggestions, isLoading } = useMeteorologySuggestions({ chainId }); const customGasSettings = useCustomGasSettings(chainId); - const menuOptions = useMemo(() => [...keys(metereologySuggestions.data), GasSpeed.CUSTOM] as GasSpeed[], [metereologySuggestions.data]); + const menuOptions = useMemo(() => [...keys(metereologySuggestions), GasSpeed.CUSTOM] as GasSpeed[], [metereologySuggestions]); const handlePressSpeedOption = useCallback( (selectedGasSpeed: GasSpeed) => { @@ -149,7 +149,7 @@ const GasMenu = ({ backToReview = false, children }: { backToReview?: boolean; c const menuConfig = useMemo(() => { const menuItems = menuOptions.map(gasOption => { const currentBaseFee = getCachedCurrentBaseFee(chainId); - const gasSettings = gasOption === GasSpeed.CUSTOM ? customGasSettings : metereologySuggestions.data?.[gasOption]; + const gasSettings = gasOption === GasSpeed.CUSTOM ? customGasSettings : metereologySuggestions?.[gasOption]; const subtitle = getEstimatedFeeRangeInGwei(gasSettings, currentBaseFee); return { @@ -160,9 +160,9 @@ const GasMenu = ({ backToReview = false, children }: { backToReview?: boolean; c }; }); return { menuItems, menuTitle: '' }; - }, [customGasSettings, menuOptions, metereologySuggestions.data, chainId]); + }, [customGasSettings, menuOptions, metereologySuggestions, chainId]); - if (metereologySuggestions.isLoading) return children; + if (isLoading) return children; return ( @@ -211,7 +211,7 @@ export function ReviewGasButton() { const animatedBorderColor = useAnimatedStyle(() => { return { - borderColor: getColorValueForThemeWorklet(internalSelectedOutputAsset.value?.highContrastColor, isDarkMode, true), + borderColor: getColorValueForThemeWorklet(internalSelectedOutputAsset.value?.highContrastColor, isDarkMode), }; }); diff --git a/src/__swaps__/screens/Swap/components/GasPanel.tsx b/src/__swaps__/screens/Swap/components/GasPanel.tsx index 4691d7d6b07..0bd16ddc8a4 100644 --- a/src/__swaps__/screens/Swap/components/GasPanel.tsx +++ b/src/__swaps__/screens/Swap/components/GasPanel.tsx @@ -6,7 +6,7 @@ import { MIN_FLASHBOTS_PRIORITY_FEE, THICK_BORDER_WIDTH } from '@/__swaps__/scre import { NavigationSteps, useSwapContext } from '@/__swaps__/screens/Swap/providers/swap-provider'; import { ChainId } from '@/state/backendNetworks/types'; import { GasSpeed } from '@/__swaps__/types/gas'; -import { gweiToWei, weiToGwei } from '@/__swaps__/utils/ethereum'; +import { gweiToWei, weiToGwei } from '@/parsers'; import { getCachedCurrentBaseFee, getSelectedSpeedSuggestion, @@ -16,13 +16,12 @@ import { useMeteorologySuggestion, useMeteorologySuggestions, } from '@/__swaps__/utils/meteorology'; -import { add, formatNumber, greaterThan, multiply, subtract } from '@/__swaps__/utils/numbers'; +import { add, greaterThan, multiply, subtract, lessThan, formatNumber } from '@/helpers/utilities'; import { opacity } from '@/__swaps__/utils/swaps'; import { ButtonPressAnimation } from '@/components/animations'; import { SPRING_CONFIGS } from '@/components/animations/animationConfigs'; import { Bleed, Box, Inline, Separator, Stack, Text, globalColors, useColorMode, useForegroundColor } from '@/design-system'; import { IS_ANDROID } from '@/env'; -import { lessThan } from '@/helpers/utilities'; import { useNavigation } from '@/navigation'; import Routes from '@/navigation/routesNames'; import { createRainbowStore } from '@/state/internal/createRainbowStore'; @@ -238,7 +237,6 @@ function useGasPanelState< speed, select, enabled: !editedSetting, - notifyOnChangeProps: !editedSetting && speed !== 'custom' ? ['data'] : [], }); if (editedSetting) return editedSetting; diff --git a/src/__swaps__/screens/Swap/components/ReviewPanel.tsx b/src/__swaps__/screens/Swap/components/ReviewPanel.tsx index b4c3d9ecf90..d05136440fa 100644 --- a/src/__swaps__/screens/Swap/components/ReviewPanel.tsx +++ b/src/__swaps__/screens/Swap/components/ReviewPanel.tsx @@ -2,12 +2,7 @@ import { AnimatedChainImage } from '@/__swaps__/screens/Swap/components/Animated import { ReviewGasButton } from '@/__swaps__/screens/Swap/components/GasButton'; import { GestureHandlerButton } from '@/__swaps__/screens/Swap/components/GestureHandlerButton'; import { useEstimatedTime } from '@/__swaps__/utils/meteorology'; -import { - convertRawAmountToBalance, - convertRawAmountToBalanceWorklet, - handleSignificantDecimals, - multiply, -} from '@/__swaps__/utils/numbers'; +import { convertRawAmountToBalance, convertRawAmountToBalanceWorklet, handleSignificantDecimals, multiply } from '@/helpers/utilities'; import { opacity } from '@/__swaps__/utils/swaps'; import { ButtonPressAnimation } from '@/components/animations'; import { SPRING_CONFIGS } from '@/components/animations/animationConfigs'; @@ -29,7 +24,6 @@ import * as i18n from '@/languages'; import { useNavigation } from '@/navigation'; import Routes from '@/navigation/routesNames'; import { swapsStore, useSwapsStore } from '@/state/swaps/swapsStore'; -import { getNativeAssetForNetwork } from '@/utils/ethereumUtils'; import { CrosschainQuote, Quote, QuoteError } from '@rainbow-me/swaps'; import React, { useCallback } from 'react'; import { StyleSheet, View } from 'react-native'; @@ -357,8 +351,8 @@ export function ReviewPanel() { }); const openGasExplainer = useCallback(async () => { - const nativeAsset = await getNativeAssetForNetwork({ chainId: swapsStore.getState().inputAsset?.chainId ?? ChainId.mainnet }); - + const chainsNativeAsset = useBackendNetworksStore.getState().getChainsNativeAsset(); + const nativeAsset = chainsNativeAsset[swapsStore.getState().inputAsset?.chainId ?? ChainId.mainnet]; navigate(Routes.EXPLAIN_SHEET, { chainId: swapsStore.getState().inputAsset?.chainId ?? ChainId.mainnet, type: 'gas', diff --git a/src/__swaps__/screens/Swap/components/SwapActionButton.tsx b/src/__swaps__/screens/Swap/components/SwapActionButton.tsx index 4d8f2dab126..13e1d722d95 100644 --- a/src/__swaps__/screens/Swap/components/SwapActionButton.tsx +++ b/src/__swaps__/screens/Swap/components/SwapActionButton.tsx @@ -61,7 +61,7 @@ function SwapButton({ }); const secondaryTextStyles = useAnimatedStyle(() => { - const secondaryColor = getColorValueForThemeWorklet(asset.value?.textColor, isDarkMode, true); + const secondaryColor = getColorValueForThemeWorklet(asset.value?.textColor, isDarkMode); let opacity = isDarkMode ? 0.76 : 0.8; if (secondaryColor === globalColors.grey100) { @@ -75,9 +75,7 @@ function SwapButton({ const buttonWrapperStyles = useAnimatedStyle(() => { return { - backgroundColor: outline - ? 'transparent' - : getColorValueForThemeWorklet(asset.value?.highContrastColor, isDarkMode, true) || fallbackColor, + backgroundColor: outline ? 'transparent' : getColorValueForThemeWorklet(asset.value?.highContrastColor, isDarkMode) || fallbackColor, borderColor: outline ? separatorSecondary : undefined, borderRadius: borderRadius ?? 24, height: small ? 36 : 48, @@ -169,7 +167,7 @@ const HoldProgress = ({ holdProgress }: { holdProgress: SharedValue }) = const { internalSelectedOutputAsset } = useSwapContext(); const [brightenedColor, setBrightenedColor] = useState( - transformColor(getColorValueForThemeWorklet(internalSelectedOutputAsset.value?.highContrastColor, isDarkMode, true), false) + transformColor(getColorValueForThemeWorklet(internalSelectedOutputAsset.value?.highContrastColor, isDarkMode), false) ); const holdProgressStyle = useAnimatedStyle(() => { @@ -192,7 +190,7 @@ const HoldProgress = ({ holdProgress }: { holdProgress: SharedValue }) = () => internalSelectedOutputAsset.value?.highContrastColor, (current, previous) => { if (current && current !== previous) { - runOnJS(transformColor)(getColorValueForThemeWorklet(current, isDarkMode, true)); + runOnJS(transformColor)(getColorValueForThemeWorklet(current, isDarkMode)); } }, [] diff --git a/src/__swaps__/screens/Swap/components/SwapBackground.tsx b/src/__swaps__/screens/Swap/components/SwapBackground.tsx index 594b33f61bc..365707c24e6 100644 --- a/src/__swaps__/screens/Swap/components/SwapBackground.tsx +++ b/src/__swaps__/screens/Swap/components/SwapBackground.tsx @@ -18,17 +18,17 @@ export const SwapBackground = () => { const { internalSelectedInputAsset, internalSelectedOutputAsset } = useSwapContext(); const animatedTopColor = useDerivedValue(() => { - if (IS_TEST) return getColorValueForThemeWorklet(DEFAULT_BACKGROUND_COLOR, isDarkMode, true); + if (IS_TEST) return getColorValueForThemeWorklet(DEFAULT_BACKGROUND_COLOR, isDarkMode); return withTiming( - getColorValueForThemeWorklet(internalSelectedInputAsset.value?.tintedBackgroundColor || DEFAULT_BACKGROUND_COLOR, isDarkMode, true), + getColorValueForThemeWorklet(internalSelectedInputAsset.value?.tintedBackgroundColor || DEFAULT_BACKGROUND_COLOR, isDarkMode), TIMING_CONFIGS.slowFadeConfig ); }); const animatedBottomColor = useDerivedValue(() => { - if (IS_TEST) return getColorValueForThemeWorklet(DEFAULT_BACKGROUND_COLOR, isDarkMode, true); + if (IS_TEST) return getColorValueForThemeWorklet(DEFAULT_BACKGROUND_COLOR, isDarkMode); return withTiming( - getColorValueForThemeWorklet(internalSelectedOutputAsset.value?.tintedBackgroundColor || DEFAULT_BACKGROUND_COLOR, isDarkMode, true), + getColorValueForThemeWorklet(internalSelectedOutputAsset.value?.tintedBackgroundColor || DEFAULT_BACKGROUND_COLOR, isDarkMode), TIMING_CONFIGS.slowFadeConfig ); }); diff --git a/src/__swaps__/screens/Swap/components/SwapCoinIconTextFallback.tsx b/src/__swaps__/screens/Swap/components/SwapCoinIconTextFallback.tsx index 306e17711da..99c36792fcc 100644 --- a/src/__swaps__/screens/Swap/components/SwapCoinIconTextFallback.tsx +++ b/src/__swaps__/screens/Swap/components/SwapCoinIconTextFallback.tsx @@ -50,7 +50,7 @@ export const SwapCoinIconTextFallback = ({ asset, height, width, style }: SwapCo const backgroundColor = useAnimatedStyle(() => { return { - backgroundColor: getColorValueForThemeWorklet(asset.value?.color, isDarkMode, true), + backgroundColor: getColorValueForThemeWorklet(asset.value?.color, isDarkMode), }; }); @@ -61,7 +61,7 @@ export const SwapCoinIconTextFallback = ({ asset, height, width, style }: SwapCo const animatedFontSize = useAnimatedStyle(() => { return { fontSize: buildFallbackFontSize(formattedSymbol.value, width), - color: getColorValueForThemeWorklet(asset.value?.textColor, isDarkMode, true), + color: getColorValueForThemeWorklet(asset.value?.textColor, isDarkMode), }; }); diff --git a/src/__swaps__/screens/Swap/components/SwapInputValuesCaret.tsx b/src/__swaps__/screens/Swap/components/SwapInputValuesCaret.tsx index 99c57675935..c7c33a83733 100644 --- a/src/__swaps__/screens/Swap/components/SwapInputValuesCaret.tsx +++ b/src/__swaps__/screens/Swap/components/SwapInputValuesCaret.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { StyleSheet } from 'react-native'; import Animated, { Easing, SharedValue, useAnimatedStyle, withRepeat, withSequence, withTiming } from 'react-native-reanimated'; import { SLIDER_COLLAPSED_HEIGHT, SLIDER_HEIGHT, caretConfig } from '@/__swaps__/screens/Swap/constants'; -import { equalWorklet } from '@/__swaps__/safe-math/SafeMath'; +import { equalWorklet } from '@/safe-math/SafeMath'; import { NavigationSteps } from '@/__swaps__/screens/Swap/hooks/useSwapNavigation'; import { useSwapContext } from '@/__swaps__/screens/Swap/providers/swap-provider'; import { inputKeys } from '@/__swaps__/types/swap'; @@ -65,7 +65,7 @@ export function SwapInputValuesCaret({ inputCaretType, disabled }: { inputCaretT const selectedAsset = inputCaretType === 'inputAmount' || inputCaretType === 'inputNativeValue' ? internalSelectedInputAsset : internalSelectedOutputAsset; return { - backgroundColor: getColorValueForThemeWorklet(selectedAsset.value?.highContrastColor, isDarkMode, true), + backgroundColor: getColorValueForThemeWorklet(selectedAsset.value?.highContrastColor, isDarkMode), }; }); diff --git a/src/__swaps__/screens/Swap/components/SwapNativeInput.tsx b/src/__swaps__/screens/Swap/components/SwapNativeInput.tsx index 7432d641750..a3e9338e550 100644 --- a/src/__swaps__/screens/Swap/components/SwapNativeInput.tsx +++ b/src/__swaps__/screens/Swap/components/SwapNativeInput.tsx @@ -6,7 +6,7 @@ import Animated, { runOnJS, useAnimatedStyle, useDerivedValue } from 'react-nati import { SwapInputValuesCaret } from '@/__swaps__/screens/Swap/components/SwapInputValuesCaret'; import { GestureHandlerButton } from '@/__swaps__/screens/Swap/components/GestureHandlerButton'; import { useSwapContext } from '@/__swaps__/screens/Swap/providers/swap-provider'; -import { equalWorklet } from '@/__swaps__/safe-math/SafeMath'; +import { equalWorklet } from '@/safe-math/SafeMath'; export function SwapNativeInput({ nativeInputType, diff --git a/src/__swaps__/screens/Swap/components/SwapNumberPad.tsx b/src/__swaps__/screens/Swap/components/SwapNumberPad.tsx index b9b9a5286a4..7f3269be7ed 100644 --- a/src/__swaps__/screens/Swap/components/SwapNumberPad.tsx +++ b/src/__swaps__/screens/Swap/components/SwapNumberPad.tsx @@ -13,7 +13,7 @@ import Animated, { } from 'react-native-reanimated'; import { supportedNativeCurrencies } from '@/references'; import { Bleed, Box, Columns, HitSlop, Separator, Text, useColorMode, useForegroundColor } from '@/design-system'; -import { equalWorklet } from '@/__swaps__/safe-math/SafeMath'; +import { equalWorklet } from '@/safe-math/SafeMath'; import { stripNonDecimalNumbers } from '@/__swaps__/utils/swaps'; import { CUSTOM_KEYBOARD_HEIGHT, diff --git a/src/__swaps__/screens/Swap/components/SwapSlider.tsx b/src/__swaps__/screens/Swap/components/SwapSlider.tsx index 42581fd1087..a845fb8b25c 100644 --- a/src/__swaps__/screens/Swap/components/SwapSlider.tsx +++ b/src/__swaps__/screens/Swap/components/SwapSlider.tsx @@ -22,7 +22,7 @@ import { SPRING_CONFIGS, TIMING_CONFIGS } from '@/components/animations/animatio import { AnimatedText, Bleed, Box, Column, Columns, Inline, globalColors, useColorMode, useForegroundColor } from '@/design-system'; import { IS_IOS } from '@/env'; import { triggerHapticFeedback } from '@/screens/points/constants'; -import { greaterThanWorklet } from '@/__swaps__/safe-math/SafeMath'; +import { greaterThanWorklet } from '@/safe-math/SafeMath'; import { SCRUBBER_WIDTH, SLIDER_COLLAPSED_HEIGHT, @@ -90,18 +90,18 @@ export const SwapSlider = ({ const colors = useDerivedValue(() => ({ inactiveColorLeft: opacityWorklet( dualColor - ? getColorValueForThemeWorklet(internalSelectedOutputAsset.value?.highContrastColor, isDarkMode, true) - : getColorValueForThemeWorklet(internalSelectedInputAsset.value?.highContrastColor, isDarkMode, true), + ? getColorValueForThemeWorklet(internalSelectedOutputAsset.value?.highContrastColor, isDarkMode) + : getColorValueForThemeWorklet(internalSelectedInputAsset.value?.highContrastColor, isDarkMode), 0.9 ), activeColorLeft: dualColor - ? getColorValueForThemeWorklet(internalSelectedOutputAsset.value?.highContrastColor, isDarkMode, true) - : getColorValueForThemeWorklet(internalSelectedInputAsset.value?.highContrastColor, isDarkMode, true), + ? getColorValueForThemeWorklet(internalSelectedOutputAsset.value?.highContrastColor, isDarkMode) + : getColorValueForThemeWorklet(internalSelectedInputAsset.value?.highContrastColor, isDarkMode), inactiveColorRight: dualColor - ? opacityWorklet(getColorValueForThemeWorklet(internalSelectedInputAsset.value?.highContrastColor, isDarkMode, true), 0.9) + ? opacityWorklet(getColorValueForThemeWorklet(internalSelectedInputAsset.value?.highContrastColor, isDarkMode), 0.9) : separatorSecondary, activeColorRight: dualColor - ? getColorValueForThemeWorklet(internalSelectedInputAsset.value?.highContrastColor, isDarkMode, true) + ? getColorValueForThemeWorklet(internalSelectedInputAsset.value?.highContrastColor, isDarkMode) : fillSecondary, })); diff --git a/src/__swaps__/screens/Swap/components/TokenList/TokenToBuyList.tsx b/src/__swaps__/screens/Swap/components/TokenList/TokenToBuyList.tsx index 749800f3339..cf2fc29c7e2 100644 --- a/src/__swaps__/screens/Swap/components/TokenList/TokenToBuyList.tsx +++ b/src/__swaps__/screens/Swap/components/TokenList/TokenToBuyList.tsx @@ -7,7 +7,8 @@ import { ChainId } from '@/state/backendNetworks/types'; import { SearchAsset } from '@/__swaps__/types/search'; import { SwapAssetType } from '@/__swaps__/types/swap'; import { parseSearchAsset } from '@/__swaps__/utils/assets'; -import { getChainColorWorklet, getStandardizedUniqueIdWorklet } from '@/__swaps__/utils/swaps'; +import { getChainColorWorklet } from '@/__swaps__/utils/swaps'; +import { getUniqueId } from '@/utils/ethereumUtils'; import { analyticsV2 } from '@/analytics'; import { AnimatedTextIcon } from '@/components/AnimatedComponents/AnimatedTextIcon'; import { TIMING_CONFIGS } from '@/components/animations/animationConfigs'; @@ -99,10 +100,7 @@ export const TokenToBuyList = () => { const handleSelectToken = useCallback( (token: SearchAsset) => { runOnUI(() => { - if ( - internalSelectedInputAsset.value && - getStandardizedUniqueIdWorklet({ address: token.address, chainId: token.chainId }) !== internalSelectedOutputAsset.value?.uniqueId - ) { + if (internalSelectedInputAsset.value && getUniqueId(token.address, token.chainId) !== internalSelectedOutputAsset.value?.uniqueId) { isQuoteStale.value = 1; isFetching.value = true; } @@ -154,11 +152,11 @@ export const TokenToBuyList = () => { return ( + } ListFooterComponent={} - ListHeaderComponent={} contentContainerStyle={{ paddingBottom: 16 }} data={sections} getItemLayout={getItemLayout} diff --git a/src/__swaps__/screens/Swap/components/TokenList/TokenToSellList.tsx b/src/__swaps__/screens/Swap/components/TokenList/TokenToSellList.tsx index 5b12988476b..653897d423b 100644 --- a/src/__swaps__/screens/Swap/components/TokenList/TokenToSellList.tsx +++ b/src/__swaps__/screens/Swap/components/TokenList/TokenToSellList.tsx @@ -4,7 +4,7 @@ import { ListEmpty } from '@/__swaps__/screens/Swap/components/TokenList/ListEmp import { useSwapContext } from '@/__swaps__/screens/Swap/providers/swap-provider'; import { ParsedSearchAsset } from '@/__swaps__/types/assets'; import { SwapAssetType } from '@/__swaps__/types/swap'; -import { getStandardizedUniqueIdWorklet } from '@/__swaps__/utils/swaps'; +import { getUniqueId } from '@/utils/ethereumUtils'; import { analyticsV2 } from '@/analytics'; import { useDelayedMount } from '@/hooks/useDelayedMount'; import * as i18n from '@/languages'; @@ -45,10 +45,7 @@ const TokenToSellListComponent = () => { if (!token) return; runOnUI(() => { - if ( - internalSelectedOutputAsset.value && - getStandardizedUniqueIdWorklet({ address: token.address, chainId: token.chainId }) !== internalSelectedInputAsset.value?.uniqueId - ) { + if (internalSelectedOutputAsset.value && getUniqueId(token.address, token.chainId) !== internalSelectedInputAsset.value?.uniqueId) { isQuoteStale.value = 1; isFetching.value = true; } @@ -86,27 +83,29 @@ const TokenToSellListComponent = () => { }); return ( - } - ListFooterComponent={} - ListHeaderComponent={} - contentContainerStyle={{ paddingBottom: 16 }} - data={userAssetIds} - getItemLayout={getItemLayout} - keyExtractor={uniqueId => uniqueId} - renderItem={({ item: uniqueId }) => { - return handleSelectToken(asset)} output={false} uniqueId={uniqueId} />; - }} - renderScrollComponent={props => { - return ( - - ); - }} - style={{ height: EXPANDED_INPUT_HEIGHT - 77, width: DEVICE_WIDTH - 24 }} - /> + <> + + } + ListFooterComponent={} + contentContainerStyle={{ paddingBottom: 16 }} + data={userAssetIds} + getItemLayout={getItemLayout} + keyExtractor={uniqueId => uniqueId} + renderItem={({ item: uniqueId }) => { + return handleSelectToken(asset)} output={false} uniqueId={uniqueId} />; + }} + renderScrollComponent={props => { + return ( + + ); + }} + style={{ height: EXPANDED_INPUT_HEIGHT - 77, width: DEVICE_WIDTH - 24 }} + /> + ); }; diff --git a/src/__swaps__/screens/Swap/constants.ts b/src/__swaps__/screens/Swap/constants.ts index 2ccb5d877c5..22ff49ca2d6 100644 --- a/src/__swaps__/screens/Swap/constants.ts +++ b/src/__swaps__/screens/Swap/constants.ts @@ -1,4 +1,4 @@ -import { gweiToWei } from '@/__swaps__/utils/ethereum'; +import { gweiToWei } from '@/parsers'; import { getDefaultKeyboardHeight } from '@/redux/keyboardHeight'; import { deviceUtils, safeAreaInsetValues } from '@/utils'; import { Easing, WithSpringConfig, WithTimingConfig } from 'react-native-reanimated'; diff --git a/src/__swaps__/screens/Swap/dummyValues.ts b/src/__swaps__/screens/Swap/dummyValues.ts deleted file mode 100644 index d20740cfee6..00000000000 --- a/src/__swaps__/screens/Swap/dummyValues.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Network } from '@/state/backendNetworks/types'; - -// /---- 🗑️ TODO: Delete these dummy values 🗑️ ----/ // -// -// 1. Colors -export const DAI_COLOR = '#F0B340'; -export const SOCKS_COLOR = '#E15EE5'; -export const USDC_COLOR = '#2775CA'; - -// 2. Addresses -export const ETH_ADDRESS = 'eth'; -export const DAI_ADDRESS = '0x6B175474E89094C44Da98b954EedeAC495271d0F'; -export const SOCKS_ADDRESS = '0x23B608675a2B2fB1890d3ABBd85c5775c51691d5'; -export const USDC_ADDRESS = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'; - -// 3. Input Config -export const INPUT_SYMBOL = 'ETH'; -export const INPUT_ADDRESS = ETH_ADDRESS; -// export const INPUT_COLOR = ETH_COLOR_DARK; -export const INPUT_NETWORK = Network.base; -export const INPUT_ASSET_BALANCE = 12.4; -// export const INPUT_ASSET_BALANCE = 12.42512485; -export const INPUT_ASSET_USD_PRICE = 2632.4; -export const IS_INPUT_STABLECOIN = false; - -// 4. Output Config -// export const OUTPUT_SYMBOL = 'USDC'; -// export const OUTPUT_ADDRESS = USDC_ADDRESS; -// export const OUTPUT_COLOR = USDC_COLOR; -// export const OUTPUT_NETWORK = Network.base; -// export const OUTPUT_ASSET_USD_PRICE = 0.999; -// export const IS_OUTPUT_STABLECOIN = true; -export const OUTPUT_SYMBOL = 'SOCKS'; -export const OUTPUT_ADDRESS = SOCKS_ADDRESS; -export const OUTPUT_COLOR = SOCKS_COLOR; -export const OUTPUT_NETWORK = Network.base; -export const OUTPUT_ASSET_USD_PRICE = 41963.7; -export const IS_OUTPUT_STABLECOIN = false; - -// 5. Swap Config -export const SWAP_FEE = 0.0085; -// -// /---- END dummy values ----/ // diff --git a/src/__swaps__/screens/Swap/hooks/useAnimatedSwapStyles.ts b/src/__swaps__/screens/Swap/hooks/useAnimatedSwapStyles.ts index 19313195039..82b200227eb 100644 --- a/src/__swaps__/screens/Swap/hooks/useAnimatedSwapStyles.ts +++ b/src/__swaps__/screens/Swap/hooks/useAnimatedSwapStyles.ts @@ -161,7 +161,7 @@ export function useAnimatedSwapStyles({ }); const outputAssetColor = useDerivedValue(() => { - return getColorValueForThemeWorklet(internalSelectedOutputAsset.value?.highContrastColor, isDarkMode, true); + return getColorValueForThemeWorklet(internalSelectedOutputAsset.value?.highContrastColor, isDarkMode); }); const swapActionWrapperStyle = useAnimatedStyle(() => { @@ -202,13 +202,13 @@ export function useAnimatedSwapStyles({ const assetToSellIconStyle = useAnimatedStyle(() => { return { - backgroundColor: getColorValueForThemeWorklet(internalSelectedInputAsset.value?.color, isDarkMode, true), + backgroundColor: getColorValueForThemeWorklet(internalSelectedInputAsset.value?.color, isDarkMode), }; }); const assetToBuyIconStyle = useAnimatedStyle(() => { return { - backgroundColor: getColorValueForThemeWorklet(internalSelectedOutputAsset.value?.color, isDarkMode, true), + backgroundColor: getColorValueForThemeWorklet(internalSelectedOutputAsset.value?.color, isDarkMode), }; }); @@ -221,7 +221,7 @@ export function useAnimatedSwapStyles({ const searchInputAssetButtonStyle = useAnimatedStyle(() => { return { - color: getColorValueForThemeWorklet(internalSelectedInputAsset.value?.highContrastColor, isDarkMode, true), + color: getColorValueForThemeWorklet(internalSelectedInputAsset.value?.highContrastColor, isDarkMode), }; }); @@ -233,7 +233,7 @@ export function useAnimatedSwapStyles({ const color = isPasteMode.value ? foregroundColors.blue : internalSelectedOutputAsset.value?.highContrastColor; return { - color: getColorValueForThemeWorklet(color, isDarkMode, true), + color: getColorValueForThemeWorklet(color, isDarkMode), }; }); @@ -257,11 +257,11 @@ export function useAnimatedSwapStyles({ const searchInputAssetButtonWrapperStyle = useAnimatedStyle(() => { return { backgroundColor: opacityWorklet( - getColorValueForThemeWorklet(internalSelectedInputAsset.value?.highContrastColor, isDarkMode, true), + getColorValueForThemeWorklet(internalSelectedInputAsset.value?.highContrastColor, isDarkMode), isDarkMode ? 0.1 : 0.08 ), borderColor: opacityWorklet( - getColorValueForThemeWorklet(internalSelectedInputAsset.value?.highContrastColor, isDarkMode, true), + getColorValueForThemeWorklet(internalSelectedInputAsset.value?.highContrastColor, isDarkMode), isDarkMode ? 0.06 : 0.01 ), }; @@ -276,9 +276,9 @@ export function useAnimatedSwapStyles({ return { backgroundColor: isPasteMode.value ? 'transparent' - : opacityWorklet(getColorValueForThemeWorklet(color, isDarkMode, true), isDarkMode ? 0.1 : 0.08), + : opacityWorklet(getColorValueForThemeWorklet(color, isDarkMode), isDarkMode ? 0.1 : 0.08), borderColor: opacityWorklet( - getColorValueForThemeWorklet(color, isDarkMode, true), + getColorValueForThemeWorklet(color, isDarkMode), isDarkMode ? darkModeBorderOpacity : lightModeBorderOpacity ), }; diff --git a/src/__swaps__/screens/Swap/hooks/useAssetsToBuy.ts b/src/__swaps__/screens/Swap/hooks/useAssetsToBuy.ts deleted file mode 100644 index b2fd3713a51..00000000000 --- a/src/__swaps__/screens/Swap/hooks/useAssetsToBuy.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { useMemo } from 'react'; -import { useSearchCurrencyLists } from '@/__swaps__/screens/Swap/hooks/useSearchCurrencyLists'; - -export const useAssetsToBuySections = () => { - const { results: searchAssetsToBuySections } = useSearchCurrencyLists(); - - return useMemo(() => searchAssetsToBuySections, [searchAssetsToBuySections]); -}; diff --git a/src/__swaps__/screens/Swap/hooks/useAssetsToSell.ts b/src/__swaps__/screens/Swap/hooks/useAssetsToSell.ts deleted file mode 100644 index e78faa661c4..00000000000 --- a/src/__swaps__/screens/Swap/hooks/useAssetsToSell.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { useMemo } from 'react'; - -import { - selectUserAssetsList, - selectUserAssetsListByChainId, - selectorFilterByUserChains, -} from '@/__swaps__/screens/Swap/resources/_selectors/assets'; -import { useUserAssets } from '@/__swaps__/screens/Swap/resources/assets'; -import { ParsedAssetsDictByChain, ParsedSearchAsset, UserAssetFilter } from '@/__swaps__/types/assets'; -import { useAccountSettings, useDebounce } from '@/hooks'; -import { useUserAssetsStore } from '@/state/assets/userAssets'; - -const sortBy = (by: UserAssetFilter) => { - switch (by) { - case 'all': - return selectUserAssetsList; - default: - return (data: ParsedAssetsDictByChain) => selectUserAssetsListByChainId(data, by); - } -}; - -export const useAssetsToSell = () => { - const { accountAddress: currentAddress, nativeCurrency: currentCurrency } = useAccountSettings(); - - const { filter, searchQuery } = useUserAssetsStore(state => ({ - filter: state.filter, - searchQuery: state.inputSearchQuery, - })); - - const debouncedAssetToSellFilter = useDebounce(searchQuery, 200); - - const { data: userAssets = [] } = useUserAssets( - { - address: currentAddress, - currency: currentCurrency, - }, - { - select: data => - selectorFilterByUserChains({ - data, - selector: sortBy(filter), - }), - } - ); - - const filteredAssetsToSell = useMemo(() => { - return debouncedAssetToSellFilter - ? userAssets.filter(({ name, symbol, address }) => - [name, symbol, address].reduce( - (res, param) => res || param.toLowerCase().startsWith(debouncedAssetToSellFilter.toLowerCase()), - false - ) - ) - : userAssets; - }, [debouncedAssetToSellFilter, userAssets]) as ParsedSearchAsset[]; - - return filteredAssetsToSell; -}; diff --git a/src/__swaps__/screens/Swap/hooks/useEstimatedGasFee.ts b/src/__swaps__/screens/Swap/hooks/useEstimatedGasFee.ts index 2adc5c8fd89..8582dc2b018 100644 --- a/src/__swaps__/screens/Swap/hooks/useEstimatedGasFee.ts +++ b/src/__swaps__/screens/Swap/hooks/useEstimatedGasFee.ts @@ -1,6 +1,6 @@ import { ChainId } from '@/state/backendNetworks/types'; -import { weiToGwei } from '@/__swaps__/utils/ethereum'; -import { convertAmountToNativeDisplayWorklet, formatNumber, multiply } from '@/__swaps__/utils/numbers'; +import { weiToGwei } from '@/parsers'; +import { convertAmountToNativeDisplayWorklet, formatNumber, multiply } from '@/helpers/utilities'; import { useNativeAsset } from '@/utils/ethereumUtils'; import { useMemo } from 'react'; import { formatUnits } from 'viem'; diff --git a/src/__swaps__/screens/Swap/hooks/useNativeAssetForChain.ts b/src/__swaps__/screens/Swap/hooks/useNativeAssetForChain.ts deleted file mode 100644 index 66a61acc05f..00000000000 --- a/src/__swaps__/screens/Swap/hooks/useNativeAssetForChain.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { useCallback } from 'react'; - -import { ExtendedAnimatedAssetWithColors } from '@/__swaps__/types/assets'; -import { ChainId } from '@/state/backendNetworks/types'; - -import { SharedValue, runOnJS, useAnimatedReaction, useDerivedValue, useSharedValue } from 'react-native-reanimated'; -import { ParsedAddressAsset } from '@/entities'; -import { ethereumUtils } from '@/utils'; -import { swapsStore } from '@/state/swaps/swapsStore'; - -export const useNativeAssetForChain = ({ inputAsset }: { inputAsset: SharedValue }) => { - const chainId = useDerivedValue(() => inputAsset.value?.chainId ?? swapsStore.getState().preferredNetwork ?? ChainId.mainnet); - const nativeAsset = useSharedValue(ethereumUtils.getNetworkNativeAsset({ chainId: chainId.value })); - - const getNativeAssetForNetwork = useCallback( - (chainId: ChainId) => { - const asset = ethereumUtils.getNetworkNativeAsset({ chainId }); - nativeAsset.value = asset; - }, - [nativeAsset] - ); - - useAnimatedReaction( - () => chainId.value, - (currentChainId, previousChainId) => { - if (currentChainId !== previousChainId) { - runOnJS(getNativeAssetForNetwork)(currentChainId); - } - }, - [] - ); - - return { - nativeAsset, - }; -}; diff --git a/src/__swaps__/screens/Swap/hooks/useSearchCurrencyLists.ts b/src/__swaps__/screens/Swap/hooks/useSearchCurrencyLists.ts index d85dd24011d..4033417ed62 100644 --- a/src/__swaps__/screens/Swap/hooks/useSearchCurrencyLists.ts +++ b/src/__swaps__/screens/Swap/hooks/useSearchCurrencyLists.ts @@ -1,13 +1,11 @@ import { TokenSearchResult, useTokenSearch } from '@/__swaps__/screens/Swap/resources/search/search'; -import { AddressOrEth } from '@/__swaps__/types/assets'; import { ChainId } from '@/state/backendNetworks/types'; import { SearchAsset, TokenSearchAssetKey, TokenSearchThreshold } from '@/__swaps__/types/search'; -import { addHexPrefix } from '@/__swaps__/utils/hex'; -import { isLowerCaseMatch } from '@/__swaps__/utils/strings'; -import { getStandardizedUniqueIdWorklet } from '@/__swaps__/utils/swaps'; +import { addHexPrefix } from '@/handlers/web3'; +import { isLowerCaseMatch, filterList } from '@/utils'; +import { getUniqueId } from '@/utils/ethereumUtils'; import { useFavorites } from '@/resources/favorites'; import { useSwapsStore } from '@/state/swaps/swapsStore'; -import { filterList } from '@/utils'; import { isAddress } from '@ethersproject/address'; import { rankings } from 'match-sorter'; import { useCallback, useMemo, useState } from 'react'; @@ -348,10 +346,7 @@ export function useSearchCurrencyLists() { chainId: state.toChainId, favorite: true, mainnetAddress: favToken.networks?.[ChainId.mainnet]?.address || favToken.mainnet_address, - uniqueId: getStandardizedUniqueIdWorklet({ - address: (favToken.networks[state.toChainId]?.address || favToken.address) as AddressOrEth, - chainId: state.toChainId, - }), + uniqueId: getUniqueId(favToken.networks[state.toChainId]?.address || favToken.address, state.toChainId), })) as SearchAsset[]; }, [favorites, state.toChainId]); diff --git a/src/__swaps__/screens/Swap/hooks/useSwapInputStyles.ts b/src/__swaps__/screens/Swap/hooks/useSwapInputStyles.ts index 783c2e4fe16..3654817ede5 100644 --- a/src/__swaps__/screens/Swap/hooks/useSwapInputStyles.ts +++ b/src/__swaps__/screens/Swap/hooks/useSwapInputStyles.ts @@ -35,7 +35,7 @@ export const useSwapInputStyles = ({ const bgColor = useDerivedValue(() => { return isDarkMode - ? opacityWorklet(getColorValueForThemeWorklet(asset.value?.highContrastColor, isDarkMode, true), 0.08) + ? opacityWorklet(getColorValueForThemeWorklet(asset.value?.highContrastColor, isDarkMode), 0.08) : opacityWorklet(globalColors.white100, 0.8); }); @@ -46,9 +46,9 @@ export const useSwapInputStyles = ({ const strokeColor = useDerivedValue(() => { return isDarkMode ? opacityWorklet( - getColorValueForThemeWorklet(asset.value?.highContrastColor, isDarkMode, true) === ETH_COLOR_DARK + getColorValueForThemeWorklet(asset.value?.highContrastColor, isDarkMode) === ETH_COLOR_DARK ? ETH_COLOR_DARK_ACCENT - : getColorValueForThemeWorklet(asset.value?.highContrastColor, isDarkMode, true), + : getColorValueForThemeWorklet(asset.value?.highContrastColor, isDarkMode), 0.06 ) : globalColors.white100; @@ -56,7 +56,7 @@ export const useSwapInputStyles = ({ const expandedStrokeColor = useDerivedValue(() => { return isDarkMode - ? opacityWorklet(getColorValueForThemeWorklet(asset.value?.highContrastColor, isDarkMode, true), 0.1) + ? opacityWorklet(getColorValueForThemeWorklet(asset.value?.highContrastColor, isDarkMode), 0.1) : globalColors.white100; }); @@ -78,7 +78,7 @@ export const useSwapInputStyles = ({ otherInputProgress.value === NavigationSteps.SEARCH_FOCUSED ? withTiming(0, TIMING_CONFIGS.fadeConfig) : withTiming(1, TIMING_CONFIGS.fadeConfig), - shadowColor: isDarkMode ? 'transparent' : getColorValueForThemeWorklet(asset.value?.mixedShadowColor, isDarkMode, true), + shadowColor: isDarkMode ? 'transparent' : getColorValueForThemeWorklet(asset.value?.mixedShadowColor, isDarkMode), transform: [ { translateY: withSpring(getContainerStyleTranslateY(progress, bottomInput), SPRING_CONFIGS.keyboardConfig), diff --git a/src/__swaps__/screens/Swap/hooks/useSwapInputsController.ts b/src/__swaps__/screens/Swap/hooks/useSwapInputsController.ts index e9e7fca5e67..6eef58993b2 100644 --- a/src/__swaps__/screens/Swap/hooks/useSwapInputsController.ts +++ b/src/__swaps__/screens/Swap/hooks/useSwapInputsController.ts @@ -1,11 +1,4 @@ -import { - divWorklet, - equalWorklet, - greaterThanWorklet, - isNumberStringWorklet, - mulWorklet, - toFixedWorklet, -} from '@/__swaps__/safe-math/SafeMath'; +import { divWorklet, equalWorklet, greaterThanWorklet, isNumberStringWorklet, mulWorklet, toFixedWorklet } from '@/safe-math/SafeMath'; import { SCRUBBER_WIDTH, SLIDER_WIDTH, snappySpringConfig } from '@/__swaps__/screens/Swap/constants'; import { ExtendedAnimatedAssetWithColors } from '@/__swaps__/types/assets'; import { ChainId } from '@/state/backendNetworks/types'; @@ -16,7 +9,7 @@ import { convertAmountToNativeDisplayWorklet, convertRawAmountToDecimalFormat, handleSignificantDecimalsWorklet, -} from '@/__swaps__/utils/numbers'; +} from '@/helpers/utilities'; import { addCommasToNumber, addSymbolToNativeDisplayWorklet, diff --git a/src/__swaps__/screens/Swap/hooks/useSwapTextStyles.ts b/src/__swaps__/screens/Swap/hooks/useSwapTextStyles.ts index be633447abb..e8ef1b63085 100644 --- a/src/__swaps__/screens/Swap/hooks/useSwapTextStyles.ts +++ b/src/__swaps__/screens/Swap/hooks/useSwapTextStyles.ts @@ -14,7 +14,7 @@ import { ETH_COLOR_DARK, ETH_COLOR_DARK_ACCENT, pulsingConfig } from '@/__swaps_ import { inputMethods, inputValuesType } from '@/__swaps__/types/swap'; import { getColorValueForThemeWorklet, opacity } from '@/__swaps__/utils/swaps'; import { ExtendedAnimatedAssetWithColors } from '@/__swaps__/types/assets'; -import { equalWorklet } from '@/__swaps__/safe-math/SafeMath'; +import { equalWorklet } from '@/safe-math/SafeMath'; import { SPRING_CONFIGS, TIMING_CONFIGS } from '@/components/animations/animationConfigs'; export function useSwapTextStyles({ @@ -87,12 +87,12 @@ export function useSwapTextStyles({ }); const inputAssetColor = useDerivedValue(() => { - const color = getColorValueForThemeWorklet(internalSelectedInputAsset.value?.highContrastColor, isDarkMode, true); + const color = getColorValueForThemeWorklet(internalSelectedInputAsset.value?.highContrastColor, isDarkMode); return color === ETH_COLOR_DARK ? ETH_COLOR_DARK_ACCENT : color; }); const outputAssetColor = useDerivedValue(() => { - const color = getColorValueForThemeWorklet(internalSelectedOutputAsset.value?.highContrastColor, isDarkMode, true); + const color = getColorValueForThemeWorklet(internalSelectedOutputAsset.value?.highContrastColor, isDarkMode); return color === ETH_COLOR_DARK ? ETH_COLOR_DARK_ACCENT : color; }); diff --git a/src/__swaps__/screens/Swap/hooks/useSwapWarning.ts b/src/__swaps__/screens/Swap/hooks/useSwapWarning.ts index bc9c7611a53..3af47091b59 100644 --- a/src/__swaps__/screens/Swap/hooks/useSwapWarning.ts +++ b/src/__swaps__/screens/Swap/hooks/useSwapWarning.ts @@ -7,9 +7,9 @@ import { CrosschainQuote, Quote, QuoteError } from '@rainbow-me/swaps'; import { getCrossChainTimeEstimateWorklet, getQuoteServiceTimeWorklet } from '@/__swaps__/utils/swaps'; import { ExtendedAnimatedAssetWithColors } from '@/__swaps__/types/assets'; import { highPriceImpactThreshold, severePriceImpactThreshold } from '@/__swaps__/screens/Swap/constants'; -import { divWorklet, greaterThanOrEqualToWorklet, subWorklet } from '@/__swaps__/safe-math/SafeMath'; +import { divWorklet, greaterThanOrEqualToWorklet, subWorklet } from '@/safe-math/SafeMath'; import { inputValuesType } from '@/__swaps__/types/swap'; -import { convertAmountToNativeDisplayWorklet } from '@/__swaps__/utils/numbers'; +import { convertAmountToNativeDisplayWorklet } from '@/helpers/utilities'; export enum SwapWarningType { unknown = 'unknown', diff --git a/src/__swaps__/screens/Swap/providers/SyncSwapStateAndSharedValues.tsx b/src/__swaps__/screens/Swap/providers/SyncSwapStateAndSharedValues.tsx index 353745931c8..beee83e8b3e 100644 --- a/src/__swaps__/screens/Swap/providers/SyncSwapStateAndSharedValues.tsx +++ b/src/__swaps__/screens/Swap/providers/SyncSwapStateAndSharedValues.tsx @@ -11,7 +11,7 @@ import { sumWorklet, toFixedWorklet, toScaledIntegerWorklet, -} from '@/__swaps__/safe-math/SafeMath'; +} from '@/safe-math/SafeMath'; import { ExtendedAnimatedAssetWithColors } from '@/__swaps__/types/assets'; import { ChainId } from '@/state/backendNetworks/types'; import { ParsedAddressAsset } from '@/entities'; diff --git a/src/__swaps__/screens/Swap/providers/swap-provider.tsx b/src/__swaps__/screens/Swap/providers/swap-provider.tsx index 261af64bc75..3789dcd6ffe 100644 --- a/src/__swaps__/screens/Swap/providers/swap-provider.tsx +++ b/src/__swaps__/screens/Swap/providers/swap-provider.tsx @@ -14,7 +14,7 @@ import { useSharedValue, } from 'react-native-reanimated'; -import { equalWorklet, lessThanOrEqualToWorklet, sumWorklet } from '@/__swaps__/safe-math/SafeMath'; +import { equalWorklet, lessThanOrEqualToWorklet, sumWorklet } from '@/safe-math/SafeMath'; import { INITIAL_SLIDER_POSITION, SLIDER_COLLAPSED_HEIGHT, SLIDER_HEIGHT, SLIDER_WIDTH } from '@/__swaps__/screens/Swap/constants'; import { useAnimatedSwapStyles } from '@/__swaps__/screens/Swap/hooks/useAnimatedSwapStyles'; import { useSwapInputsController } from '@/__swaps__/screens/Swap/hooks/useSwapInputsController'; @@ -414,8 +414,6 @@ export const SwapProvider = ({ children }: SwapProviderProps) => { const isNativeWrapOrUnwrap = quoteData.swapType === SwapType.wrap || quoteData.swapType === SwapType.unwrap; - // Do not deleeeet the comment below 😤 - // About to get quote const parameters: Omit, 'gasParams' | 'gasFeeParamsBySpeed' | 'selectedGasFee'> = { sellAmount: quoteData.sellAmount?.toString(), buyAmount: quoteData.buyAmount?.toString(), diff --git a/src/__swaps__/screens/Swap/resources/_selectors/assets.ts b/src/__swaps__/screens/Swap/resources/_selectors/assets.ts index 4c764ecdfbd..390045d369c 100644 --- a/src/__swaps__/screens/Swap/resources/_selectors/assets.ts +++ b/src/__swaps__/screens/Swap/resources/_selectors/assets.ts @@ -1,7 +1,7 @@ import { ParsedAssetsDict, ParsedAssetsDictByChain, ParsedUserAsset, UniqueId } from '@/__swaps__/types/assets'; import { ChainId } from '@/state/backendNetworks/types'; -import { deriveAddressAndChainWithUniqueId } from '@/__swaps__/utils/address'; -import { add } from '@/__swaps__/utils/numbers'; +import { getAddressAndChainIdFromUniqueId } from '@/utils/ethereumUtils'; +import { add } from '@/helpers/utilities'; // selectors export function selectorFilterByUserChains({ @@ -34,14 +34,6 @@ export function selectUserAssetsDictByChain(assets: ParsedAssetsDictByChain) { return assets; } -export function selectUserAssetsListByChainId(assets: ParsedAssetsDictByChain, chainId: ChainId) { - const assetsForNetwork = assets?.[chainId]; - - return Object.values(assetsForNetwork).sort( - (a: ParsedUserAsset, b: ParsedUserAsset) => parseFloat(b?.native?.balance?.amount) - parseFloat(a?.native?.balance?.amount) - ); -} - export function selectUserAssetAddressMapByChainId(assets: ParsedAssetsDictByChain) { const mapAddresses = (list: ParsedAssetsDict = {}) => Object.values(list).map(i => i.address); return { @@ -59,8 +51,8 @@ export function selectUserAssetAddressMapByChainId(assets: ParsedAssetsDictByCha // selector generators export function selectUserAssetWithUniqueId(uniqueId: UniqueId) { return (assets: ParsedAssetsDictByChain) => { - const { chain } = deriveAddressAndChainWithUniqueId(uniqueId); - return assets?.[chain]?.[uniqueId]; + const { chainId } = getAddressAndChainIdFromUniqueId(uniqueId); + return assets?.[chainId]?.[uniqueId]; }; } diff --git a/src/__swaps__/screens/Swap/resources/assets/assets.ts b/src/__swaps__/screens/Swap/resources/assets/assets.ts deleted file mode 100644 index 1177df0402d..00000000000 --- a/src/__swaps__/screens/Swap/resources/assets/assets.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; - -import { requestMetadata } from '@/graphql'; -import { QueryConfigWithSelect, QueryFunctionArgs, QueryFunctionResult, createQueryKey, queryClient } from '@/react-query'; -import { SupportedCurrencyKey } from '@/references'; -import { AddressOrEth, AssetMetadata, ParsedAsset, UniqueId } from '@/__swaps__/types/assets'; -import { ChainId } from '@/state/backendNetworks/types'; -import { chunkArray, createAssetQuery, parseAssetMetadata } from '@/__swaps__/utils/assets'; -import { RainbowError, logger } from '@/logger'; -export const ASSETS_TIMEOUT_DURATION = 10000; -const ASSETS_REFETCH_INTERVAL = 60000; - -// /////////////////////////////////////////////// -// Query Types - -export type AssetsQueryArgs = { - assetAddresses: AddressOrEth[]; - chainId: ChainId; - currency: SupportedCurrencyKey; -}; - -// /////////////////////////////////////////////// -// Query Key - -const assetsQueryKey = ({ assetAddresses, chainId, currency }: AssetsQueryArgs) => - createQueryKey('assets', { assetAddresses, chainId, currency }, { persisterVersion: 2 }); - -type AssetsQueryKey = ReturnType; - -// /////////////////////////////////////////////// -// Query Function - -export async function assetsQueryFunction({ - queryKey: [{ assetAddresses, chainId, currency }], -}: QueryFunctionArgs): Promise<{ - [key: UniqueId]: ParsedAsset; -}> { - try { - if (!assetAddresses || !assetAddresses.length) return {}; - const batches = chunkArray([...assetAddresses], 10); // chunking because a full batch would throw 413 - const batchQueries = batches.map(batchedQuery => createAssetQuery(batchedQuery, chainId, currency, true), { - timeout: ASSETS_TIMEOUT_DURATION, - }); - - const batchResults = batchQueries.map(query => requestMetadata(query)) as Promise[]>[]; - const results = (await Promise.all(batchResults)) - .flat() - .map(r => Object.values(r)) - .flat(); - const parsedAssets = parseAssets(results, chainId, currency); - return parsedAssets; - } catch (e) { - logger.error(new RainbowError('[assetsQueryFunction]: Failed to fetch assets'), { - message: (e as Error)?.message, - }); - return {}; - } -} - -type AssetsQueryResult = QueryFunctionResult; - -// /////////////////////////////////////////////// -// Query Fetcher - -export async function fetchAssets( - { assetAddresses, chainId, currency }: AssetsQueryArgs, - config: QueryConfigWithSelect = {} -) { - return await queryClient.fetchQuery(assetsQueryKey({ assetAddresses, chainId, currency }), assetsQueryFunction, config); -} - -function parseAssets(assets: AssetMetadata[], chainId: ChainId, currency: SupportedCurrencyKey) { - return assets.reduce( - (assetsDict, asset) => { - const address = asset.networks?.[chainId]?.address; - if (address) { - const parsedAsset = parseAssetMetadata({ - address, - asset, - chainId, - currency, - }); - assetsDict[parsedAsset?.uniqueId] = parsedAsset; - } - return assetsDict; - }, - {} as Record - ); -} - -// /////////////////////////////////////////////// -// Query Hook - -export function useAssets( - { assetAddresses, chainId, currency }: AssetsQueryArgs, - config: QueryConfigWithSelect = {} -) { - return useQuery(assetsQueryKey({ assetAddresses, chainId, currency }), assetsQueryFunction, { - ...config, - refetchInterval: ASSETS_REFETCH_INTERVAL, - }); -} diff --git a/src/__swaps__/screens/Swap/resources/assets/index.ts b/src/__swaps__/screens/Swap/resources/assets/index.ts index f485a083154..37014e65e00 100644 --- a/src/__swaps__/screens/Swap/resources/assets/index.ts +++ b/src/__swaps__/screens/Swap/resources/assets/index.ts @@ -1,5 +1,3 @@ -export { useAssets } from './assets'; export { useUserAssets } from './userAssets'; export type { UserAssetsArgs } from './userAssets'; -export { useUserAssetsByChain } from './userAssetsByChain'; export type { UserAssetsByChainArgs } from './userAssetsByChain'; diff --git a/src/__swaps__/screens/Swap/resources/assets/userAssets.ts b/src/__swaps__/screens/Swap/resources/assets/userAssets.ts index e2523ceb985..c724db135af 100644 --- a/src/__swaps__/screens/Swap/resources/assets/userAssets.ts +++ b/src/__swaps__/screens/Swap/resources/assets/userAssets.ts @@ -11,7 +11,7 @@ import { ParsedAssetsDictByChain, ZerionAsset } from '@/__swaps__/types/assets'; import { ChainId } from '@/state/backendNetworks/types'; import { AddressAssetsReceivedMessage } from '@/__swaps__/types/refraction'; import { parseUserAsset } from '@/__swaps__/utils/assets'; -import { greaterThan } from '@/__swaps__/utils/numbers'; +import { greaterThan } from '@/helpers/utilities'; import { fetchUserAssetsByChain } from './userAssetsByChain'; import { fetchHardhatBalancesByChainId } from '@/resources/assets/hardhatAssets'; @@ -124,12 +124,14 @@ async function userAssetsQueryFunction({ const chainIdsWithErrorsInResponse = res?.data?.meta?.chain_ids_with_errors || []; const assets = res?.data?.payload?.assets || []; if (address) { - userAssetsQueryFunctionRetryByChain({ - address, - chainIds: chainIdsWithErrorsInResponse, - currency, - testnetMode, - }); + if (chainIdsWithErrorsInResponse.length) { + userAssetsQueryFunctionRetryByChain({ + address, + chainIds: chainIdsWithErrorsInResponse, + currency, + testnetMode, + }); + } if (assets.length && chainIdsInResponse.length) { const parsedAssetsDict = await parseUserAssets({ assets, @@ -186,9 +188,11 @@ async function userAssetsQueryFunctionRetryByChain({ } const parsedRetries = await Promise.all(retries); for (const parsedAssets of parsedRetries) { - const values = Object.values(parsedAssets); - if (values[0]) { - cachedUserAssets[values[0].chainId] = parsedAssets; + if (parsedAssets) { + const values = Object.values(parsedAssets); + if (values[0]) { + cachedUserAssets[values[0].chainId] = parsedAssets; + } } } queryClient.setQueryData(userAssetsQueryKey({ address, currency, testnetMode }), cachedUserAssets); @@ -236,9 +240,9 @@ export function useUserAssets( config: QueryConfigWithSelect = {} ) { const { connectedToHardhat } = useConnectedToHardhatStore(); - return useQuery(userAssetsQueryKey({ address, currency, testnetMode: connectedToHardhat }), userAssetsQueryFunction, { ...config, + enabled: !!address && !!currency, refetchInterval: USER_ASSETS_REFETCH_INTERVAL, staleTime: process.env.IS_TESTING === 'true' ? 0 : 1000, }); diff --git a/src/__swaps__/screens/Swap/resources/assets/userAssetsByChain.ts b/src/__swaps__/screens/Swap/resources/assets/userAssetsByChain.ts index 9fb15647109..723334cce51 100644 --- a/src/__swaps__/screens/Swap/resources/assets/userAssetsByChain.ts +++ b/src/__swaps__/screens/Swap/resources/assets/userAssetsByChain.ts @@ -1,4 +1,3 @@ -import { useQuery } from '@tanstack/react-query'; import { Address } from 'viem'; import { QueryConfigWithSelect, QueryFunctionArgs, QueryFunctionResult, createQueryKey, queryClient } from '@/react-query'; @@ -12,8 +11,6 @@ import { parseUserAssets, userAssetsQueryKey } from './userAssets'; import { RainbowFetchClient } from '@/rainbow-fetch'; import { ADDYS_API_KEY } from 'react-native-dotenv'; -const USER_ASSETS_REFETCH_INTERVAL = 60000; - const addysHttp = new RainbowFetchClient({ baseURL: 'https://addys.p.rainbow.me/v3', headers: { @@ -59,15 +56,19 @@ export async function fetchUserAssetsByChain): Promise> { const cache = queryClient.getQueryCache(); const cachedUserAssets = (cache.find(userAssetsQueryKey({ address, currency }))?.state?.data || {}) as ParsedAssetsDictByChain; - const cachedDataForChain = cachedUserAssets?.[chainId]; + const cachedDataForChain = cachedUserAssets?.[chainId] || {}; try { - const url = `/${chainId}/${address}/assets/?currency=${currency.toLowerCase()}`; - const res = await addysHttp.get(url); + const url = `/${chainId}/${address}/assets`; + const res = await addysHttp.get(url, { + params: { + currency: currency.toLowerCase(), + }, + }); const chainIdsInResponse = res?.data?.meta?.chain_ids || []; const assets = res?.data?.payload?.assets || []; if (assets.length && chainIdsInResponse.length) { @@ -90,24 +91,3 @@ export async function userAssetsByChainQueryFunction({ } type UserAssetsByChainResult = QueryFunctionResult; - -// /////////////////////////////////////////////// -// Query Hook - -export function useUserAssetsByChain( - { address, chainId, currency }: UserAssetsByChainArgs, - config: QueryConfigWithSelect = {} -) { - return useQuery( - userAssetsByChainQueryKey({ - address, - chainId, - currency, - }), - userAssetsByChainQueryFunction, - { - ...config, - refetchInterval: USER_ASSETS_REFETCH_INTERVAL, - } - ); -} diff --git a/src/__swaps__/screens/Swap/resources/search/utils.ts b/src/__swaps__/screens/Swap/resources/search/utils.ts index 5691c7b4f42..975c0c3e8f4 100644 --- a/src/__swaps__/screens/Swap/resources/search/utils.ts +++ b/src/__swaps__/screens/Swap/resources/search/utils.ts @@ -1,31 +1,7 @@ import { ChainId } from '@/state/backendNetworks/types'; import { SearchAsset } from '@/__swaps__/types/search'; -import { - ARBITRUM_ETH_ADDRESS, - AVAX_AVALANCHE_ADDRESS, - BASE_ETH_ADDRESS, - BLAST_ETH_ADDRESS, - BNB_BSC_ADDRESS, - DEGEN_CHAIN_DEGEN_ADDRESS, - ETH_ADDRESS, - MATIC_POLYGON_ADDRESS, - OPTIMISM_ETH_ADDRESS, - ZORA_ETH_ADDRESS, -} from '@/references'; import { Address } from 'viem'; - -const NATIVE_ASSET_UNIQUE_IDS = new Set([ - `${ETH_ADDRESS}_${ChainId.mainnet}`, - `${OPTIMISM_ETH_ADDRESS}_${ChainId.optimism}`, - `${ARBITRUM_ETH_ADDRESS}_${ChainId.arbitrum}`, - `${BNB_BSC_ADDRESS}_${ChainId.bsc}`, - `${MATIC_POLYGON_ADDRESS}_${ChainId.polygon}`, - `${BASE_ETH_ADDRESS}_${ChainId.base}`, - `${ZORA_ETH_ADDRESS}_${ChainId.zora}`, - `${AVAX_AVALANCHE_ADDRESS}_${ChainId.avalanche}`, - `${BLAST_ETH_ADDRESS}_${ChainId.blast}`, - `${DEGEN_CHAIN_DEGEN_ADDRESS}_${ChainId.degen}`, -]); +import { isNativeAsset } from '@/handlers/assets'; export function parseTokenSearch(assets: SearchAsset[], chainId?: ChainId): SearchAsset[] { const results: SearchAsset[] = []; @@ -43,7 +19,7 @@ export function parseTokenSearch(assets: SearchAsset[], chainId?: ChainId): Sear address, chainId, decimals: networkInfo ? networkInfo.decimals : asset.decimals, - isNativeAsset: NATIVE_ASSET_UNIQUE_IDS.has(uniqueId), + isNativeAsset: isNativeAsset(address, chainId), mainnetAddress: mainnetInfo ? mainnetInfo.address : chainId === ChainId.mainnet ? address : ('' as Address), uniqueId, }); @@ -63,7 +39,7 @@ export function parseTokenSearch(assets: SearchAsset[], chainId?: ChainId): Sear address, chainId: networkChainId, decimals: networkInfo ? networkInfo.decimals : asset.decimals, - isNativeAsset: NATIVE_ASSET_UNIQUE_IDS.has(uniqueId), + isNativeAsset: isNativeAsset(address, networkChainId), mainnetAddress: mainnetInfo ? mainnetInfo.address : networkChainId === ChainId.mainnet ? address : ('' as Address), uniqueId, }); diff --git a/src/__swaps__/utils/__tests__/numbers.test.ts b/src/__swaps__/utils/__tests__/numbers.test.ts index faf3be13c6b..7c4675acc04 100644 --- a/src/__swaps__/utils/__tests__/numbers.test.ts +++ b/src/__swaps__/utils/__tests__/numbers.test.ts @@ -1,4 +1,4 @@ -import { convertAmountToNativeDisplayWorklet } from '../numbers'; +import { convertAmountToNativeDisplayWorklet } from '@/helpers/utilities'; import { supportedCurrencies } from '@/references/supportedCurrencies'; const testCases = [ diff --git a/src/__swaps__/utils/address.ts b/src/__swaps__/utils/address.ts deleted file mode 100644 index a3b3e40c978..00000000000 --- a/src/__swaps__/utils/address.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Address } from 'viem'; - -import { AddressOrEth, UniqueId } from '@/__swaps__/types/assets'; -import { ChainId } from '@/state/backendNetworks/types'; - -export function truncateAddress(address?: AddressOrEth) { - if (!address) return ''; - return `${address?.slice(0, 6)}…${address?.slice(-4)}`; -} - -export function deriveAddressAndChainWithUniqueId(uniqueId: UniqueId) { - const fragments = uniqueId.split('_'); - const address = fragments[0] as Address; - const chain = parseInt(fragments[1], 10) as ChainId; - return { - address, - chain, - }; -} diff --git a/src/__swaps__/utils/assets.ts b/src/__swaps__/utils/assets.ts index c87c5f62995..98e45ff44e4 100644 --- a/src/__swaps__/utils/assets.ts +++ b/src/__swaps__/utils/assets.ts @@ -1,6 +1,4 @@ -import { AddressZero } from '@ethersproject/constants'; - -import { ETH_ADDRESS, SupportedCurrencyKey } from '@/references'; +import { SupportedCurrencyKey } from '@/references'; import { AddressOrEth, AssetApiResponse, @@ -17,41 +15,25 @@ import { ChainId, ChainName } from '@/state/backendNetworks/types'; import * as i18n from '@/languages'; import { SearchAsset } from '@/__swaps__/types/search'; -import { customChainIdsToAssetNames, isNativeAsset } from '@/__swaps__/utils/chains'; +import { isNativeAsset } from '@/handlers/assets'; import { convertAmountAndPriceToNativeDisplay, convertAmountToBalanceDisplay, convertAmountToNativeDisplayWorklet, convertAmountToPercentageDisplay, convertRawAmountToDecimalFormat, -} from '@/__swaps__/utils/numbers'; -import { isLowerCaseMatch, isLowerCaseMatchWorklet } from '@/__swaps__/utils/strings'; +} from '@/helpers/utilities'; +import { isLowerCaseMatch } from '@/utils'; import { useBackendNetworksStore } from '@/state/backendNetworks/backendNetworks'; export const isSameAsset = (a1: Pick, a2: Pick) => +a1.chainId === +a2.chainId && isLowerCaseMatch(a1.address, a2.address); -export const isSameAssetWorklet = (a1: Pick, a2: Pick) => { - 'worklet'; - return +a1.chainId === +a2.chainId && isLowerCaseMatchWorklet(a1.address, a2.address); -}; - const get24HrChange = (priceData?: ZerionAssetPrice) => { const twentyFourHrChange = priceData?.relative_change_24h; return twentyFourHrChange ? convertAmountToPercentageDisplay(twentyFourHrChange) : ''; }; -export const getCustomChainIconUrl = (chainId: ChainId, address: AddressOrEth) => { - if (!chainId || !customChainIdsToAssetNames[chainId]) return ''; - const baseUrl = 'https://raw.githubusercontent.com/rainbow-me/assets/master/blockchains/'; - - if (address === AddressZero || address === ETH_ADDRESS) { - return `${baseUrl}${customChainIdsToAssetNames[chainId]}/info/logo.png`; - } else { - return `${baseUrl}${customChainIdsToAssetNames[chainId]}/assets/${address}/logo.png`; - } -}; - export const getNativeAssetPrice = ({ priceData, currency }: { priceData?: ZerionAssetPrice; currency: SupportedCurrencyKey }) => { const priceUnit = priceData?.value; return { @@ -127,7 +109,7 @@ export function parseAsset({ asset, currency }: { asset: ZerionAsset | AssetApiR symbol: asset.symbol, type: asset.type, decimals: asset.decimals, - icon_url: asset.icon_url || getCustomChainIconUrl(chainId, address), + icon_url: asset.icon_url, colors: asset.colors, standard, ...('networks' in asset && { networks: asset.networks }), @@ -310,42 +292,3 @@ export const parseSearchAsset = ({ colors: userAsset?.colors || assetWithPrice?.colors || searchAsset?.colors, type: userAsset?.type || assetWithPrice?.type || searchAsset?.type, }); - -const assetQueryFragment = ( - address: AddressOrEth, - chainId: ChainId, - currency: SupportedCurrencyKey, - index: number, - withPrice?: boolean -) => { - const priceQuery = withPrice ? 'price { value relativeChange24h }' : ''; - return `Q${index}: token(address: "${address}", chainID: ${chainId}, currency: "${currency}") { - colors { - primary - fallback - shadow - } - decimals - iconUrl - name - networks - symbol - ${priceQuery} - }`; -}; - -export const chunkArray = (arr: TItem[], chunkSize: number) => { - const result = []; - - for (let i = 0; i < arr.length; i += chunkSize) { - result.push(arr.slice(i, i + chunkSize)); - } - - return result; -}; - -export const createAssetQuery = (addresses: AddressOrEth[], chainId: ChainId, currency: SupportedCurrencyKey, withPrice?: boolean) => { - return `{ - ${addresses.map((a, i) => assetQueryFragment(a, chainId, currency, i, withPrice)).join(',')} - }`; -}; diff --git a/src/__swaps__/utils/chains.ts b/src/__swaps__/utils/chains.ts deleted file mode 100644 index 688e85a0743..00000000000 --- a/src/__swaps__/utils/chains.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { celo, fantom, harmonyOne, moonbeam } from 'viem/chains'; -import { AddressOrEth } from '@/__swaps__/types/assets'; -import { ChainId } from '@/state/backendNetworks/types'; -import { isLowerCaseMatch } from '@/__swaps__/utils/strings'; -import { useBackendNetworksStore } from '@/state/backendNetworks/backendNetworks'; - -// @ts-expect-error Property '[ChainId.hardhat]' is missing -export const customChainIdsToAssetNames: Record = { - 42170: 'arbitrumnova', - 1313161554: 'aurora', - 43114: 'avalanchex', - 81457: 'blast', - 168587773: 'blastsepolia', - 288: 'boba', - 42220: 'celo', - 61: 'classic', - 25: 'cronos', - 2000: 'dogechain', - 250: 'fantom', - 314: 'filecoin', - 1666600000: 'harmony', - 13371: 'immutablezkevm', - 2222: 'kavaevm', - 8217: 'klaytn', - 59144: 'linea', - 957: 'lyra', - 169: 'manta', - 5000: 'mantle', - 1088: 'metis', - 34443: 'mode', - 1284: 'moonbeam', - 7700: 'nativecanto', - 204: 'opbnb', - 11297108109: 'palm', - 424: 'pgn', - 1101: 'polygonzkevm', - 369: 'pulsechain', - 1380012617: 'rari', - 1918988905: 'raritestnet', - 17001: 'redstoneholesky', - 534352: 'scroll', - 100: 'xdai', - 324: 'zksync', -}; - -export function isNativeAsset(address: AddressOrEth, chainId: ChainId) { - return isLowerCaseMatch(useBackendNetworksStore.getState().getChainsNativeAsset()[chainId].address, address); -} - -export const chainIdToUse = (connectedToHardhat: boolean, connectedToHardhatOp: boolean, activeSessionChainId: number) => { - if (connectedToHardhat) { - return ChainId.hardhat; - } - if (connectedToHardhatOp) { - return ChainId.hardhatOptimism; - } - return activeSessionChainId; -}; - -export const deriveChainIdByHostname = (hostname: string) => { - switch (hostname) { - case 'etherscan.io': - return ChainId.mainnet; - case 'arbiscan.io': - return ChainId.arbitrum; - case 'explorer-mumbai.maticvigil.com': - case 'explorer-mumbai.matic.today': - case 'mumbai.polygonscan.com': - return ChainId.polygonMumbai; - case 'polygonscan.com': - return ChainId.polygon; - case 'optimistic.etherscan.io': - return ChainId.optimism; - case 'bscscan.com': - return ChainId.bsc; - case 'ftmscan.com': - return fantom.id; - case 'explorer.celo.org': - return celo.id; - case 'explorer.harmony.one': - return harmonyOne.id; - case 'explorer.avax.network': - case 'subnets.avax.network': - case 'snowtrace.io': - return ChainId.avalanche; - case 'subnets-test.avax.network': - case 'testnet.snowtrace.io': - return ChainId.avalancheFuji; - case 'moonscan.io': - return moonbeam.id; - case 'explorer.holesky.redstone.xyz': - return 17001; - case 'blastscan.io': - return ChainId.blast; - case 'testnet.blastscan.io': - return 168587773; - default: - return ChainId.mainnet; - } -}; diff --git a/src/__swaps__/utils/decimalFormatter.ts b/src/__swaps__/utils/decimalFormatter.ts index fc0f9a943ec..437a707d150 100644 --- a/src/__swaps__/utils/decimalFormatter.ts +++ b/src/__swaps__/utils/decimalFormatter.ts @@ -7,7 +7,7 @@ import { toFixedWorklet, orderOfMagnitudeWorklet, significantDecimalsWorklet, -} from '../safe-math/SafeMath'; +} from '@/safe-math/SafeMath'; const MAXIMUM_SIGNIFICANT_DECIMALS = 6; const STABLECOIN_MINIMUM_SIGNIFICANT_DECIMALS = 2; diff --git a/src/__swaps__/utils/ethereum.ts b/src/__swaps__/utils/ethereum.ts deleted file mode 100644 index d929152b3f2..00000000000 --- a/src/__swaps__/utils/ethereum.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { isAddress } from '@ethersproject/address'; -import { Mnemonic, isValidMnemonic } from '@ethersproject/hdnode'; -import { parseEther } from '@ethersproject/units'; -import { Address } from 'viem'; -import { EthereumPrivateKey } from '@/model/wallet'; -import { ethUnits } from '@/references'; - -import { addHexPrefix, isHexStringIgnorePrefix } from '@/__swaps__/utils/hex'; -import { divide, multiply } from '@/__swaps__/utils/numbers'; - -export type EthereumWalletSeed = EthereumPrivateKey | Mnemonic['phrase']; -export enum EthereumWalletType { - mnemonic = 'mnemonic', - privateKey = 'privateKey', - readOnly = 'readOnly', - seed = 'seed', - ledgerPublicKey = 'ledgerPublicKey', - trezorPublicKey = 'trezorPublicKey', -} - -const validTLDs = ['eth', 'xyz', 'luxe', 'kred', 'reverse', 'addr', 'test']; -export const isENSAddressFormat = (name: string) => { - if (!name) return false; - const tld = name.split('.').at(-1); - if (!tld || tld === name) return false; - return validTLDs.includes(tld.toLowerCase()); -}; - -/** - * @desc Checks if a string is a valid private key. - * @param value The string. - * @return Whether or not the string is a valid private key string. - */ -export const isValidPrivateKey = (value: string): boolean => { - return isHexStringIgnorePrefix(value) && addHexPrefix(value).length === 66; -}; - -export const identifyWalletType = (walletSeed: EthereumWalletSeed): EthereumWalletType => { - if (isValidPrivateKey(walletSeed)) { - return EthereumWalletType.privateKey; - } - // 12 or 24 words seed phrase - if (isValidMnemonic(walletSeed)) { - return EthereumWalletType.mnemonic; - } - // Public address (0x) - if (isAddress(walletSeed)) { - return EthereumWalletType.readOnly; - } - // seed - return EthereumWalletType.seed; -}; - -/** - * @desc Checks if a an address has previous transactions - * @param {String} address - * @return {Promise} - */ -export const hasPreviousTransactions = async (address: Address): Promise => { - try { - const url = `https://aha.rainbow.me/?address=${address}`; - const response = await fetch(url); - if (!response.ok) { - return false; - } - - const parsedResponse = (await response.json()) as { - data: { addresses: Record }; - }; - - return parsedResponse?.data?.addresses[address.toLowerCase()] === true; - } catch (e) { - return false; - } -}; - -export const gweiToWei = (gweiAmount: string) => { - const weiAmount = multiply(gweiAmount, ethUnits.gwei); - return weiAmount; -}; - -export const weiToGwei = (weiAmount: string) => { - const gweiAmount = divide(weiAmount, ethUnits.gwei); - return gweiAmount; -}; - -export const toWei = (ether: string): string => { - const result = parseEther(ether); - return result.toString(); -}; - -// This function removes all the keys from the message that are not present in the types -// preventing a know phising attack where the signature process could allow malicious DApps -// to trick users into signing an EIP-712 object different from the one presented -// in the signature approval preview. Consequently, users were at risk of unknowingly -// transferring control of their ERC-20 tokens, NFTs, etc to adversaries by signing -// hidden Permit messages. - -// For more info read https://www.coinspect.com/wallet-EIP-712-injection-vulnerability/ - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const sanitizeTypedData = (data: any) => { - if (data.types[data.primaryType].length > 0) { - // Extract all the valid permit types for the primary type - const permitPrimaryTypes: string[] = data.types[data.primaryType].map((type: { name: string; type: string }) => type.name); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const sanitizedMessage: any = {}; - // Extract all the message keys that matches the valid permit types - Object.keys(data.message).forEach(key => { - if (permitPrimaryTypes.includes(key)) { - sanitizedMessage[key] = data.message[key]; - } - }); - - const sanitizedData = { - ...data, - message: sanitizedMessage, - }; - - return sanitizedData; - } - return data; -}; diff --git a/src/__swaps__/utils/flipAssets.ts b/src/__swaps__/utils/flipAssets.ts index 3947b7b546d..683ee713139 100644 --- a/src/__swaps__/utils/flipAssets.ts +++ b/src/__swaps__/utils/flipAssets.ts @@ -3,7 +3,7 @@ import { inputKeys, inputMethods, inputValuesType } from '@/__swaps__/types/swap import { valueBasedDecimalFormatter } from '@/__swaps__/utils/decimalFormatter'; import { niceIncrementFormatter } from '@/__swaps__/utils/swaps'; import { ExtendedAnimatedAssetWithColors } from '@/__swaps__/types/assets'; -import { divWorklet, equalWorklet, greaterThanWorklet, mulWorklet } from '@/__swaps__/safe-math/SafeMath'; +import { divWorklet, equalWorklet, greaterThanWorklet, mulWorklet } from '@/safe-math/SafeMath'; export function getInputValuesForSliderPositionWorklet({ selectedInputAsset, diff --git a/src/__swaps__/utils/gasUtils.ts b/src/__swaps__/utils/gasUtils.ts index 6c8f12e5715..58a106eb5c5 100644 --- a/src/__swaps__/utils/gasUtils.ts +++ b/src/__swaps__/utils/gasUtils.ts @@ -14,9 +14,10 @@ import { ParsedAsset } from '@/__swaps__/types/assets'; import { ChainId } from '@/state/backendNetworks/types'; import { BlocksToConfirmation, GasFeeLegacyParams, GasFeeParam, GasFeeParams, GasSpeed } from '@/__swaps__/types/gas'; -import { gweiToWei, weiToGwei } from '@/__swaps__/utils/ethereum'; -import { addHexPrefix, convertStringToHex, toHex } from '@/__swaps__/utils/hex'; +import { gweiToWei, weiToGwei } from '@/parsers'; import { + convertStringToHex, + lessThan, add, addBuffer, convertAmountAndPriceToNativeDisplay, @@ -24,11 +25,11 @@ import { divide, fraction, greaterThan, - lessThan, multiply, -} from '@/__swaps__/utils/numbers'; -import { getMinimalTimeUnitStringForMs } from '@/__swaps__/utils/time'; +} from '@/helpers/utilities'; +import { addHexPrefix, toHex } from '@/handlers/web3'; import { MeteorologyLegacyResponse, MeteorologyResponse } from '@/entities/gas'; +import { getMinimalTimeUnitStringForMs } from '@/helpers/time'; export const FLASHBOTS_MIN_TIP = 6; diff --git a/src/__swaps__/utils/hex.ts b/src/__swaps__/utils/hex.ts deleted file mode 100644 index 8b00190bdd7..00000000000 --- a/src/__swaps__/utils/hex.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { isHexString } from '@ethersproject/bytes'; -import BigNumber from 'bignumber.js'; -import { startsWith } from 'lodash'; - -export type BigNumberish = number | string | BigNumber; - -/** - * @desc Checks if a hex string, ignoring prefixes and suffixes. - * @param value The string. - * @return Whether or not the string is a hex string. - */ -export const isHexStringIgnorePrefix = (value: string): boolean => { - if (!value) return false; - const trimmedValue = value.trim(); - const updatedValue = addHexPrefix(trimmedValue); - return isHexString(updatedValue); -}; - -/** - * @desc Adds an "0x" prefix to a string if one is not present. - * @param value The starting string. - * @return The prefixed string. - */ -export const addHexPrefix = (value: string): string => (startsWith(value, '0x') ? value : `0x${value}`); - -export const addHexPrefixWorklet = (value: string): string => { - 'worklet'; - return startsWith(value, '0x') ? value : `0x${value}`; -}; - -export const convertStringToHex = (stringToConvert: string): string => new BigNumber(stringToConvert).toString(16); - -export const toHex = (stringToConvert: string): string => addHexPrefix(convertStringToHex(stringToConvert)); - -export const toHexNoLeadingZeros = (value: string): string => toHex(value).replace(/^0x0*/, '0x'); diff --git a/src/__swaps__/utils/meteorology.ts b/src/__swaps__/utils/meteorology.ts index 4e9d52afb6c..3b710cad24e 100644 --- a/src/__swaps__/utils/meteorology.ts +++ b/src/__swaps__/utils/meteorology.ts @@ -11,8 +11,8 @@ import { MIN_FLASHBOTS_PRIORITY_FEE } from '../screens/Swap/constants'; import { GasSettings } from '../screens/Swap/hooks/useCustomGas'; import { getSelectedGasSpeed, useGasSettings } from '../screens/Swap/hooks/useSelectedGas'; import { GasSpeed } from '../types/gas'; -import { getMinimalTimeUnitStringForMs } from './time'; import { MeteorologyLegacyResponse, MeteorologyResponse } from '@/entities/gas'; +import { getMinimalTimeUnitStringForMs } from '@/helpers/time'; // Query Types @@ -53,11 +53,7 @@ export async function fetchMeteorology( export function useMeteorology( { chainId }: MeteorologyArgs, - { - select, - enabled, - notifyOnChangeProps = ['data'], - }: { select?: (data: MeteorologyResult) => Selected; enabled?: boolean; notifyOnChangeProps?: 'data'[] } + { select, enabled }: { select?: (data: MeteorologyResult) => Selected; enabled?: boolean } ) { return useQuery(meteorologyQueryKey({ chainId }), meteorologyQueryFunction, { select, @@ -65,7 +61,6 @@ export function useMeteorology( refetchInterval: 12_000, // 12 seconds staleTime: 12_000, // 12 seconds cacheTime: Infinity, - notifyOnChangeProps, }); } @@ -189,7 +184,6 @@ export function useMeteorologySuggestions({ chainId, enabled }: { chainId: Chain { select: useCallback((data: MeteorologyResult) => selectGasSuggestions(data, flashbots), [flashbots]), enabled, - notifyOnChangeProps: enabled ? ['data'] : [], } ); } @@ -199,13 +193,11 @@ export function useMeteorologySuggestion({ speed, enabled, select = s => s as Selected, - notifyOnChangeProps = ['data'], }: { chainId: ChainId; speed: GasSpeed; enabled?: boolean; select?: (d: GasSuggestion | undefined) => Selected; - notifyOnChangeProps?: ['data'] | []; }) { const flashbots = useSwapsStore(s => chainId === ChainId.mainnet && s.flashbots); return useMeteorology( @@ -216,7 +208,6 @@ export function useMeteorologySuggestion({ [select, speed, flashbots] ), enabled: enabled && speed !== 'custom', - notifyOnChangeProps, } ); } diff --git a/src/__swaps__/utils/numbers.ts b/src/__swaps__/utils/numbers.ts deleted file mode 100644 index c6c3e11a115..00000000000 --- a/src/__swaps__/utils/numbers.ts +++ /dev/null @@ -1,379 +0,0 @@ -import { BigNumber as EthersBigNumber } from '@ethersproject/bignumber'; -import BigNumber from 'bignumber.js'; -import currency from 'currency.js'; -import { isNil } from 'lodash'; - -import { supportedNativeCurrencies } from '@/references'; -import { BigNumberish } from '@/__swaps__/utils/hex'; -import { divWorklet, lessThanWorklet, orderOfMagnitudeWorklet, powWorklet } from '../safe-math/SafeMath'; - -type nativeCurrencyType = typeof supportedNativeCurrencies; - -export const toBigNumber = (v?: string | number | BigNumber) => (v ? EthersBigNumber.from(v) : undefined); - -export const abs = (value: BigNumberish): string => new BigNumber(value).abs().toFixed(); - -export const isPositive = (value: BigNumberish): boolean => new BigNumber(value).isPositive(); - -export const subtract = (numberOne: BigNumberish, numberTwo: BigNumberish): string => - new BigNumber(numberOne).minus(new BigNumber(numberTwo)).toFixed(); - -export const convertAmountToRawAmount = (value: BigNumberish, decimals: number | string): string => - new BigNumber(value).times(new BigNumber(10).pow(decimals)).toFixed(); - -export const isZero = (value: BigNumberish): boolean => new BigNumber(value).isZero(); - -export const toFixedDecimals = (value: BigNumberish, decimals: number): string => new BigNumber(value).toFixed(decimals); - -export const convertNumberToString = (value: BigNumberish): string => new BigNumber(value).toFixed(); - -export const greaterThan = (numberOne: BigNumberish, numberTwo: BigNumberish): boolean => new BigNumber(numberOne).gt(numberTwo); - -export const greaterThanOrEqualTo = (numberOne: BigNumberish, numberTwo: BigNumberish): boolean => new BigNumber(numberOne).gte(numberTwo); - -export const isEqual = (numberOne: BigNumberish, numberTwo: BigNumberish): boolean => new BigNumber(numberOne).eq(numberTwo); - -export const formatFixedDecimals = (value: BigNumberish, decimals: number): string => { - const _value = convertNumberToString(value); - const _decimals = convertStringToNumber(decimals); - return new BigNumber(new BigNumber(_value).toFixed(_decimals)).toFixed(); -}; - -export const mod = (numberOne: BigNumberish, numberTwo: BigNumberish): string => - new BigNumber(numberOne).mod(new BigNumber(numberTwo)).toFixed(); - -/** - * @desc real floor divides two numbers - * @param {Number} numberOne - * @param {Number} numberTwo - * @return {String} - */ -export const floorDivide = (numberOne: BigNumberish, numberTwo: BigNumberish): string => - new BigNumber(numberOne).dividedToIntegerBy(new BigNumber(numberTwo)).toFixed(); - -/** - * @desc count value's number of decimals places - * @param {String} value - * @return {String} - */ -export const countDecimalPlaces = (value: BigNumberish): number => new BigNumber(value).dp(); - -/** - * @desc update the amount to display precision - * equivalent to ~0.01 of the native price - * or use most significant decimal - * if the updated precision amounts to zero - * @param {String} amount - * @param {String} nativePrice - * @param {Boolean} use rounding up mode - * @return {String} updated amount - */ -export const updatePrecisionToDisplay = (amount: BigNumberish, nativePrice?: BigNumberish, roundUp = false): string => { - if (!amount) return '0'; - const roundingMode = roundUp ? BigNumber.ROUND_UP : BigNumber.ROUND_DOWN; - if (!nativePrice) return new BigNumber(amount).decimalPlaces(6, roundingMode).toFixed(); - const bnAmount = new BigNumber(amount); - const significantDigitsOfNativePriceInteger = new BigNumber(nativePrice).decimalPlaces(0, BigNumber.ROUND_DOWN).sd(true); - const truncatedPrecision = new BigNumber(significantDigitsOfNativePriceInteger).plus(2, 10).toNumber(); - const truncatedAmount = bnAmount.decimalPlaces(truncatedPrecision, BigNumber.ROUND_DOWN); - return truncatedAmount.isZero() - ? new BigNumber(bnAmount.toPrecision(1, roundingMode)).toFixed() - : bnAmount.decimalPlaces(truncatedPrecision, roundingMode).toFixed(); -}; - -/** - * @desc format inputOne value to signficant decimals given inputTwo - * @param {String} inputOne - * @param {String} inputTwo - * @return {String} - */ -// TODO revisit logic, at least rename so it is not native amount dp -export const formatInputDecimals = (inputOne: BigNumberish, inputTwo: BigNumberish): string => { - const _nativeAmountDecimalPlaces = countDecimalPlaces(inputTwo); - const decimals = _nativeAmountDecimalPlaces > 8 ? _nativeAmountDecimalPlaces : 8; - const result = new BigNumber(formatFixedDecimals(inputOne, decimals)).toFormat().replace(/,/g, ''); - return result; -}; - -export const add = (numberOne: BigNumberish, numberTwo: BigNumberish): string => new BigNumber(numberOne).plus(numberTwo).toFixed(); - -export const minus = (numberOne: BigNumberish, numberTwo: BigNumberish): string => new BigNumber(numberOne).minus(numberTwo).toFixed(); - -export const addDisplay = (numberOne: string, numberTwo: string): string => { - const unit = numberOne.replace(/[\d.-]/g, ''); - const leftAlignedUnit = numberOne.indexOf(unit) === 0; - return currency(0, { symbol: unit, pattern: leftAlignedUnit ? '!#' : '#!' }) - .add(numberOne) - .add(numberTwo) - .format(); -}; - -export const multiply = (numberOne: BigNumberish, numberTwo: BigNumberish): string => new BigNumber(numberOne).times(numberTwo).toFixed(); - -export const addBuffer = (numberOne: BigNumberish, buffer: BigNumberish = '1.2'): string => - new BigNumber(numberOne).times(buffer).toFixed(0); - -export const divide = (numberOne: BigNumberish, numberTwo: BigNumberish): string => { - if (!(numberOne || numberTwo)) return '0'; - return new BigNumber(numberOne).dividedBy(numberTwo).toFixed(); -}; - -export const fraction = (target: BigNumberish, numerator: BigNumberish, denominator: BigNumberish): string => { - if (!target || !numerator || !denominator) return '0'; - return new BigNumber(target).times(numerator).dividedBy(denominator).toFixed(0); -}; - -/** - * @desc convert to asset amount units from native price value units - * @param {String} value - * @param {Object} asset - * @param {Number} priceUnit - * @return {String} - */ -export const convertAmountFromNativeValue = (value: BigNumberish, priceUnit: BigNumberish, decimals = 18): string => { - if (isNil(priceUnit) || isZero(priceUnit)) return '0'; - return new BigNumber(new BigNumber(value).dividedBy(priceUnit).toFixed(decimals, BigNumber.ROUND_DOWN)).toFixed(); -}; - -export const convertStringToNumber = (value: BigNumberish) => new BigNumber(value).toNumber(); - -export const lessThan = (numberOne: BigNumberish, numberTwo: BigNumberish): boolean => new BigNumber(numberOne).lt(numberTwo); - -export const lessOrEqualThan = (numberOne: BigNumberish, numberTwo: BigNumberish): boolean => - new BigNumber(numberOne).lt(numberTwo) || new BigNumber(numberOne).eq(numberTwo); - -export const handleSignificantDecimalsWithThreshold = (value: BigNumberish, decimals: number, threshold = '0.0001') => { - const result = toFixedDecimals(value, decimals); - return lessThan(result, threshold) ? `< ${threshold}` : result; -}; - -export const handleSignificantDecimalsWorklet = (value: number | string, decimals: number, buffer = 3): string => { - 'worklet'; - let dec; - - if (lessThanWorklet(value, 1)) { - const orderOfMagnitude = orderOfMagnitudeWorklet(value); - const sigDigitsWithBuffer = -orderOfMagnitude - 1 + buffer; - dec = Math.min(sigDigitsWithBuffer, 8); - } else { - dec = Math.min(decimals, buffer); - } - return Number(value).toLocaleString('en-US', { - useGrouping: true, - minimumFractionDigits: 2, - maximumFractionDigits: dec, - }); -}; - -export const handleSignificantDecimals = (value: BigNumberish, decimals: number, buffer = 3, skipDecimals = false): string => { - let dec; - if (lessThan(new BigNumber(value).abs(), 1)) { - dec = new BigNumber(value).toFixed()?.slice?.(2).search(/[^0]/g) + buffer; - dec = Math.min(dec, 8); - } else { - dec = Math.min(decimals, buffer); - } - const result = new BigNumber(new BigNumber(value).toFixed(dec)).toFixed(); - const resultBN = new BigNumber(result); - return resultBN.dp() <= 2 ? resultBN.toFormat(skipDecimals ? 0 : 2) : resultBN.toFormat(); -}; - -export const handleSignificantDecimalsAsNumber = (value: BigNumberish, decimals: number): string => { - return new BigNumber(new BigNumber(multiply(value, new BigNumber(10).pow(decimals))).toFixed(0)) - .dividedBy(new BigNumber(10).pow(decimals)) - .toFixed(); -}; - -/** - * @desc convert from asset BigNumber amount to native price BigNumber amount - */ -export const convertAmountToNativeAmount = (amount: BigNumberish, priceUnit: BigNumberish): string => multiply(amount, priceUnit); - -/** - * @desc convert from amount to display formatted string - */ -export const convertAmountAndPriceToNativeDisplay = ( - amount: BigNumberish, - priceUnit: BigNumberish, - nativeCurrency: keyof nativeCurrencyType, - useThreshold = false -): { amount: string; display: string } => { - const nativeBalanceRaw = convertAmountToNativeAmount(amount, priceUnit); - const nativeDisplay = convertAmountToNativeDisplayWorklet(nativeBalanceRaw, nativeCurrency, useThreshold); - return { - amount: nativeBalanceRaw, - display: nativeDisplay, - }; -}; - -/** - * @desc convert from raw amount to display formatted string - */ -export const convertRawAmountToNativeDisplay = ( - rawAmount: BigNumberish, - assetDecimals: number, - priceUnit: BigNumberish, - nativeCurrency: keyof nativeCurrencyType -) => { - const assetBalance = convertRawAmountToDecimalFormat(rawAmount, assetDecimals); - const ret = convertAmountAndPriceToNativeDisplay(assetBalance, priceUnit, nativeCurrency); - return ret; -}; - -/** - * @desc convert from raw amount to decimal format - */ -export const convertRawAmountToDecimalFormatWorklet = (value: number | string, decimals = 18): string => { - 'worklet'; - return divWorklet(value, powWorklet(10, decimals)); -}; - -/** - * @desc convert from amount value to display formatted string - */ -export const convertAmountToBalanceDisplayWorklet = ( - value: number | string, - asset: { decimals: number; symbol?: string }, - buffer?: number -) => { - 'worklet'; - const decimals = asset?.decimals ?? 18; - const display = handleSignificantDecimalsWorklet(value, decimals, buffer); - return `${display} ${asset?.symbol || ''}`; -}; - -/** - * @desc convert from raw amount to balance object - */ -export const convertRawAmountToBalanceWorklet = (value: number | string, asset: { decimals: number; symbol?: string }, buffer?: number) => { - 'worklet'; - const decimals = asset?.decimals ?? 18; - - const assetBalance = convertRawAmountToDecimalFormatWorklet(value, decimals); - - return { - amount: assetBalance, - display: convertAmountToBalanceDisplayWorklet(assetBalance, asset, buffer), - }; -}; - -/** - * @desc convert from raw amount to balance object - */ -export const convertRawAmountToBalance = (value: BigNumberish, asset: { decimals: number; symbol?: string }, buffer?: number) => { - const decimals = asset?.decimals ?? 18; - const assetBalance = convertRawAmountToDecimalFormat(value, decimals); - - return { - amount: assetBalance, - display: convertAmountToBalanceDisplay(assetBalance, asset, buffer), - }; -}; - -/** - * @desc convert from amount value to display formatted string - */ -export const convertAmountToBalanceDisplay = (value: BigNumberish, asset: { decimals: number; symbol?: string }, buffer?: number) => { - const decimals = asset?.decimals ?? 18; - const display = handleSignificantDecimals(value, decimals, buffer); - return `${display} ${asset?.symbol || ''}`; -}; - -/** - * @desc convert from amount to display formatted string - */ -export const convertAmountToPercentageDisplay = (value: BigNumberish, buffer?: number, skipDecimals?: boolean, decimals = 2): string => { - const display = handleSignificantDecimals(value, decimals, buffer, skipDecimals); - return `${display}%`; -}; - -/** - * @desc convert from amount to display formatted string - * with a threshold percent - */ -export const convertAmountToPercentageDisplayWithThreshold = (value: BigNumberish, decimals = 2, threshold = '0.0001'): string => { - if (lessThan(value, threshold)) { - return '< 0.01%'; - } else { - const display = new BigNumber(value).times(100).toFixed(decimals); - return `${display}%`; - } -}; - -/** - * @desc convert from bips amount to percentage format - */ -export const convertBipsToPercentage = (value: BigNumberish, decimals = 2): string => { - if (value === null) return '0'; - return new BigNumber(value || 0).shiftedBy(-2).toFixed(decimals); -}; - -/** - * @desc convert from amount value to display formatted string - */ -export const convertAmountToNativeDisplayWorklet = ( - value: number | string, - nativeCurrency: keyof nativeCurrencyType, - useThreshold = false, - ignoreAlignment = false -) => { - 'worklet'; - - const nativeSelected = supportedNativeCurrencies?.[nativeCurrency]; - const { alignment, decimals: rawDecimals, symbol } = nativeSelected; - const decimals = Math.min(rawDecimals, 6); - - const valueNumber = Number(value); - const threshold = decimals < 4 ? 0.01 : 0.0001; - let thresholdReached = false; - - if (useThreshold && valueNumber < threshold) { - thresholdReached = true; - } - - const nativeValue = thresholdReached - ? threshold - : valueNumber.toLocaleString('en-US', { - useGrouping: true, - minimumFractionDigits: nativeCurrency === 'ETH' ? undefined : decimals, - maximumFractionDigits: decimals, - }); - - const nativeDisplay = `${thresholdReached ? '<' : ''}${alignment === 'left' || ignoreAlignment ? symbol : ''}${nativeValue}${!ignoreAlignment && alignment === 'right' ? symbol : ''}`; - - return nativeDisplay; -}; - -/** - * @desc convert from raw amount to decimal format - */ -export const convertRawAmountToDecimalFormat = (value: BigNumberish, decimals = 18): string => - new BigNumber(value).dividedBy(new BigNumber(10).pow(decimals)).toFixed(); - -/** - * @desc convert from decimal format to raw amount - */ -export const convertDecimalFormatToRawAmount = (value: string, decimals = 18): string => - new BigNumber(value).multipliedBy(new BigNumber(10).pow(decimals)).toFixed(0); - -export const fromWei = (number: BigNumberish): string => convertRawAmountToDecimalFormat(number, 18); - -const decimalSeparator = '.'; -const lessThanPrefix = '<'; - -export const formatNumber = (value: string, options?: { decimals?: number }) => { - if (!+value) return `0${decimalSeparator}0`; - if (+value < 0.0001) return `${lessThanPrefix}0${decimalSeparator}0001`; - - const [whole, fraction = ''] = value.split(decimalSeparator); - const decimals = options?.decimals; - const paddedFraction = `${fraction.padEnd(decimals || 4, '0')}`; - - if (decimals) { - if (decimals === 0) return whole; - return `${whole}${decimalSeparator}${paddedFraction.slice(0, decimals)}`; - } - - if (+whole > 0) return `${whole}${decimalSeparator}${paddedFraction.slice(0, 2)}`; - return `0${decimalSeparator}${paddedFraction.slice(0, 4)}`; -}; diff --git a/src/__swaps__/utils/strings.ts b/src/__swaps__/utils/strings.ts deleted file mode 100644 index dd7c7d26615..00000000000 --- a/src/__swaps__/utils/strings.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const isLowerCaseMatch = (a?: string, b?: string) => a?.toLowerCase() === b?.toLowerCase(); - -export const isLowerCaseMatchWorklet = (a?: string, b?: string) => { - 'worklet'; - return a?.toLowerCase() === b?.toLowerCase(); -}; - -export const capitalize = (s = '') => s.charAt(0).toUpperCase() + s.slice(1); - -export const truncateString = (txt = '', maxLength = 22) => { - return `${txt?.slice(0, maxLength)}${txt.length > maxLength ? '…' : ''}`; -}; diff --git a/src/__swaps__/utils/swaps.ts b/src/__swaps__/utils/swaps.ts index c3b6d936fa0..4d9ec3982ea 100644 --- a/src/__swaps__/utils/swaps.ts +++ b/src/__swaps__/utils/swaps.ts @@ -32,12 +32,19 @@ import { powWorklet, roundWorklet, toFixedWorklet, -} from '../safe-math/SafeMath'; -import { AddressOrEth, ExtendedAnimatedAssetWithColors, ParsedSearchAsset } from '../types/assets'; +} from '@/safe-math/SafeMath'; +import { ExtendedAnimatedAssetWithColors, ParsedSearchAsset } from '../types/assets'; import { inputKeys } from '../types/swap'; import { valueBasedDecimalFormatter } from './decimalFormatter'; -import { convertAmountToRawAmount } from './numbers'; +import { convertAmountToRawAmount } from '@/helpers/utilities'; import { ChainId } from '@/state/backendNetworks/types'; +import { getUniqueId } from '@/utils/ethereumUtils'; + +// DO NOT REMOVE THESE COMMENTED ENV VARS +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { IS_APK_BUILD } from 'react-native-dotenv'; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import isTestFlight from '@/helpers/isTestFlight'; // /---- 🎨 Color functions 🎨 ----/ // // @@ -50,14 +57,14 @@ export type ResponseByTheme = { dark: T; }; -export const getColorValueForTheme = (values: ResponseByTheme | undefined, isDarkMode: boolean, useDefaults = false) => { +export const getColorValueForTheme = (values: ResponseByTheme | undefined, isDarkMode: boolean) => { if (!values) { return isDarkMode ? ETH_COLOR_DARK : ETH_COLOR; } return isDarkMode ? values.dark : values.light; }; -export const getColorValueForThemeWorklet = (values: ResponseByTheme | undefined, isDarkMode: boolean, useDefaults = true) => { +export const getColorValueForThemeWorklet = (values: ResponseByTheme | undefined, isDarkMode: boolean) => { 'worklet'; if (!values) { @@ -525,11 +532,6 @@ const ETH_COLORS: Colors = { shadow: undefined, }; -export const getStandardizedUniqueIdWorklet = ({ address, chainId }: { address: AddressOrEth; chainId: ChainId }) => { - 'worklet'; - return `${address}_${chainId}`; -}; - export const parseAssetAndExtend = ({ asset, insertUserAssetBalance, @@ -543,7 +545,7 @@ export const parseAssetAndExtend = ({ colors: (isAssetEth ? ETH_COLORS : asset.colors) as TokenColors, }); - const uniqueId = getStandardizedUniqueIdWorklet({ address: asset.address, chainId: asset.chainId }); + const uniqueId = getUniqueId(asset.address, asset.chainId); const balance = insertUserAssetBalance ? userAssetsStore.getState().getUserAsset(uniqueId)?.balance || asset.balance : asset.balance; return { @@ -589,7 +591,7 @@ export const buildQuoteParams = ({ const isCrosschainSwap = inputAsset.chainId !== outputAsset.chainId; - return { + const quoteParams: QuoteParams = { source: source === 'auto' ? undefined : source, chainId: inputAsset.chainId, fromAddress: currentAddress, @@ -608,4 +610,9 @@ export const buildQuoteParams = ({ toChainId: isCrosschainSwap ? outputAsset.chainId : inputAsset.chainId, currency: store.getState().settings.nativeCurrency, }; + + // Do not delete the comment below 😤 + // @ts-ignore About to get quote + + return quoteParams; }; diff --git a/src/__swaps__/utils/time.ts b/src/__swaps__/utils/time.ts deleted file mode 100644 index 306caa25906..00000000000 --- a/src/__swaps__/utils/time.ts +++ /dev/null @@ -1,49 +0,0 @@ -import parseMilliseconds from 'parse-ms'; - -import * as i18n from '@/languages'; - -export const buildLocalizedTimeUnitString = ({ plural, short, unit }: { plural?: boolean; short: boolean; unit: string }) => { - const length = short ? 'short' : 'long'; - const plurality = plural ? 'plural' : 'singular'; - - return i18n.t(`time.${unit}.${length}.${plurality}`); -}; - -const getHighestResolutionUnit = (timeUnitKey?: string, timeUnitValues?: { [key: string]: number }) => { - const highestResolutionUnit = timeUnitKey || 'seconds'; - return { - unit: highestResolutionUnit, - value: timeUnitValues?.[highestResolutionUnit] || 0, - }; -}; - -/** - * @desc get time string for minimal unit - * @param {String} [value=''] - * @param {Boolean} [short=true] - * @param {Boolean} [plural=false] - * @return {String} - */ -export const getMinimalTimeUnitStringForMs = (value: number, plural?: boolean, short = true): string => { - const ms = Number(value); - - const { days, hours, minutes, seconds } = parseMilliseconds(Number(ms)); - - const times = { days, hours, minutes, seconds }; - const timeUnitKey = Object.entries(times).find(([, value]) => value !== 0)?.[0]; - - const { unit: highestResolutionUnit, value: highestResolutionValue } = getHighestResolutionUnit(timeUnitKey, { - days, - hours, - minutes, - seconds, - }); - - const label = buildLocalizedTimeUnitString({ - plural, - short, - unit: highestResolutionUnit, - }); - - return `${highestResolutionValue} ${label}`; -}; diff --git a/src/__swaps__/utils/userChains.ts b/src/__swaps__/utils/userChains.ts deleted file mode 100644 index 5330863e70e..00000000000 --- a/src/__swaps__/utils/userChains.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { - Chain, - arbitrum, - arbitrumGoerli, - arbitrumSepolia, - avalanche, - avalancheFuji, - base, - baseSepolia, - bsc, - bscTestnet, - holesky, - optimism, - optimismSepolia, - polygon, - polygonMumbai, - zora, - zoraSepolia, - goerli, - mainnet, - sepolia, -} from 'viem/chains'; - -import { ChainId } from '@/state/backendNetworks/types'; -import { useBackendNetworksStore } from '@/state/backendNetworks/backendNetworks'; - -export const chainIdMap: Record< - ChainId.mainnet | ChainId.optimism | ChainId.polygon | ChainId.base | ChainId.bsc | ChainId.zora | ChainId.avalanche, - ChainId[] -> = { - [ChainId.mainnet]: [mainnet.id, goerli.id, sepolia.id, holesky.id], - [ChainId.optimism]: [optimism.id, optimismSepolia.id], - [ChainId.arbitrum]: [arbitrum.id, arbitrumGoerli.id, arbitrumSepolia.id], - [ChainId.polygon]: [polygon.id, polygonMumbai.id], - [ChainId.base]: [base.id, baseSepolia.id], - [ChainId.bsc]: [bsc.id, bscTestnet.id], - [ChainId.zora]: [zora.id, zoraSepolia.id], - [ChainId.avalanche]: [avalanche.id, avalancheFuji.id], -}; - -export const chainLabelMap: Record< - ChainId.mainnet | ChainId.optimism | ChainId.polygon | ChainId.base | ChainId.bsc | ChainId.zora | ChainId.avalanche, - string[] -> = { - [ChainId.mainnet]: [ - useBackendNetworksStore.getState().getChainsLabel()[goerli.id], - useBackendNetworksStore.getState().getChainsLabel()[sepolia.id], - useBackendNetworksStore.getState().getChainsLabel()[holesky.id], - ], - [ChainId.optimism]: [useBackendNetworksStore.getState().getChainsLabel()[optimismSepolia.id]], - [ChainId.arbitrum]: [ - useBackendNetworksStore.getState().getChainsLabel()[arbitrumGoerli.id], - useBackendNetworksStore.getState().getChainsLabel()[arbitrumSepolia.id], - ], - [ChainId.polygon]: [useBackendNetworksStore.getState().getChainsLabel()[polygonMumbai.id]], - [ChainId.base]: [useBackendNetworksStore.getState().getChainsLabel()[baseSepolia.id]], - [ChainId.bsc]: [useBackendNetworksStore.getState().getChainsLabel()[bscTestnet.id]], - [ChainId.zora]: [useBackendNetworksStore.getState().getChainsLabel()[zoraSepolia.id]], - [ChainId.avalanche]: [useBackendNetworksStore.getState().getChainsLabel()[avalancheFuji.id]], -}; - -export const sortNetworks = (order: ChainId[], chains: Chain[]) => { - const allChainsOrder = order?.map(chainId => chainIdMap[chainId] || [chainId])?.flat(); - const ordered = chains.sort((a, b) => { - const aIndex = allChainsOrder.indexOf(a.id); - const bIndex = allChainsOrder.indexOf(b.id); - if (aIndex === -1) return bIndex === -1 ? 0 : 1; - if (bIndex === -1) return -1; - return aIndex - bIndex; - }); - return ordered; -}; - -export const filterUserNetworks = ({ userChains }: { userChains: Record }) => { - const availableChains = Object.keys(userChains) - .filter(chainId => userChains[Number(chainId)] === true) - .map(chainId => Number(chainId)); - - const allAvailableUserChains = availableChains.map(chainId => chainIdMap[chainId]).flat(); - - return allAvailableUserChains; -}; diff --git a/src/components/AnimatedComponents/extendedPropsAllowlists.ts b/src/components/AnimatedComponents/extendedPropsAllowlists.ts index b9d470f2d66..40cd35c9a43 100644 --- a/src/components/AnimatedComponents/extendedPropsAllowlists.ts +++ b/src/components/AnimatedComponents/extendedPropsAllowlists.ts @@ -12,7 +12,6 @@ import Animated from 'react-native-reanimated'; */ Animated.addWhitelistedNativeProps({ defaultValue: true, // AnimatedTextInput - text: true, // AnimatedTextInput }); /** diff --git a/src/components/AppStateChangeHandler.tsx b/src/components/AppStateChangeHandler.tsx deleted file mode 100644 index d43dceff17d..00000000000 --- a/src/components/AppStateChangeHandler.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { useCallback, useEffect, useRef, useState } from 'react'; -import { AppState, AppStateStatus } from 'react-native'; -import { analyticsV2 } from '@/analytics'; -import store from '@/redux/store'; -import { walletConnectLoadState } from '@/redux/walletconnect'; - -type AppStateChangeHandlerProps = { - walletReady: boolean; -}; - -export function AppStateChangeHandler({ walletReady }: AppStateChangeHandlerProps) { - const [appState, setAppState] = useState(AppState.currentState); - const eventSubscription = useRef | null>(null); - - const handleAppStateChange = useCallback( - (nextAppState: AppStateStatus) => { - if (appState === 'background' && nextAppState === 'active') { - store.dispatch(walletConnectLoadState()); - } - setAppState(nextAppState); - analyticsV2.track(analyticsV2.event.appStateChange, { - category: 'app state', - label: nextAppState, - }); - }, - [appState] - ); - - useEffect(() => { - if (!walletReady) return; - - eventSubscription.current = AppState.addEventListener('change', handleAppStateChange); - - return () => eventSubscription.current?.remove(); - }, [handleAppStateChange, walletReady]); - - return null; -} diff --git a/src/components/exchange/CurrencySelectionList.tsx b/src/components/CurrencySelectionList.tsx similarity index 79% rename from src/components/exchange/CurrencySelectionList.tsx rename to src/components/CurrencySelectionList.tsx index ae980536e6c..ecc0d551f83 100644 --- a/src/components/exchange/CurrencySelectionList.tsx +++ b/src/components/CurrencySelectionList.tsx @@ -1,15 +1,15 @@ import React, { forwardRef, ForwardRefRenderFunction } from 'react'; import { SectionList } from 'react-native'; -import { magicMemo } from '../../utils'; -import { EmptyAssetList } from '../asset-list'; -import { Centered } from '../layout'; -import { NoResults } from '../list'; -import { CurrencySelectModalHeaderHeight } from './CurrencySelectModalHeader'; -import ExchangeAssetList from './ExchangeAssetList'; -import { ExchangeSearchHeight } from './ExchangeSearch'; +import { magicMemo } from '@/utils'; +import { EmptyAssetList } from '@/components/asset-list'; +import { Centered } from '@/components/layout'; +import { NoResults } from '@/components/list'; +import ExchangeAssetList, { EnrichedExchangeAsset } from '@/components/ExchangeAssetList'; import { Box } from '@/design-system'; -import { EnrichedExchangeAsset } from '@/screens/CurrencySelectModal'; -import { NoResultsType } from '../list/NoResults'; +import { NoResultsType } from '@/components/list/NoResults'; + +const CurrencySelectModalHeaderHeight = 59; +const SearchHeight = 40; interface CurrencySelectionListProps { keyboardDismissMode?: 'none' | 'interactive' | 'on-drag'; @@ -44,7 +44,7 @@ const CurrencySelectionList: ForwardRefRenderFunction {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */} diff --git a/src/components/DappBrowser/BrowserTab.tsx b/src/components/DappBrowser/BrowserTab.tsx index 23e7a37ac8b..02cca1a6425 100644 --- a/src/components/DappBrowser/BrowserTab.tsx +++ b/src/components/DappBrowser/BrowserTab.tsx @@ -42,7 +42,6 @@ import { Homepage } from './Homepage'; import { WebViewBorder } from './WebViewBorder'; import { RAINBOW_HOME, - DEFAULT_TAB_URL, TAB_SCREENSHOT_FILE_FORMAT, TAB_SCREENSHOT_FASTER_IMAGE_CONFIG, USER_AGENT, @@ -196,7 +195,7 @@ const FreezableWebViewComponent = ({ const webViewRef = useRef(null); const isActiveTab = useBrowserStore(state => state.isTabActive(tabId)); - const tabUrl = useBrowserStore(state => state.getTabData?.(tabId)?.url) || DEFAULT_TAB_URL; + const tabUrl = useBrowserStore(state => state.getTabData?.(tabId)?.url) || RAINBOW_HOME; const isOnHomepage = tabUrl === RAINBOW_HOME; const handleOnMessage = useCallback( diff --git a/src/components/DappBrowser/Homepage.tsx b/src/components/DappBrowser/Homepage.tsx index 121b20811ef..3cb97e495e4 100644 --- a/src/components/DappBrowser/Homepage.tsx +++ b/src/components/DappBrowser/Homepage.tsx @@ -1,6 +1,6 @@ import { BlurView } from '@react-native-community/blur'; import React, { memo, useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react'; -import { PixelRatio, ScrollView, StyleSheet, View } from 'react-native'; +import { ScrollView, StyleSheet, View } from 'react-native'; import LinearGradient from 'react-native-linear-gradient'; import { runOnJS, useAnimatedReaction } from 'react-native-reanimated'; import { ButtonPressAnimation } from '@/components/animations'; @@ -40,11 +40,10 @@ import { useBrowserContext } from './BrowserContext'; import { getNameFromFormattedUrl } from './utils'; import { useTrendingDApps } from '@/resources/metadata/trendingDapps'; import { DApp } from '@/graphql/__generated__/metadata'; -import { DEFAULT_TAB_URL } from './constants'; -import { FeaturedResult } from '@/graphql/__generated__/arc'; +import { RAINBOW_HOME } from './constants'; import { useRemoteConfig } from '@/model/remoteConfig'; import { FEATURED_RESULTS, useExperimentalFlag } from '@/config'; -import { FeaturedResultStack } from '../FeaturedResult/FeaturedResultStack'; +import { FeaturedResultStack, FeaturedResultStackProps } from '@/components/FeaturedResult/FeaturedResultStack'; const HORIZONTAL_PAGE_INSET = 24; const MAX_RECENTS_TO_DISPLAY = 6; @@ -86,7 +85,7 @@ export const Homepage = ({ tabId }: { tabId: string }) => { ); }; -const DappBrowserFeaturedResults = () => { +const DappBrowserFeaturedResults = ({ children }: { children: FeaturedResultStackProps['children'] }) => { const { goToUrl } = useBrowserContext(); const { featured_results } = useRemoteConfig(); const featuredResultsEnabled = (useExperimentalFlag(FEATURED_RESULTS) || featured_results) && !IS_TEST; @@ -102,7 +101,11 @@ const DappBrowserFeaturedResults = () => { return null; } - return ; + return ( + + {children} + + ); }; const Trending = ({ goToUrl }: { goToUrl: (url: string) => void }) => { @@ -132,7 +135,18 @@ const Trending = ({ goToUrl }: { goToUrl: (url: string) => void }) => { > - + + {({ featuredResult, handlePress }) => ( + + )} + {data.dApps .filter((dApp): dApp is DApp => dApp !== null) .map((dApp, index) => ( @@ -175,7 +189,7 @@ const Favorites = ({ goToUrl, tabId }: { goToUrl: (url: string) => void; tabId: ({ currentGridSort, isActiveTab }: { currentGridSort: string[] | undefined; isActiveTab: boolean }) => { 'worklet'; const homepageTabsCount = currentlyOpenTabIds.value.filter( - tabId => !animatedTabUrls.value[tabId] || animatedTabUrls.value[tabId] === DEFAULT_TAB_URL + tabId => !animatedTabUrls.value[tabId] || animatedTabUrls.value[tabId] === RAINBOW_HOME ).length; const inactiveAndMounted = !isActiveTab && currentGridSort !== undefined; @@ -390,7 +404,8 @@ const Card = memo(function Card({ const dappIconUrl = useMemo(() => { const dappUrl = site.url; const iconUrl = site.image; - const host = new URL(dappUrl).hostname; + const url = dappUrl.startsWith('http') ? dappUrl : `https://${dappUrl}`; + const host = new URL(url).hostname; // 👇 TODO: Remove this once the Uniswap logo in the dapps metadata is fixed const isUniswap = host === 'uniswap.org' || host.endsWith('.uniswap.org'); const dappOverride = dapps.find(dapp => dapp.urlDisplay === host); @@ -507,65 +522,6 @@ const Card = memo(function Card({ ); }); -const getImageForDevicePixelRatio = ({ imageUrl, imageVariants }: FeaturedResult) => { - if (!imageVariants) return imageUrl; - - const pixelRatio = PixelRatio.get(); - const { x1, x2, x3 } = imageVariants; - - if (pixelRatio < 1.5) return x1?.url || imageUrl; - if (pixelRatio < 3) return x2?.url || x1?.url || imageUrl; - return x3?.url || x2?.url || x1?.url || imageUrl; -}; - -export const DappBrowserFeaturedResultsCard = memo(function Card({ - handlePress, - featuredResult, -}: { - handlePress: () => void; - featuredResult: FeaturedResult; -}) { - const { isDarkMode } = useColorMode(); - - const imageUrl = getImageForDevicePixelRatio(featuredResult); - - return ( - - - - - - {IS_IOS && ( - - )} - - - ); -}); - const CardBackground = memo(function CardBackgroundOverlay({ imageUrl, isDarkMode, diff --git a/src/components/DappBrowser/constants.ts b/src/components/DappBrowser/constants.ts index e3bdae2b9bd..c3e29fd02cd 100644 --- a/src/components/DappBrowser/constants.ts +++ b/src/components/DappBrowser/constants.ts @@ -6,8 +6,6 @@ export const HTTP = 'http://'; export const HTTPS = 'https://'; export const RAINBOW_HOME = 'RAINBOW_HOME'; -export const DEFAULT_TAB_URL = RAINBOW_HOME; - export const USER_AGENT = { IOS: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4.1 Mobile/15E148 Safari/604.1', ANDROID: 'Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.6533.103 Mobile Safari/537.36', diff --git a/src/components/DappBrowser/control-panel/ControlPanel.tsx b/src/components/DappBrowser/control-panel/ControlPanel.tsx index cd3c587b259..bc16b72e4f1 100644 --- a/src/components/DappBrowser/control-panel/ControlPanel.tsx +++ b/src/components/DappBrowser/control-panel/ControlPanel.tsx @@ -33,7 +33,6 @@ import { useSyncSharedValue } from '@/hooks/reanimated/useSyncSharedValue'; import { useBrowserStore } from '@/state/browser/browserStore'; import { colors } from '@/styles'; import { deviceUtils, watchingAlert } from '@/utils'; -import ethereumUtils from '@/utils/ethereumUtils'; import { addressHashedEmoji } from '@/utils/profileUtils'; import { getHighContrastTextColorWorklet } from '@/worklets/colors'; import { TOP_INSET } from '../Dimensions'; @@ -45,19 +44,17 @@ import { useDispatch } from 'react-redux'; import store from '@/redux/store'; import { getDappHost } from '@/utils/connectedApps'; import WebView from 'react-native-webview'; -import { Navigation, useNavigation } from '@/navigation'; +import { useNavigation } from '@/navigation'; import Routes from '@/navigation/routesNames'; import { address } from '@/utils/abbreviations'; import { fontWithWidthWorklet } from '@/styles/buildTextStyles'; import { useAppSessionsStore } from '@/state/appSessions'; -import { DEFAULT_TAB_URL, RAINBOW_HOME } from '../constants'; +import { RAINBOW_HOME } from '../constants'; import { FavoritedSite, useFavoriteDappsStore } from '@/state/browser/favoriteDappsStore'; import WalletTypes from '@/helpers/walletTypes'; import { usePersistentDominantColorFromImage } from '@/hooks/usePersistentDominantColorFromImage'; import { findWalletWithAccount } from '@/helpers/findWalletWithAccount'; import { addressSetSelected, walletsSetSelected } from '@/redux/wallets'; -import { getRemoteConfig } from '@/model/remoteConfig'; -import { SWAPS_V2, useExperimentalFlag } from '@/config'; import { swapsStore } from '@/state/swaps/swapsStore'; import { userAssetsStore } from '@/state/assets/userAssets'; import { greaterThan } from '@/helpers/utilities'; @@ -89,7 +86,7 @@ export const ControlPanel = () => { } = useRoute>(); const walletsWithBalancesAndNames = useWalletsWithBalancesAndNames(); const activeTabUrl = useBrowserStore(state => state.getActiveTabUrl()); - const activeTabHost = getDappHost(activeTabUrl || '') || DEFAULT_TAB_URL; + const activeTabHost = getDappHost(activeTabUrl || '') || RAINBOW_HOME; const updateActiveSessionNetwork = useAppSessionsStore(state => state.updateActiveSessionNetwork); const updateActiveSession = useAppSessionsStore(state => state.updateActiveSession); const addSession = useAppSessionsStore(state => state.addSession); @@ -382,8 +379,6 @@ const HomePanel = ({ const dispatch = useDispatch(); const { navigate } = useNavigation(); - const swapsV2Enabled = useExperimentalFlag(SWAPS_V2); - const actionButtonList = useMemo(() => { const walletIcon = selectedWallet?.IconComponent || <>; const walletLabel = selectedWallet?.label || ''; @@ -444,57 +439,27 @@ const HomePanel = ({ const valid = await runWalletChecksBeforeSwapOrBridge(); if (!valid) return; - const { swaps_v2 } = getRemoteConfig(); - - if (swaps_v2 || swapsV2Enabled) { - swapsStore.setState({ - inputAsset: userAssetsStore.getState().getHighestValueEth(), - }); - InteractionManager.runAfterInteractions(() => { - navigate(Routes.SWAP); - }); - return; - } - - const mainnetEth = await ethereumUtils.getNativeAssetForNetwork({ chainId: ChainId.mainnet, address: selectedWallet?.uniqueId }); - Navigation.handleAction(Routes.EXCHANGE_MODAL, { - fromDiscover: true, - params: { - inputAsset: mainnetEth, - }, - screen: Routes.MAIN_EXCHANGE_SCREEN, + swapsStore.setState({ + inputAsset: userAssetsStore.getState().getHighestValueEth(), + }); + InteractionManager.runAfterInteractions(() => { + navigate(Routes.SWAP); }); - }, [navigate, runWalletChecksBeforeSwapOrBridge, selectedWallet?.uniqueId, swapsV2Enabled]); + }, [navigate, runWalletChecksBeforeSwapOrBridge]); const handleOnPressBridge = useCallback(async () => { const valid = await runWalletChecksBeforeSwapOrBridge(); if (!valid) return; - const { swaps_v2 } = getRemoteConfig(); - - if (swaps_v2 || swapsV2Enabled) { - // TODO: We need to set something in swapsStore that deliniates between a swap and bridge - // for now let's just treat it like a normal swap - swapsStore.setState({ - inputAsset: userAssetsStore.getState().getHighestValueEth(), - }); - InteractionManager.runAfterInteractions(() => { - navigate(Routes.SWAP); - }); - return; - } - - const mainnetEth = await ethereumUtils.getNativeAssetForNetwork({ chainId: ChainId.mainnet, address: selectedWallet?.uniqueId }); - Navigation.handleAction(Routes.EXCHANGE_MODAL, { - fromDiscover: true, - params: { - inputAsset: mainnetEth, - }, - screen: Routes.MAIN_EXCHANGE_SCREEN, + swapsStore.setState({ + inputAsset: userAssetsStore.getState().getHighestValueEth(), + }); + InteractionManager.runAfterInteractions(() => { + navigate(Routes.SWAP); }); - }, [navigate, runWalletChecksBeforeSwapOrBridge, selectedWallet?.uniqueId, swapsV2Enabled]); + }, [navigate, runWalletChecksBeforeSwapOrBridge]); - const isOnHomepage = useBrowserStore(state => (state.getActiveTabUrl() || DEFAULT_TAB_URL) === RAINBOW_HOME); + const isOnHomepage = useBrowserStore(state => (state.getActiveTabUrl() || RAINBOW_HOME) === RAINBOW_HOME); return ( diff --git a/src/components/DappBrowser/search-input/AccountIcon.tsx b/src/components/DappBrowser/search-input/AccountIcon.tsx index 823a00feb0a..9bf8cc16b5d 100644 --- a/src/components/DappBrowser/search-input/AccountIcon.tsx +++ b/src/components/DappBrowser/search-input/AccountIcon.tsx @@ -13,7 +13,7 @@ import { getDappHost } from '../handleProviderRequest'; import { ButtonPressAnimation } from '@/components/animations'; import { useBrowserStore } from '@/state/browser/browserStore'; import { useBrowserContext } from '../BrowserContext'; -import { DEFAULT_TAB_URL, RAINBOW_HOME } from '../constants'; +import { RAINBOW_HOME } from '../constants'; export const AccountIcon = React.memo(function AccountIcon() { const { navigate } = useNavigation(); @@ -23,8 +23,8 @@ export const AccountIcon = React.memo(function AccountIcon() { const [currentAddress, setCurrentAddress] = useState(accountAddress); const { activeTabRef } = useBrowserContext(); - const activeTabHost = useBrowserStore(state => getDappHost(state.getActiveTabUrl())) || DEFAULT_TAB_URL; - const isOnHomepage = useBrowserStore(state => (state.getActiveTabUrl() || DEFAULT_TAB_URL) === RAINBOW_HOME); + const activeTabHost = useBrowserStore(state => getDappHost(state.getActiveTabUrl())) || RAINBOW_HOME; + const isOnHomepage = useBrowserStore(state => (state.getActiveTabUrl() || RAINBOW_HOME) === RAINBOW_HOME); const hostSessions = useAppSessionsStore(state => state.getActiveSession({ host: activeTabHost })); const currentSession = useMemo( () => diff --git a/src/components/exchange/ExchangeAssetList.tsx b/src/components/ExchangeAssetList.tsx similarity index 91% rename from src/components/exchange/ExchangeAssetList.tsx rename to src/components/ExchangeAssetList.tsx index 52c916b5e80..c22c58e1ca3 100644 --- a/src/components/exchange/ExchangeAssetList.tsx +++ b/src/components/ExchangeAssetList.tsx @@ -12,14 +12,14 @@ import React, { } from 'react'; import { InteractionManager, Keyboard, SectionList, SectionListData } from 'react-native'; import LinearGradient from 'react-native-linear-gradient'; -import { ButtonPressAnimation } from '../animations'; -import useAccountSettings from '../../hooks/useAccountSettings'; -import FastCurrencySelectionRow from '../asset-list/RecyclerAssetList2/FastComponents/FastCurrencySelectionRow'; -import { ContactRow } from '../contacts'; -import DiscoverSheetContext from '../../screens/discover/DiscoverScreenContext'; -import { GradientText } from '../text'; -import { CopyToast, ToastPositionContainer } from '../toasts'; -import contextMenuProps from './exchangeAssetRowContextMenuProps'; +import { ButtonPressAnimation } from '@/components/animations'; +import useAccountSettings from '@/hooks/useAccountSettings'; +import FastCurrencySelectionRow from '@/components/asset-list/RecyclerAssetList2/FastComponents/FastCurrencySelectionRow'; +import { ContactRow } from '@/components/contacts'; +import DiscoverSheetContext from '@/screens/discover/DiscoverScreenContext'; +import { GradientText } from '@/components/text'; +import { CopyToast, ToastPositionContainer } from '@/components/toasts'; +import contextMenuProps from '@/components/exchangeAssetRowContextMenuProps'; import { TokenSectionTypes } from '@/helpers'; import { useAndroidScrollViewGestureHandler, usePrevious } from '@/hooks'; import { useNavigation } from '@/navigation'; @@ -29,11 +29,22 @@ import { useTheme } from '@/theme'; import { abbreviations, deviceUtils, haptics, magicMemo } from '@/utils'; import { Box, Text } from '@/design-system'; import { colors, Colors } from '@/styles'; -import { EnrichedExchangeAsset } from '@/screens/CurrencySelectModal'; -import ExchangeTokenRow from './ExchangeTokenRow'; +import ExchangeTokenRow from '@/components/ExchangeTokenRow'; import { SwappableAsset } from '@/entities'; import { toggleFavorite, useFavorites } from '@/resources/favorites'; +export interface EnrichedExchangeAsset extends SwappableAsset { + ens: boolean; + color: string; + nickname: string; + onPress: (el: ReactElement) => void; + testID: string; + useGradientText: boolean; + title?: string; + key: string; + disabled?: boolean; +} + const deviceWidth = deviceUtils.dimensions.width; const HeaderBackground = styled(LinearGradient).attrs(({ theme: { colors } }: { theme: { colors: Colors } }) => ({ diff --git a/src/components/exchange/ExchangeHeader.tsx b/src/components/ExchangeHeader.tsx similarity index 81% rename from src/components/exchange/ExchangeHeader.tsx rename to src/components/ExchangeHeader.tsx index 78fe69639e1..4b83588ebe1 100644 --- a/src/components/exchange/ExchangeHeader.tsx +++ b/src/components/ExchangeHeader.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { SheetHandle } from '../sheet'; +import { SheetHandle } from '@/components/sheet'; import { Box, Text, Inset, Stack } from '@/design-system'; interface ExchangeHeaderProps { @@ -7,7 +7,7 @@ interface ExchangeHeaderProps { title: string; } -export default function ExchangeHeader({ testID, title }: ExchangeHeaderProps) { +export function ExchangeHeader({ testID, title }: ExchangeHeaderProps) { return ( diff --git a/src/components/exchange/ExchangeInput.js b/src/components/ExchangeInput.js similarity index 98% rename from src/components/exchange/ExchangeInput.js rename to src/components/ExchangeInput.js index baa013b087f..5c53792b5d9 100644 --- a/src/components/exchange/ExchangeInput.js +++ b/src/components/ExchangeInput.js @@ -1,7 +1,7 @@ import React, { Fragment, useCallback, useState } from 'react'; import { InteractionManager } from 'react-native'; import TextInputMask from 'react-native-text-input-mask'; -import { Text } from '../text'; +import { Text } from '@/components/text'; import styled from '@/styled-thing'; import { buildTextStyles } from '@/styles'; import { magicMemo } from '@/utils'; diff --git a/src/components/exchange/ExchangeTokenRow.tsx b/src/components/ExchangeTokenRow.tsx similarity index 93% rename from src/components/exchange/ExchangeTokenRow.tsx rename to src/components/ExchangeTokenRow.tsx index 4b7cb7e3147..0e509debbe4 100644 --- a/src/components/exchange/ExchangeTokenRow.tsx +++ b/src/components/ExchangeTokenRow.tsx @@ -3,12 +3,12 @@ import isEqual from 'react-fast-compare'; import { Box, Column, Columns, Inline, Stack, Text } from '@/design-system'; import { isNativeAsset } from '@/handlers/assets'; import { useAsset, useDimensions } from '@/hooks'; -import { ButtonPressAnimation } from '../animations'; -import { FloatingEmojis } from '../floating-emojis'; +import { ButtonPressAnimation } from '@/components/animations'; +import { FloatingEmojis } from '@/components/floating-emojis'; import { IS_IOS } from '@/env'; -import { FavStar, Info } from '../asset-list/RecyclerAssetList2/FastComponents/FastCurrencySelectionRow'; +import { FavStar, Info } from '@/components/asset-list/RecyclerAssetList2/FastComponents/FastCurrencySelectionRow'; import { View } from 'react-native'; -import RainbowCoinIcon from '../coin-icon/RainbowCoinIcon'; +import RainbowCoinIcon from '@/components/coin-icon/RainbowCoinIcon'; import { ChainId } from '@/state/backendNetworks/types'; import { ParsedAddressAsset } from '@/entities'; diff --git a/src/components/FeaturedResult/FeaturedResultCard.tsx b/src/components/FeaturedResult/FeaturedResultCard.tsx index 319b89274f1..4fc5edcbc6f 100644 --- a/src/components/FeaturedResult/FeaturedResultCard.tsx +++ b/src/components/FeaturedResult/FeaturedResultCard.tsx @@ -2,7 +2,7 @@ import { FeaturedResultsVariables, useFeaturedResults } from '@/resources/featur import { getFeaturedResultById } from '@/resources/featuredResults/_selectors/getFeaturedResultById'; import { useTrackFeaturedResult } from '@/resources/featuredResults/trackFeaturedResult'; import { TrackFeaturedResultType } from '@/graphql/__generated__/arc'; -import React, { useCallback, useEffect } from 'react'; +import { useCallback, useEffect } from 'react'; import { FeaturedResultStackProps } from './FeaturedResultStack'; import { logger } from '@/logger'; @@ -11,7 +11,7 @@ type FeaturedResultCardProps = FeaturedResultStackProps & featuredResultId: string; }; -export const FeaturedResultCard = ({ featuredResultId, onNavigate, Card, ...props }: FeaturedResultCardProps) => { +export const FeaturedResultCard = ({ featuredResultId, onNavigate, children, ...props }: FeaturedResultCardProps) => { const { data: featuredResult } = useFeaturedResults(props, { select: data => getFeaturedResultById(data, featuredResultId), }); @@ -56,5 +56,5 @@ export const FeaturedResultCard = ({ featuredResultId, onNavigate, Card, ...prop return null; } - return ; + return children({ featuredResult, handlePress }); }; diff --git a/src/components/FeaturedResult/FeaturedResultStack.tsx b/src/components/FeaturedResult/FeaturedResultStack.tsx index 17649921268..5d3e7f48472 100644 --- a/src/components/FeaturedResult/FeaturedResultStack.tsx +++ b/src/components/FeaturedResult/FeaturedResultStack.tsx @@ -10,10 +10,10 @@ import { FeaturedResult } from '@/graphql/__generated__/arc'; export type FeaturedResultStackProps = { onNavigate: (url: string) => void; placementId: string; - Card: React.FC<{ handlePress: () => void; featuredResult: FeaturedResult }>; + children: React.FC<{ featuredResult: FeaturedResult; handlePress: () => void }>; }; -export const FeaturedResultStack = ({ onNavigate, placementId, Card }: FeaturedResultStackProps) => { +export const FeaturedResultStack = ({ onNavigate, placementId, children }: FeaturedResultStackProps) => { const { accountAddress, language } = useAccountSettings(); const currentIndex = useSharedValue(0); @@ -42,7 +42,8 @@ export const FeaturedResultStack = ({ onNavigate, placementId, Card }: FeaturedR featuredResultId={featuredResultId} placementId={placementId} onNavigate={onNavigate} - Card={Card} - /> + > + {children} + ); }; diff --git a/src/components/expanded-state/custom-gas/FeesGweiInput.tsx b/src/components/FeesGweiInput.tsx similarity index 97% rename from src/components/expanded-state/custom-gas/FeesGweiInput.tsx rename to src/components/FeesGweiInput.tsx index 61eb40753db..89ddbea223c 100644 --- a/src/components/expanded-state/custom-gas/FeesGweiInput.tsx +++ b/src/components/FeesGweiInput.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; import ReactNativeHapticFeedback from 'react-native-haptic-feedback'; -import { ButtonPressAnimation } from '../../animations'; -import GweiInputPill from './GweiInputPill'; +import { ButtonPressAnimation } from '@/components/animations'; +import GweiInputPill from '@/components/GweiInputPill'; import { delay } from '@/helpers/utilities'; import { usePrevious } from '@/hooks'; import { TextInput } from 'react-native'; diff --git a/src/components/expanded-state/custom-gas/FeesPanel.tsx b/src/components/FeesPanel.tsx similarity index 97% rename from src/components/expanded-state/custom-gas/FeesPanel.tsx rename to src/components/FeesPanel.tsx index de4f837c369..c320cc89576 100644 --- a/src/components/expanded-state/custom-gas/FeesPanel.tsx +++ b/src/components/FeesPanel.tsx @@ -4,10 +4,10 @@ import { upperFirst } from 'lodash'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { InteractionManager, Keyboard, KeyboardAvoidingView } from 'react-native'; import { IS_TESTING } from 'react-native-dotenv'; -import { Alert } from '../../alerts'; -import { useTheme } from '../../../theme/ThemeContext'; -import { ButtonPressAnimation } from '../../animations'; -import FeesGweiInput from './FeesGweiInput'; +import { Alert } from '@/components/alerts'; +import { useTheme } from '@/theme/ThemeContext'; +import { ButtonPressAnimation } from '@/components/animations'; +import FeesGweiInput from '@/components/FeesGweiInput'; import { calculateMinerTipAddDifference, calculateMinerTipSubstDifference } from '@/helpers/gas'; import { add, greaterThan, isZero, lessThan, multiply, toFixedDecimals } from '@/helpers/utilities'; import { useFeesPanelInputRefs, useGas, usePrevious, useTimeout } from '@/hooks'; @@ -156,7 +156,6 @@ export default function FeesPanel({ currentGasTrend, colorForAsset, setCanGoBack as={ButtonPressAnimation} paddingVertical="8px" marginVertical="-8px" - // @ts-ignore overloaded props onPress={openHelper} backgroundColor="accent" style={{ maxWidth: 175 }} @@ -546,15 +545,7 @@ export default function FeesPanel({ currentGasTrend, colorForAsset, setCanGoBack - openGasHelper(trendType)} - scaleTo={1} - > + openGasHelper(trendType)} scaleTo={1}> {renderRowLabel(lang.t('gas.current_base_fee'), trendType)} - openGasHelper(trendType)} - scaleTo={1} - > + openGasHelper(trendType)} scaleTo={1}> {formattedBaseFee} diff --git a/src/components/expanded-state/custom-gas/FeesPanelTabs.tsx b/src/components/FeesPanelTabs.tsx similarity index 97% rename from src/components/expanded-state/custom-gas/FeesPanelTabs.tsx rename to src/components/FeesPanelTabs.tsx index df49764c4e8..3298cc22348 100644 --- a/src/components/expanded-state/custom-gas/FeesPanelTabs.tsx +++ b/src/components/FeesPanelTabs.tsx @@ -1,6 +1,6 @@ import { isEmpty } from 'lodash'; import React from 'react'; -import { ButtonPressAnimation } from '../../animations'; +import { ButtonPressAnimation } from '@/components/animations'; import { useGas } from '@/hooks'; import { colors } from '@/styles'; import { gasUtils } from '@/utils'; diff --git a/src/components/expanded-state/custom-gas/GweiInputPill.tsx b/src/components/GweiInputPill.tsx similarity index 96% rename from src/components/expanded-state/custom-gas/GweiInputPill.tsx rename to src/components/GweiInputPill.tsx index 59e49424706..a1f9657de32 100644 --- a/src/components/expanded-state/custom-gas/GweiInputPill.tsx +++ b/src/components/GweiInputPill.tsx @@ -1,9 +1,9 @@ import React, { useCallback } from 'react'; import { IS_TESTING } from 'react-native-dotenv'; import LinearGradient from 'react-native-linear-gradient'; -// @ts-expect-error +// @ts-expect-error - no declaration file import TextInputMask from 'react-native-text-input-mask'; -import { ButtonPressAnimation } from '../../animations'; +import { ButtonPressAnimation } from '@/components/animations'; import styled from '@/styled-thing'; import { buildTextStyles, margin, padding } from '@/styles'; import { useTheme } from '@/theme'; diff --git a/src/components/L2Disclaimer.js b/src/components/L2Disclaimer.js index a9b5b385194..38305bcb90a 100644 --- a/src/components/L2Disclaimer.js +++ b/src/components/L2Disclaimer.js @@ -10,7 +10,7 @@ import { darkModeThemeColors } from '@/styles/colors'; import * as lang from '@/languages'; import { isL2Chain } from '@/handlers/web3'; import { EthCoinIcon } from './coin-icon/EthCoinIcon'; -import { getChainsName } from '@/chains'; +import { useBackendNetworksStore } from '@/state/backendNetworks/backendNetworks'; const L2Disclaimer = ({ chainId, @@ -57,7 +57,7 @@ const L2Disclaimer = ({ ? customText : lang.t(lang.l.expanded_state.asset.l2_disclaimer, { symbol, - network: getChainsName()[chainId], + network: useBackendNetworksStore.getState().getChainsName()[chainId], })} diff --git a/src/components/asset-list/RecyclerAssetList2/Claimable.tsx b/src/components/asset-list/RecyclerAssetList2/Claimable.tsx index 0924ee1c320..7a34dd140fd 100644 --- a/src/components/asset-list/RecyclerAssetList2/Claimable.tsx +++ b/src/components/asset-list/RecyclerAssetList2/Claimable.tsx @@ -7,7 +7,7 @@ import { ButtonPressAnimation } from '@/components/animations'; import { deviceUtils } from '@/utils'; import Routes from '@/navigation/routesNames'; import { ExtendedState } from './core/RawRecyclerList'; -import { convertAmountToNativeDisplayWorklet } from '@/__swaps__/utils/numbers'; +import { convertAmountToNativeDisplayWorklet } from '@/helpers/utilities'; import { analyticsV2 } from '@/analytics'; export const Claimable = React.memo(function Claimable({ uniqueId, extendedState }: { uniqueId: string; extendedState: ExtendedState }) { diff --git a/src/components/asset-list/RecyclerAssetList2/index.tsx b/src/components/asset-list/RecyclerAssetList2/index.tsx index 9c39e196ee7..779bce675da 100644 --- a/src/components/asset-list/RecyclerAssetList2/index.tsx +++ b/src/components/asset-list/RecyclerAssetList2/index.tsx @@ -16,9 +16,7 @@ import { ProfileNameRow } from './profile-header/ProfileNameRow'; import AndroidContextMenu from '@/components/context-menu/ContextMenu.android'; import ContextMenuButton from '@/components/native-context-menu/contextMenu'; import { analytics } from '@/analytics'; -import useWalletConnectConnections from '@/hooks/useWalletConnectConnections'; import lang from 'i18n-js'; -import { useWalletConnectV2Sessions } from '@/walletConnect/hooks/useWalletConnectV2Sessions'; import { IS_ANDROID } from '@/env'; export type AssetListType = 'wallet' | 'ens-profile' | 'select-nft'; @@ -150,9 +148,6 @@ function NavbarOverlay({ accentColor, position }: { accentColor?: string; positi // //////////////////////////////////////////////////// // Context Menu - const { mostRecentWalletConnectors } = useWalletConnectConnections(); - const { sessions: activeWCV2Sessions } = useWalletConnectV2Sessions(); - const menuConfig = React.useMemo( () => ({ menuItems: [ @@ -175,7 +170,7 @@ function NavbarOverlay({ accentColor, position }: { accentColor?: string; positi ].filter(Boolean), ...(ios ? { menuTitle: '' } : {}), }), - [activeWCV2Sessions.length, mostRecentWalletConnectors.length] + [] ); const handlePressMenuItem = React.useCallback( diff --git a/src/components/asset-list/RecyclerAssetList2/profile-header/ProfileActionButtonsRow.tsx b/src/components/asset-list/RecyclerAssetList2/profile-header/ProfileActionButtonsRow.tsx index adc5a5b905c..a1ae36ea082 100644 --- a/src/components/asset-list/RecyclerAssetList2/profile-header/ProfileActionButtonsRow.tsx +++ b/src/components/asset-list/RecyclerAssetList2/profile-header/ProfileActionButtonsRow.tsx @@ -5,12 +5,11 @@ import { InteractionManager, PressableProps } from 'react-native'; import Animated, { useAnimatedStyle, useDerivedValue, withSpring } from 'react-native-reanimated'; import { ButtonPressAnimation } from '@/components/animations'; import { CopyFloatingEmojis } from '@/components/floating-emojis'; -import { enableActionsOnReadOnlyWallet, useExperimentalFlag, SWAPS_V2 } from '@/config'; +import { enableActionsOnReadOnlyWallet } from '@/config'; import { AccentColorProvider, Box, Column, Columns, Inset, Stack, Text, useColorMode } from '@/design-system'; -import { useAccountProfile, useAccountSettings, useWallets } from '@/hooks'; -import { delayNext } from '@/hooks/useMagicAutofocus'; +import { useAccountProfile, useWallets } from '@/hooks'; import { useNavigation } from '@/navigation'; -import { ethereumUtils, watchingAlert } from '@/utils'; +import { watchingAlert } from '@/utils'; import Routes from '@rainbow-me/routes'; import showWalletErrorAlert from '@/helpers/support'; import { analytics } from '@/analytics'; @@ -20,7 +19,6 @@ import { useAccountAccentColor } from '@/hooks/useAccountAccentColor'; import { addressCopiedToastAtom } from '@/recoil/addressCopiedToastAtom'; import { swapsStore } from '@/state/swaps/swapsStore'; import { userAssetsStore } from '@/state/assets/userAssets'; -import { ChainId } from '@/state/backendNetworks/types'; export const ProfileActionButtonsRowHeight = 80; @@ -169,9 +167,6 @@ function BuyButton() { function SwapButton() { const { isReadOnlyWallet } = useWallets(); - const { accountAddress } = useAccountSettings(); - const remoteConfig = useRemoteConfig(); - const swapsV2Enabled = useExperimentalFlag(SWAPS_V2) || remoteConfig.swaps_v2; const { navigate } = useNavigation(); const handlePress = React.useCallback(async () => { @@ -179,30 +174,16 @@ function SwapButton() { analytics.track('Tapped "Swap"', { category: 'home screen', }); - if (swapsV2Enabled) { - swapsStore.setState({ - inputAsset: userAssetsStore.getState().getHighestValueEth(), - }); - InteractionManager.runAfterInteractions(() => { - navigate(Routes.SWAP); - }); - return; - } - - android && delayNext(); - - const mainnetEth = await ethereumUtils.getNativeAssetForNetwork({ chainId: ChainId.mainnet, address: accountAddress }); - navigate(Routes.EXCHANGE_MODAL, { - fromDiscover: true, - params: { - inputAsset: mainnetEth, - }, - screen: Routes.MAIN_EXCHANGE_SCREEN, + swapsStore.setState({ + inputAsset: userAssetsStore.getState().getHighestValueEth(), + }); + InteractionManager.runAfterInteractions(() => { + navigate(Routes.SWAP); }); } else { watchingAlert(); } - }, [accountAddress, isReadOnlyWallet, navigate, swapsV2Enabled]); + }, [isReadOnlyWallet, navigate]); return ( diff --git a/src/components/backup/BackupCloudStep.tsx b/src/components/backup/BackupCloudStep.tsx index e839d9323c0..cb0c4dde868 100644 --- a/src/components/backup/BackupCloudStep.tsx +++ b/src/components/backup/BackupCloudStep.tsx @@ -11,7 +11,7 @@ import { Text } from '@/components/text'; import WalletAndBackup from '@/assets/WalletsAndBackup.png'; import { analytics } from '@/analytics'; import { cloudBackupPasswordMinLength, isCloudBackupPasswordValid } from '@/handlers/cloudBackup'; -import { useDimensions, useMagicAutofocus, useWallets } from '@/hooks'; +import { useDimensions, useMagicAutofocus } from '@/hooks'; import styled from '@/styled-thing'; import { padding } from '@/styles'; import { Box, Inset, Stack } from '@/design-system'; @@ -23,9 +23,6 @@ import { usePasswordValidation } from './usePasswordValidation'; import { TextInput } from 'react-native'; import { useTheme } from '@/theme'; import { useNavigation } from '@/navigation'; -import Routes from '@/navigation/routesNames'; -import { SETTINGS_BACKUP_ROUTES } from '@/screens/SettingsSheet/components/Backups/routes'; -import walletTypes from '@/helpers/walletTypes'; type BackupCloudStepParams = { BackupCloudStep: { diff --git a/src/components/buttons/TokenSelectionButton.js b/src/components/buttons/TokenSelectionButton.js deleted file mode 100644 index 88ca2955713..00000000000 --- a/src/components/buttons/TokenSelectionButton.js +++ /dev/null @@ -1,83 +0,0 @@ -import lang from 'i18n-js'; -import React, { useMemo } from 'react'; -import { useTheme } from '../../theme/ThemeContext'; -import { ButtonPressAnimation } from '../animations'; -import { InnerBorder, RowWithMargins } from '../layout'; -import { TruncatedText } from '../text'; -import CaretImageSource from '@/assets/family-dropdown-arrow.png'; -import { ImgixImage } from '@/components/images'; -import styled from '@/styled-thing'; -import { padding, position } from '@/styles'; -import ShadowStack from '@/react-native-shadow-stack'; - -const TokenSelectionButtonHeight = 46; -const TokenSelectionButtonMaxWidth = 130; -const TokenSelectionButtonElevation = ios ? 0 : 8; - -const Content = styled(RowWithMargins).attrs({ - align: 'center', - margin: 7, -})({ - ...padding.object(11.5, 14, 13.5, 16), - height: TokenSelectionButtonHeight, - zIndex: 1, -}); - -const CaretIcon = styled(ImgixImage).attrs(({ theme: { colors } }) => ({ - resizeMode: ImgixImage.resizeMode.contain, - source: CaretImageSource, - tintColor: colors.whiteLabel, - size: 30, -}))({ - height: 18, - top: 0.5, - width: 8, -}); - -export default function TokenSelectionButton({ color, borderRadius = 30, onPress, symbol, testID }) { - const { isDarkMode, colors } = useTheme(); - - const shadowsForAsset = useMemo( - () => [ - [0, 10, 30, colors.shadow, 0.2], - [0, 5, 15, color, isDarkMode ? 0 : 0.4], - ], - [color, colors.shadow, isDarkMode] - ); - - return ( - - - - - {symbol ?? lang.t('swap.choose_token')} - - - - - - ); -} diff --git a/src/components/buttons/index.js b/src/components/buttons/index.js index 5541b1af74b..7ab8def660f 100644 --- a/src/components/buttons/index.js +++ b/src/components/buttons/index.js @@ -4,4 +4,3 @@ export { HoldToAuthorizeButton, HoldToAuthorizeButtonIcon } from './hold-to-auth export { default as MiniButton } from './MiniButton'; export { default as PasteAddressButton } from './PasteAddressButton'; export { default as RainbowButton } from './rainbow-button/RainbowButton'; -export { default as TokenSelectionButton } from './TokenSelectionButton'; diff --git a/src/components/cards/CarouselCard.tsx b/src/components/cards/CarouselCard.tsx index 7745ae27d8e..fe74acaa4bf 100644 --- a/src/components/cards/CarouselCard.tsx +++ b/src/components/cards/CarouselCard.tsx @@ -156,7 +156,7 @@ const RefreshButton = ({ refresh, isRefreshing, dataUpdatedAt }: RefreshButtonPr const { colorMode } = useColorMode(); const [canRefresh, setCanRefresh] = useState(dataUpdatedAt < Date.now() - 30_000); - const interval = useRef(); + const interval = useRef>(); useEffect(() => { const checkRefresh = () => { diff --git a/src/components/coin-row/CoinRowInfoButton.js b/src/components/coin-row/CoinRowInfoButton.js deleted file mode 100644 index c9153d1a1fa..00000000000 --- a/src/components/coin-row/CoinRowInfoButton.js +++ /dev/null @@ -1,162 +0,0 @@ -import lang from 'i18n-js'; -import { startCase } from 'lodash'; -import React from 'react'; -import { View } from 'react-native'; -import { IS_TESTING } from 'react-native-dotenv'; -import { ContextMenuButton } from 'react-native-ios-context-menu'; -import RadialGradient from 'react-native-radial-gradient'; -import { ButtonPressAnimation } from '../animations'; -import { Centered } from '../layout'; -import { Text } from '../text'; -import { CoinRowHeight } from './CoinRow'; -import { useClipboard } from '@/hooks'; -import styled from '@/styled-thing'; -import { fonts, fontWithWidth, padding } from '@/styles'; -import { abbreviations, ethereumUtils, haptics, showActionSheetWithOptions } from '@/utils'; - -const InfoButton = styled(Centered)({ - ...padding.object(8, 0), - alignItems: 'center', - bottom: 0, - flex: 0, - height: CoinRowHeight, - justifyContent: 'center', - position: 'absolute', - right: ({ showFavoriteButton }) => (showFavoriteButton ? 40 : 0), - top: 0, - width: 68, -}); - -const Circle = styled(IS_TESTING === 'true' ? View : RadialGradient).attrs(({ theme: { colors } }) => ({ - center: [0, 15], - colors: colors.gradients.lightestGrey, -}))({ - borderRadius: 15, - height: 30, - margin: 10, - overflow: 'hidden', - width: 30, -}); - -const Icon = styled(Text).attrs(({ theme: { colors } }) => ({ - align: 'center', - color: colors.alpha(colors.blueGreyDark, 0.3), - letterSpacing: 'zero', - size: 'lmedium', - weight: 'bold', -}))({ - ...fontWithWidth(fonts.weight.bold), - height: '100%', - lineHeight: 30, - width: '100%', -}); - -const CoinRowActionsEnum = { - blockExplorer: 'blockExplorer', - copyAddress: 'copyAddress', -}; - -const CoinRowActions = { - [CoinRowActionsEnum.copyAddress]: { - actionKey: CoinRowActionsEnum.copyAddress, - actionTitle: lang.t('wallet.action.copy_contract_address'), - icon: { - iconType: 'SYSTEM', - iconValue: 'doc.on.doc', - }, - }, -}; - -const buildBlockExplorerAction = chainId => { - const blockExplorerText = lang.t('exchange.coin_row.view_on', { - blockExplorerName: startCase(ethereumUtils.getBlockExplorer({ chainId })), - }); - return { - actionKey: CoinRowActionsEnum.blockExplorer, - actionTitle: blockExplorerText, - icon: { - iconType: 'SYSTEM', - iconValue: 'link', - }, - }; -}; - -const CoinRowInfoButton = ({ item, onCopySwapDetailsText, showFavoriteButton }) => { - const { setClipboard } = useClipboard(); - const handleCopyContractAddress = useCallback( - address => { - haptics.selection(); - setClipboard(address); - onCopySwapDetailsText(address); - }, - [onCopySwapDetailsText, setClipboard] - ); - - const onPressAndroid = useCallback(() => { - const blockExplorerText = `View on ${startCase(ethereumUtils.getBlockExplorer({ chainId: item?.chainId }))}`; - const androidContractActions = [lang.t('wallet.action.copy_contract_address'), blockExplorerText, lang.t('button.cancel')]; - - showActionSheetWithOptions( - { - cancelButtonIndex: 2, - options: androidContractActions, - showSeparators: true, - title: `${item?.name} (${item?.symbol})`, - }, - idx => { - if (idx === 0) { - handleCopyContractAddress(item?.address); - } - if (idx === 1) { - ethereumUtils.openTokenEtherscanURL({ address: item?.address, chainId: item?.chainId }); - } - } - ); - }, [item, handleCopyContractAddress]); - - const menuConfig = useMemo(() => { - const blockExplorerAction = buildBlockExplorerAction(item?.chainId); - return { - menuItems: [ - blockExplorerAction, - { - ...CoinRowActions[CoinRowActionsEnum.copyAddress], - discoverabilityTitle: abbreviations.formatAddressForDisplay(item?.address), - }, - ], - menuTitle: `${item?.name} (${item?.symbol})`, - }; - }, [item?.address, item?.chainId, item?.name, item?.symbol]); - - const handlePressMenuItem = useCallback( - ({ nativeEvent: { actionKey } }) => { - if (actionKey === CoinRowActionsEnum.copyAddress) { - handleCopyContractAddress(item?.address); - } else if (actionKey === CoinRowActionsEnum.blockExplorer) { - ethereumUtils.openTokenEtherscanURL({ address: item?.address, chainId: item?.chainId }); - } - }, - [item, handleCopyContractAddress] - ); - return ( - - - - - 􀅳 - - - - - ); -}; - -export default CoinRowInfoButton; diff --git a/src/components/coin-row/FastTransactionCoinRow.tsx b/src/components/coin-row/FastTransactionCoinRow.tsx index f0fa251c68f..d1cea9dcc1e 100644 --- a/src/components/coin-row/FastTransactionCoinRow.tsx +++ b/src/components/coin-row/FastTransactionCoinRow.tsx @@ -23,8 +23,8 @@ import { import { TwoCoinsIcon } from '../coin-icon/TwoCoinsIcon'; import Spinner from '../Spinner'; import * as lang from '@/languages'; -import RainbowCoinIcon from '../coin-icon/RainbowCoinIcon'; -import { checkForPendingSwap } from '@/screens/transaction-details/helpers/checkForPendingSwap'; +import RainbowCoinIcon from '@/components/coin-icon/RainbowCoinIcon'; +import { checkForPendingSwap } from '@/helpers/checkForPendingSwap'; export const getApprovalLabel = ({ approvalAmount, asset, type }: Pick) => { if (!approvalAmount || !asset) return; diff --git a/src/components/coin-row/RequestCoinRow.js b/src/components/coin-row/RequestCoinRow.js index 6adbe5b0b29..c0f027524d8 100644 --- a/src/components/coin-row/RequestCoinRow.js +++ b/src/components/coin-row/RequestCoinRow.js @@ -1,7 +1,6 @@ import { addHours, differenceInMinutes, isPast } from 'date-fns'; import lang from 'i18n-js'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { useDispatch } from 'react-redux'; import { useTheme } from '../../theme/ThemeContext'; import { ButtonPressAnimation } from '../animations'; import { RequestCoinIcon } from '../coin-icon'; @@ -9,9 +8,9 @@ import { RowWithMargins } from '../layout'; import { Emoji, Text } from '../text'; import CoinName from './CoinName'; import CoinRow from './CoinRow'; -import { removeRequest } from '@/redux/requests'; import styled from '@/styled-thing'; import { handleWalletConnectRequest } from '@/utils/requestNavigationHandlers'; +import { removeWalletConnectRequest } from '@/state/walletConnectRequests'; const getPercentageOfTimeElapsed = (startDate, endDate) => { const originalDifference = differenceInMinutes(endDate, startDate); @@ -48,7 +47,6 @@ const TopRow = ({ expirationColor, expiresAt }) => { const RequestCoinRow = ({ item, ...props }) => { const buttonRef = useRef(); - const dispatch = useDispatch(); const [expiresAt, setExpiresAt] = useState(null); const [expirationColor, setExpirationColor] = useState(null); const [percentElapsed, setPercentElapsed] = useState(null); @@ -67,9 +65,11 @@ const RequestCoinRow = ({ item, ...props }) => { const handleExpiredRequests = useCallback(() => { if (isPast(expiresAt)) { - dispatch(removeRequest(item.requestId)); + removeWalletConnectRequest({ + walletConnectRequestId: item.requestId, + }); } - }, [dispatch, expiresAt, item.requestId]); + }, [expiresAt, item.requestId]); const handlePressOpen = useCallback(() => { handleWalletConnectRequest(item); diff --git a/src/components/discover/DiscoverSearchInput.js b/src/components/discover/DiscoverSearchInput.js index 038db1c34b6..c9b31dc4189 100644 --- a/src/components/discover/DiscoverSearchInput.js +++ b/src/components/discover/DiscoverSearchInput.js @@ -13,29 +13,29 @@ import styled from '@/styled-thing'; import { margin, padding } from '@/styles'; import { deviceUtils } from '@/utils'; import DiscoverSheetContext from '@/screens/discover/DiscoverScreenContext'; -import { getChainsName } from '@/chains'; +import { useBackendNetworksStore } from '@/state/backendNetworks/backendNetworks'; -export const ExchangeSearchHeight = 40; -const ExchangeSearchWidth = deviceUtils.dimensions.width - 30; +const SearchHeight = 40; +const SearchWidth = deviceUtils.dimensions.width - 30; const Container = styled(Row)(({ isSearchModeEnabled, theme: { colors } }) => ({ ...margin.object(0, 15, isSearchModeEnabled ? 8 : 0), ...(isSearchModeEnabled ? padding.object(0, 37, 0, 12) : padding.object(0)), backgroundColor: colors.transparent, - borderRadius: ExchangeSearchHeight / 2, - height: ExchangeSearchHeight, + borderRadius: SearchHeight / 2, + height: SearchHeight, overflow: 'hidden', })); const BackgroundGradient = styled(RadialGradient).attrs(({ isDiscover, theme: { colors } }) => ({ - center: [ExchangeSearchWidth, ExchangeSearchWidth / 2], + center: [SearchWidth, SearchWidth / 2], colors: isDiscover ? colors.gradients.searchBar : colors.gradients.lightGreyTransparent, }))({ - height: ExchangeSearchWidth, + height: SearchWidth, position: 'absolute', - top: -(ExchangeSearchWidth - ExchangeSearchHeight) / 2, - transform: [{ scaleY: ExchangeSearchHeight / ExchangeSearchWidth }], - width: ExchangeSearchWidth, + top: -(SearchWidth - SearchHeight) / 2, + transform: [{ scaleY: SearchHeight / SearchWidth }], + width: SearchWidth, }); const SearchIcon = styled(Text).attrs(({ theme: { colors } }) => ({ @@ -98,7 +98,7 @@ const timingConfig = { duration: 300, }; -const ExchangeSearch = ( +const DiscoverSearchInput = ( { isDiscover, isFetching, @@ -132,7 +132,7 @@ const ExchangeSearch = ( const placeholder = useMemo(() => { if (!currentChainId) return placeholderText; return lang.t('button.exchange_search_placeholder_network', { - network: getChainsName()[currentChainId], + network: useBackendNetworksStore.getState().getChainsName()[currentChainId], }); }, [currentChainId, placeholderText]); @@ -188,7 +188,7 @@ const ExchangeSearch = ( value={searchQuery} /> null; - -export default function ConfirmExchangeButton({ - chainId, - disabled, - loading, - isHighPriceImpact, - quoteError, - onPressViewDetails, - onSubmit, - testID, - tradeDetails, - type = ExchangeModalTypes.swap, - isSufficientBalance, - isBridgeSwap, - ...props -}) { - const { inputCurrency, outputCurrency } = useSwapCurrencies(); - const asset = outputCurrency ?? inputCurrency; - const { isSufficientGas, isValidGas, isGasReady } = useGas(); - const { name: routeName } = useRoute(); - const { navigate } = useNavigation(); - const [isSwapSubmitting, setIsSwapSubmitting] = useState(false); - const { isHardwareWallet } = useWallets(); - - const isSwapDetailsRoute = routeName === Routes.SWAP_DETAILS_SHEET; - const shouldOpenSwapDetails = tradeDetails && !isSwapDetailsRoute; - - const { colors, isDarkMode } = useTheme(); - - const shadows = useMemo( - () => ({ - default: [[0, 10, 30, darkModeThemeColors.shadow, 0.4]], - disabled: [ - [0, 10, 30, colors.shadow, isDarkMode ? 0 : 0.2], - [0, 5, 15, isDarkMode ? colors.shadow : lightModeThemeColors.blueGreyDark50, 0.4], - ], - }), - [colors, isDarkMode] - ); - - const colorForAsset = useColorForAsset(asset, undefined, true, true); - - const disabledButtonColor = isSwapDetailsRoute - ? isDarkMode - ? darkModeThemeColors.blueGreyDark04 - : lightModeThemeColors.blueGreyDark50 - : darkModeThemeColors.blueGreyDark04; - - const { buttonColor, shadowsForAsset } = useMemo(() => { - const color = quoteError - ? disabledButtonColor - : asset.address === ETH_ADDRESS - ? colors.appleBlue - : isSwapDetailsRoute - ? colorForAsset - : makeColorMoreChill(colorForAsset, (isSwapDetailsRoute ? colors : darkModeThemeColors).light); - - return { - buttonColor: color, - shadowsForAsset: [ - [0, 10, 30, colors.shadow, 0.2], - [0, 5, 15, isDarkMode ? colors.trueBlack : color, 0.4], - ], - }; - }, [asset.address, colorForAsset, colors, disabledButtonColor, quoteError, isDarkMode, isSwapDetailsRoute]); - - let label = ''; - let explainerType = null; - - if (type === ExchangeModalTypes.swap) { - label = `􀕹 ${lang.t('button.confirm_exchange.review')}`; - } - if (loading) { - label = lang.t('button.confirm_exchange.fetching_quote'); - } else if (!isSufficientBalance) { - label = lang.t('button.confirm_exchange.insufficient_funds'); - } else if (isSufficientGas != null && !isSufficientGas) { - label = lang.t('button.confirm_exchange.insufficient_token', { - tokenName: getChainsNativeAsset()[chainId].symbol, - }); - } else if (!isValidGas && isGasReady) { - label = lang.t('button.confirm_exchange.invalid_fee'); - } else if (isSwapDetailsRoute) { - if (isSwapSubmitting) { - label = lang.t('button.confirm_exchange.submitting'); - } else if (isBridgeSwap) { - label = `${lang.t('button.confirm_exchange.bridge')}`; - } else { - label = isHighPriceImpact ? lang.t('button.confirm_exchange.swap_anyway') : `${lang.t('button.confirm_exchange.swap')}`; - } - } else if (disabled) { - label = lang.t('button.confirm_exchange.enter_amount'); - } - - if (quoteError) { - const error = handleSwapErrorCodes(quoteError); - label = error.buttonLabel; - explainerType = error.explainerType; - } - - const handleExplainer = useCallback(() => { - Keyboard.dismiss(); - navigate(Routes.EXPLAIN_SHEET, { - inputCurrency, - outputCurrency, - type: explainerType, - }); - }, [explainerType, inputCurrency, navigate, outputCurrency]); - - const isDisabled = disabled || !isSufficientBalance || !isSufficientGas || !isValidGas || !isSufficientGas; - - const onSwap = useCallback(async () => { - setIsSwapSubmitting(true); - const submitted = await onSubmit(setIsSwapSubmitting); - setIsSwapSubmitting(submitted); - }, [onSubmit]); - - return ( - - - - - - - - ); -} diff --git a/src/components/exchange/CurrencySelectModalHeader.tsx b/src/components/exchange/CurrencySelectModalHeader.tsx deleted file mode 100644 index 0cf43b17ed6..00000000000 --- a/src/components/exchange/CurrencySelectModalHeader.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { useRoute } from '@react-navigation/native'; -import React, { useCallback } from 'react'; -import { delayNext } from '../../hooks/useMagicAutofocus'; -import { BackButton } from '../header'; -import { SheetHandleFixedToTop } from '../sheet'; -import { Box, Inset, Text } from '@/design-system'; -import { useNavigation } from '@/navigation'; -import Routes from '@/navigation/routesNames'; -import RainbowCoinIcon from '../coin-icon/RainbowCoinIcon'; -import { useTheme } from '@/theme'; -import { useBackendNetworksStore } from '@/state/backendNetworks/backendNetworks'; - -export const CurrencySelectModalHeaderHeight = 59; - -export default function CurrencySelectModalHeader({ - handleBackButton, - showBackButton, - showHandle, - testID, -}: { - handleBackButton: () => void; - showBackButton: boolean; - showHandle: boolean; - testID: string; -}) { - const { navigate, getState: dangerouslyGetState } = useNavigation(); - const { - params: { defaultOutputAsset, title, showCoinIcon }, - } = useRoute(); - const theme = useTheme(); - - const handlePressBack = useCallback(() => { - // @ts-expect-error – updating read-only property - dangerouslyGetState().index = 1; - delayNext(); - handleBackButton(); - navigate(Routes.MAIN_EXCHANGE_SCREEN); - }, [dangerouslyGetState, handleBackButton, navigate]); - - return ( - - {showHandle && } - {showBackButton && ( - - {/** @ts-expect-error JavaScript component */} - - - )} - {showCoinIcon && ( - - - - )} - - - {title} - - - ); -} diff --git a/src/components/exchange/ExchangeDetailsButton.js b/src/components/exchange/ExchangeDetailsButton.js deleted file mode 100644 index 049c2768f3e..00000000000 --- a/src/components/exchange/ExchangeDetailsButton.js +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import { ButtonPressAnimation } from '../animations'; -import { Text } from '../text'; -import styled from '@/styled-thing'; -import { lightModeThemeColors, padding } from '@/styles'; - -const ExchangeDetailsButtonLabel = styled(Text).attrs({ - color: lightModeThemeColors.white, - size: 'large', - weight: 'heavy', - ...(android && { lineHeight: 21 }), -})({ - ...padding.object(9), -}); - -export default function ExchangeDetailsButton({ children, disabled, onPress, ...props }) { - return ( - - {children} - - ); -} diff --git a/src/components/exchange/ExchangeDetailsRow.tsx b/src/components/exchange/ExchangeDetailsRow.tsx deleted file mode 100644 index fa38179855e..00000000000 --- a/src/components/exchange/ExchangeDetailsRow.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import lang from 'i18n-js'; -import React, { useEffect } from 'react'; -import Animated, { Easing, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'; -import ExchangeDetailsButton from './ExchangeDetailsButton'; -import PriceImpactWarning from './PriceImpactWarning'; -import { analytics } from '@/analytics'; -import { Box } from '@/design-system'; -import { usePrevious, useSwapCurrencies } from '@/hooks'; - -const defaultPriceImpactScale = 1.15; -const timingConfig = { - duration: 200, - easing: Easing.bezier(0.76, 0, 0.24, 1), -}; - -interface ExchangeDetailsRowProps { - isHighPriceImpact: boolean; - onFlipCurrencies: () => void; - onPressSettings: () => void; - onPressImpactWarning: () => void; - priceImpactColor?: string; - priceImpactNativeAmount?: string | null; - priceImpactPercentDisplay?: string | null; - outputCurrencySymbol?: string | null; - type: string; -} - -export default function ExchangeDetailsRow({ - isHighPriceImpact, - onFlipCurrencies, - onPressSettings, - onPressImpactWarning, - priceImpactColor, - priceImpactNativeAmount, - priceImpactPercentDisplay, - outputCurrencySymbol, - type, -}: ExchangeDetailsRowProps) { - const detailsRowOpacity = useSharedValue(1); - const priceImpactOpacity = useSharedValue(0); - const priceImpactScale = useSharedValue(defaultPriceImpactScale); - const { outputCurrency } = useSwapCurrencies(); - - const detailsRowAnimatedStyle = useAnimatedStyle(() => ({ - opacity: detailsRowOpacity.value, - })); - - const priceImpactAnimatedStyle = useAnimatedStyle(() => ({ - opacity: priceImpactOpacity.value, - transform: [{ scale: priceImpactScale.value }], - })); - - const prevIsHighPriceImpact = usePrevious(isHighPriceImpact); - - useEffect(() => { - if (isHighPriceImpact && !prevIsHighPriceImpact) { - analytics.track('Showing high price impact warning in Swap', { - name: outputCurrency.name, - priceImpact: priceImpactPercentDisplay, - symbol: outputCurrency.symbol, - tokenAddress: outputCurrency.address, - type, - }); - } - }, [isHighPriceImpact, outputCurrency, prevIsHighPriceImpact, priceImpactPercentDisplay, type]); - - useEffect(() => { - if (isHighPriceImpact) { - detailsRowOpacity.value = withTiming(0, timingConfig); - priceImpactOpacity.value = withTiming(1, timingConfig); - priceImpactScale.value = withTiming(1, timingConfig); - } else { - detailsRowOpacity.value = withTiming(1, timingConfig); - priceImpactOpacity.value = withTiming(0, timingConfig); - priceImpactScale.value = withTiming(defaultPriceImpactScale, timingConfig); - } - }, [detailsRowOpacity, isHighPriceImpact, priceImpactOpacity, priceImpactScale]); - - return ( - - - - {/* @ts-expect-error - Javascript Component */} - - 􀄬 {lang.t('exchange.flip')} - - {/* @ts-expect-error - Javascript Component */} - - 􀣋 {lang.t('exchange.settings')} - - - - ); -} diff --git a/src/components/exchange/ExchangeField.tsx b/src/components/exchange/ExchangeField.tsx deleted file mode 100644 index 7b9fca0e005..00000000000 --- a/src/components/exchange/ExchangeField.tsx +++ /dev/null @@ -1,182 +0,0 @@ -import React, { FocusEvent, ForwardRefRenderFunction, MutableRefObject, useCallback, useEffect, useState } from 'react'; -import { StyleProp, TextInput, TouchableWithoutFeedback, ViewStyle } from 'react-native'; -import { TokenSelectionButton } from '../buttons'; -import { ChainBadge, CoinIconSize } from '../coin-icon'; -import { EnDash } from '../text'; -import ExchangeInput from './ExchangeInput'; -import { ChainId } from '@/state/backendNetworks/types'; -import styled from '@/styled-thing'; -import { borders } from '@/styles'; -import { useTheme } from '@/theme'; -import { AccentColorProvider, Box, Space } from '@/design-system'; -import RainbowCoinIcon from '../coin-icon/RainbowCoinIcon'; -import { TokenColors } from '@/graphql/__generated__/metadata'; - -const ExchangeFieldHeight = android ? 64 : 38; -const ExchangeFieldPadding: Space = android ? '15px (Deprecated)' : '19px (Deprecated)'; - -const Input = styled(ExchangeInput).attrs({ - letterSpacing: 'roundedTightest', -})({ - height: ExchangeFieldHeight + (android ? 20 : 0), - marginVertical: -10, -}); - -interface ExchangeFieldProps { - icon?: string; - colors?: TokenColors; - address: string; - color: string; - mainnetAddress?: string; - amount: string | null; - disableCurrencySelection?: boolean; - editable: boolean; - loading: boolean; - type?: string; - chainId: ChainId; - onBlur?: (event: FocusEvent) => void; - onFocus: (event: FocusEvent) => void; - onPressSelectCurrency: (chainId: any) => void; - onTapWhileDisabled?: () => void; - setAmount: (value: string | null) => void; - symbol?: string; - testID: string; - useCustomAndroidMask: boolean; - updateOnFocus: boolean; - style?: StyleProp; -} - -const ExchangeField: ForwardRefRenderFunction = ( - { - icon, - colors, - amount, - color, - disableCurrencySelection, - editable, - loading, - chainId, - onBlur, - onFocus, - onPressSelectCurrency, - onTapWhileDisabled, - setAmount, - symbol, - testID, - useCustomAndroidMask = false, - updateOnFocus = false, - style, - }, - ref -) => { - const inputRef = ref as MutableRefObject; - const theme = useTheme(); - const [value, setValue] = useState(amount); - - const handleFocusField = useCallback(() => { - inputRef?.current?.focus(); - }, [inputRef]); - - const handleBlur = useCallback( - // @ts-expect-error passed to an untyped JS component - event => { - onBlur?.(event); - }, - [onBlur] - ); - const handleFocus = useCallback( - // @ts-expect-error passed to an untyped JS component - event => { - onFocus?.(event); - if (loading) { - setAmount(value); - } - }, - [loading, onFocus, setAmount, value] - ); - - const onChangeText = useCallback( - // @ts-expect-error passed to an untyped JS component - text => { - setAmount(text); - setValue(text); - }, - [setAmount] - ); - - const placeholderTextColor = symbol - ? theme.colors.alpha(theme.colors.blueGreyDark, 0.3) - : theme.colors.alpha(theme.colors.blueGreyDark, 0.1); - - const placeholderText = symbol ? '0' : EnDash.unicode; - - const editing = inputRef?.current?.isFocused?.() ?? false; - - useEffect(() => { - if (!editing || updateOnFocus) { - setValue(amount); - } - }, [amount, editing, updateOnFocus]); - - return ( - - - - - {symbol ? ( - - ) : ( - - - - - - - )} - - - - - - {!disableCurrencySelection && ( - - )} - - ); -}; - -export default React.forwardRef(ExchangeField); diff --git a/src/components/exchange/ExchangeFloatingPanels.tsx b/src/components/exchange/ExchangeFloatingPanels.tsx deleted file mode 100644 index 5584e9c77ca..00000000000 --- a/src/components/exchange/ExchangeFloatingPanels.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React, { ReactNode } from 'react'; -import { View } from 'react-native'; -import { Box, Stack } from '@/design-system'; - -interface ExchangeFloatingPanelsProps { - children: ReactNode; -} - -const ExchangeFloatingPanels = React.forwardRef((props, ref) => { - const children = props.children; - return ( - - {children} - - ); -}); - -ExchangeFloatingPanels.displayName = 'ExchangeFloatingPanels'; - -export default ExchangeFloatingPanels; diff --git a/src/components/exchange/ExchangeInputField.tsx b/src/components/exchange/ExchangeInputField.tsx deleted file mode 100644 index 2cd86615e86..00000000000 --- a/src/components/exchange/ExchangeInputField.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import React, { MutableRefObject } from 'react'; -import { TextInput } from 'react-native'; -import { ColumnWithMargins, Row } from '../layout'; -import ExchangeField from './ExchangeField'; -import ExchangeMaxButton from './ExchangeMaxButton'; -import ExchangeNativeField from './ExchangeNativeField'; -import { ChainId } from '@/state/backendNetworks/types'; -import styled from '@/styled-thing'; -import { TokenColors } from '@/graphql/__generated__/metadata'; - -const Container = styled(ColumnWithMargins).attrs({ margin: 5 })({ - paddingTop: android ? 0 : 6, - width: '100%', - zIndex: 1, -}); - -const NativeFieldRow = styled(Row).attrs({ - align: 'center', - justify: 'space-between', -})({ - height: android ? 16 : 32, - paddingLeft: 19, -}); - -interface ExchangeInputFieldProps { - color: string; - disableInputCurrencySelection: boolean; - editable: boolean; - loading: boolean; - nativeAmount: string | null; - nativeCurrency: string; - nativeFieldRef: MutableRefObject; - chainId: ChainId; - onFocus: ({ target }: { target: Element }) => void; - onPressMaxBalance: () => void; - onPressSelectInputCurrency: (chainId: ChainId) => void; - inputAmount: string | null; - inputCurrencyIcon?: string; - inputCurrencyColors?: TokenColors; - inputCurrencyAddress: string; - inputCurrencyMainnetAddress?: string; - inputCurrencyNetwork?: string; - inputCurrencySymbol?: string; - inputFieldRef: MutableRefObject; - setInputAmount: (value: string | null) => void; - setNativeAmount: (value: string | null) => void; - updateAmountOnFocus: boolean; - testID: string; -} - -export default function ExchangeInputField({ - color, - disableInputCurrencySelection, - editable, - inputAmount, - inputCurrencyAddress, - inputCurrencyMainnetAddress, - inputCurrencySymbol, - inputCurrencyIcon, - inputCurrencyColors, - inputFieldRef, - loading, - nativeAmount, - nativeCurrency, - nativeFieldRef, - chainId, - inputCurrencyNetwork, - onFocus, - onPressMaxBalance, - onPressSelectInputCurrency, - setInputAmount, - setNativeAmount, - testID, - updateAmountOnFocus, -}: ExchangeInputFieldProps) { - return ( - - - - - - - - ); -} diff --git a/src/components/exchange/ExchangeMaxButton.tsx b/src/components/exchange/ExchangeMaxButton.tsx deleted file mode 100644 index 548f79859a6..00000000000 --- a/src/components/exchange/ExchangeMaxButton.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import lang from 'i18n-js'; -import React from 'react'; -import { ButtonPressAnimation } from '../animations'; -import { Box, Text } from '@/design-system'; -import styled from '@/styled-thing'; -import { useTheme } from '@/theme'; - -const Container = styled(ButtonPressAnimation)({ - marginRight: 4, -}); - -interface ExchangeMaxButtonProps { - color: string; - disabled: boolean; - onPress: () => void; - testID: string; -} - -export default function ExchangeMaxButton({ color, disabled, onPress, testID }: ExchangeMaxButtonProps) { - const { colors } = useTheme(); - - return ( - - - - 􀜍 {lang.t('exchange.max')} - - - - ); -} diff --git a/src/components/exchange/ExchangeNativeField.tsx b/src/components/exchange/ExchangeNativeField.tsx deleted file mode 100644 index 84acc1a3fb0..00000000000 --- a/src/components/exchange/ExchangeNativeField.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import React, { ForwardRefRenderFunction, MutableRefObject, useCallback, useEffect, useMemo, useState } from 'react'; -import { TextInput, TouchableWithoutFeedback } from 'react-native'; -import { Row } from '../layout'; -import ExchangeInput from './ExchangeInput'; -import { supportedNativeCurrencies } from '@/references'; -import styled from '@/styled-thing'; -import { fonts } from '@/styles'; -import { useTheme } from '@/theme'; -import { Box, Text } from '@/design-system'; -import { NativeCurrencyKey } from '@/entities'; - -const NativeInput = styled(ExchangeInput).attrs({ - letterSpacing: fonts.letterSpacing.roundedTight, - size: fonts.size.larger, - weight: fonts.weight.regular, -})({ - height: ({ height }: { height: number }) => height, -}); - -interface ExchangeNativeFieldProps { - color: string; - editable: boolean; - height: number; - loading: boolean; - nativeAmount: string | null; - nativeCurrency: string; - onFocus: ({ target }: { target: Element }) => void; - setNativeAmount: (value: string | null) => void; - updateOnFocus: boolean; - testID: string; -} - -const ExchangeNativeField: ForwardRefRenderFunction = ( - { color, editable, height, loading, nativeAmount, nativeCurrency, onFocus, setNativeAmount, updateOnFocus, testID }, - ref -) => { - const nativeFieldRef = ref as MutableRefObject; - - const [value, setValue] = useState(nativeAmount); - - const { mask, placeholder, symbol } = supportedNativeCurrencies[nativeCurrency as NativeCurrencyKey]; - - const handleFocusNativeField = useCallback(() => nativeFieldRef?.current?.focus(), [nativeFieldRef]); - - const handleFocus = useCallback( - // @ts-expect-error passed to an untyped JS component - event => { - onFocus?.(event); - if (loading) { - setNativeAmount(value); - } - }, - [loading, onFocus, setNativeAmount, value] - ); - - const onChangeText = useCallback( - // @ts-expect-error passed to an untyped JS component - text => { - setNativeAmount(text); - setValue(text); - }, - [setNativeAmount] - ); - - const { colors } = useTheme(); - - const isFocused = nativeFieldRef?.current?.isFocused?.(); - - const nativeAmountColor = useMemo(() => { - const nativeAmountExists = typeof nativeAmount === 'string' && nativeAmount.length > 0; - - const color = isFocused ? colors.dark : colors.blueGreyDark; - const opacity = isFocused ? 1 : nativeAmountExists ? 0.5 : 0.3; - - return colors.alpha(color, opacity); - }, [colors, isFocused, nativeAmount]); - - useEffect(() => { - if (!isFocused || updateOnFocus) { - setValue(nativeAmount); - } - }, [nativeAmount, isFocused, updateOnFocus]); - - return ( - - - - - {symbol} - - - - - - ); -}; - -export default React.forwardRef(ExchangeNativeField); diff --git a/src/components/exchange/ExchangeNotch.tsx b/src/components/exchange/ExchangeNotch.tsx deleted file mode 100644 index 96bea8e3a4a..00000000000 --- a/src/components/exchange/ExchangeNotch.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import React from 'react'; -import FastImage from 'react-native-fast-image'; -import ExchangeNotchLeft from '@/assets/exchangeNotchLeft.png'; -import ExchangeNotchLeftDark from '@/assets/exchangeNotchLeftDark.png'; -import ExchangeNotchMiddle from '@/assets/exchangeNotchMiddle.png'; -import ExchangeNotchMiddleDark from '@/assets/exchangeNotchMiddleDark.png'; -import ExchangeNotchRight from '@/assets/exchangeNotchRight.png'; -import ExchangeNotchRightDark from '@/assets/exchangeNotchRightDark.png'; -import { Box } from '@/design-system'; -import { useDimensions } from '@/hooks'; -import styled from '@/styled-thing'; -import { useTheme } from '@/theme'; - -const notchHeight = 48; -const notchSideWidth = 78; - -const NotchMiddle = styled(FastImage).attrs(({ isDarkMode }: { isDarkMode: boolean }) => ({ - resizeMode: FastImage.resizeMode.stretch, - source: isDarkMode ? ExchangeNotchMiddleDark : ExchangeNotchMiddle, -}))({ - height: notchHeight, - left: 0, - width: ({ deviceWidth }: { deviceWidth: number }) => deviceWidth - notchSideWidth * 2, -}); - -const NotchSide = styled(FastImage)({ - height: android ? notchHeight + 2 : notchHeight, - left: 0, - width: notchSideWidth, -}); - -interface ExchangeNotchProps { - testID: string; -} - -export default function ExchangeNotch({ testID }: ExchangeNotchProps) { - const { width: deviceWidth } = useDimensions(); - const { isDarkMode } = useTheme(); - return ( - - - - - - ); -} diff --git a/src/components/exchange/ExchangeOutputField.tsx b/src/components/exchange/ExchangeOutputField.tsx deleted file mode 100644 index c24e811408d..00000000000 --- a/src/components/exchange/ExchangeOutputField.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import React, { MutableRefObject } from 'react'; -import { TextInput } from 'react-native'; -import ExchangeField from './ExchangeField'; -import { Box } from '@rainbow-me/design-system'; -import { TokenColors } from '@/graphql/__generated__/metadata'; -import { ChainId } from '@/state/backendNetworks/types'; - -interface ExchangeOutputFieldProps { - color: string; - chainId: ChainId; - editable: boolean; - loading: boolean; - onFocus: ({ target }: { target: Element }) => void; - onPressSelectOutputCurrency: () => void; - onTapWhileDisabled?: () => void; - outputAmount: string | null; - outputCurrencyIcon?: string; - outputCurrencyColors?: TokenColors; - outputCurrencyAddress: string; - outputCurrencyMainnetAddress?: string; - outputCurrencyNetwork?: string; - outputCurrencySymbol?: string; - outputFieldRef: MutableRefObject; - setOutputAmount: (value: string | null) => void; - updateAmountOnFocus: boolean; - testID: string; -} - -export default function ExchangeOutputField({ - color, - editable, - loading, - chainId, - onFocus, - onPressSelectOutputCurrency, - onTapWhileDisabled, - outputAmount, - outputCurrencyIcon, - outputCurrencyColors, - outputCurrencyAddress, - outputCurrencyMainnetAddress, - outputCurrencySymbol, - outputFieldRef, - setOutputAmount, - updateAmountOnFocus, - testID, -}: ExchangeOutputFieldProps) { - return ( - - - - ); -} diff --git a/src/components/exchange/ExchangeSearch.tsx b/src/components/exchange/ExchangeSearch.tsx deleted file mode 100644 index 9f5302788ae..00000000000 --- a/src/components/exchange/ExchangeSearch.tsx +++ /dev/null @@ -1,235 +0,0 @@ -import lang from 'i18n-js'; -import { isEmpty } from 'lodash'; -import React, { ForwardRefRenderFunction, MutableRefObject, useCallback, useContext, useEffect, useMemo, useRef } from 'react'; -import LinearGradient from 'react-native-linear-gradient'; -import Animated, { Easing, useAnimatedStyle, useSharedValue, withRepeat, withTiming } from 'react-native-reanimated'; -import Spinner from '../../assets/chartSpinner.png'; -import DiscoverSheetContext from '../../screens/discover/DiscoverScreenContext'; -import { ClearInputDecorator, Input } from '../inputs'; -import { analytics } from '@/analytics'; -import { ImgixImage } from '@/components/images'; -import styled from '@/styled-thing'; -import { margin, Colors } from '@/styles'; -import { deviceUtils } from '@/utils'; -import ShadowStack from '@/react-native-shadow-stack'; -import { TextInput } from 'react-native'; -import { useTheme } from '@/theme'; -import { Box, Text } from '@/design-system'; -import { Source } from 'react-native-fast-image'; -import { IS_TEST } from '@/env'; -import { ChainId } from '@rainbow-me/swaps'; - -export const ExchangeSearchHeight = 40; -const DoneButtonWidth = 52; -const ExchangeSearchWidth = deviceUtils.dimensions.width - 40; - -const ShadowContainer = styled(ShadowStack)(() => ({ - ...margin.object(0, 20, 20, 20), -})); - -const BackgroundGradient = styled(LinearGradient).attrs(({ theme: { colors } }: { theme: { colors: Colors } }) => ({ - colors: colors.gradients.offWhite, - end: { x: 0.5, y: 1 }, - start: { x: 0.5, y: 0 }, -}))({ - borderRadius: ExchangeSearchHeight / 2, - height: ExchangeSearchWidth, - left: 0, - overflow: 'hidden', - position: 'absolute', - top: -(ExchangeSearchWidth - ExchangeSearchHeight) / 2, - transform: [{ scaleY: ExchangeSearchHeight / ExchangeSearchWidth }], - width: ExchangeSearchWidth, -}); - -const SearchInput = styled(Input).attrs( - ({ - theme: { colors }, - isSearchModeEnabled, - clearTextOnFocus, - }: { - theme: { colors: Colors }; - isSearchModeEnabled: boolean; - clearTextOnFocus: boolean; - }) => ({ - blurOnSubmit: false, - clearTextOnFocus, - color: colors.alpha(colors.blueGreyDark, 0.8), - enablesReturnKeyAutomatically: true, - keyboardAppearance: 'dark', - keyboardType: 'ascii-capable', - lineHeight: 'looserLoose', - placeholderTextColor: colors.alpha(colors.blueGreyDark, 0.6), - returnKeyType: 'search', - selectionColor: isSearchModeEnabled ? colors.appleBlue : colors.transparent, - size: 'large', - spellCheck: false, - weight: 'semibold', - }) -)({ - ...(android ? { marginBottom: -10, marginTop: -6 } : {}), - flex: 1, - height: ios ? 39 : 56, - marginBottom: 1, - marginLeft: ({ isSearchModeEnabled }: { isSearchModeEnabled: boolean }) => (isSearchModeEnabled ? 4 : 0), - textAlign: ({ isSearchModeEnabled }: { isSearchModeEnabled: boolean }) => (isSearchModeEnabled ? 'left' : 'center'), -}); - -const rotationConfig = { - duration: 500, - easing: Easing.linear, -}; - -const timingConfig = { - duration: 300, -}; - -const ExchangeSearchShadowsFactory = (c: Colors) => [ - [0, 7, 21, c.shadow, 0.15], - [0, 3.5, 10.5, c.shadow, 0.04], -]; - -interface ExchangeSearchProps { - isDiscover?: boolean; - isFetching: boolean; - isSearching: boolean; - onChangeText: (arg: string) => void; - onFocus: ({ target }: any) => void; - searchQuery: string; - testID: string; - placeholderText?: string; - clearTextOnFocus: boolean; -} - -const ExchangeSearch: ForwardRefRenderFunction = ( - { - isDiscover, - isFetching, - isSearching, - onChangeText, - onFocus, - searchQuery, - testID, - placeholderText = lang.t('button.exchange_search_placeholder'), - clearTextOnFocus = true, - }, - ref -) => { - const inputRef = ref as MutableRefObject; - const handleClearInput = useCallback(() => { - if (isDiscover && searchQuery.length > 1) { - analytics.track('Search Query', { - category: 'discover', - length: searchQuery.length, - query: searchQuery, - }); - } - inputRef?.current?.clear(); - onChangeText?.(''); - }, [isDiscover, searchQuery, inputRef, onChangeText]); - - const ExchangeSearchWidthFocused = isDiscover ? ExchangeSearchWidth - DoneButtonWidth : ExchangeSearchWidth; - - const spinnerRotation = useSharedValue(0); - const spinnerScale = useSharedValue(0); - const { isSearchModeEnabled = true } = useContext(DiscoverSheetContext) ?? { - isSearchModeEnabled: true, - }; - - const spinnerTimeout = useRef(); - const { colors } = useTheme(); - const shadows = useMemo(() => ExchangeSearchShadowsFactory(colors), [colors]); - - useEffect(() => { - if ((isFetching || isSearching) && !isEmpty(searchQuery)) { - clearTimeout(spinnerTimeout.current as NodeJS.Timeout); - spinnerRotation.value = 0; - spinnerRotation.value = withRepeat(withTiming(360, rotationConfig), -1, false); - spinnerScale.value = withTiming(1, timingConfig); - } else { - spinnerScale.value = withTiming(0, timingConfig); - spinnerTimeout.current = setTimeout(() => (spinnerRotation.value = 0), timingConfig.duration); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isFetching, isSearching, searchQuery]); - - const searchIconStyle = useAnimatedStyle(() => { - return { - opacity: 1 - spinnerScale.value, - transform: [{ scale: 1 - spinnerScale.value }], - }; - }); - - const spinnerStyle = useAnimatedStyle(() => { - return { - opacity: spinnerScale.value, - transform: [{ rotate: `${spinnerRotation.value}deg` }, { scale: spinnerScale.value }], - }; - }); - - return ( - - - - - {isSearchModeEnabled && !IS_TEST && ( - <> - - - 􀊫 - - - - - - - - )} - - - - - ); -}; - -export default React.forwardRef(ExchangeSearch); diff --git a/src/components/exchange/NetworkSwitcher.js b/src/components/exchange/NetworkSwitcher.js deleted file mode 100644 index e2d8d9b5c6b..00000000000 --- a/src/components/exchange/NetworkSwitcher.js +++ /dev/null @@ -1,112 +0,0 @@ -import lang from 'i18n-js'; -import React from 'react'; -import RadialGradient from 'react-native-radial-gradient'; -import Divider from '@/components/Divider'; -import ChainBadge from '@/components/coin-icon/ChainBadge'; -import { ContextMenuButton } from '@/components/context-menu'; -import { Column, Row } from '../layout'; -import { Text } from '../text'; -import { padding, position } from '@/styles'; -import { showActionSheetWithOptions } from '@/utils'; -import { EthCoinIcon } from '../coin-icon/EthCoinIcon'; -import { getChainsLabel, getChainsName, getDefaultChains, supportedSwapChainIds } from '@/chains'; -import { ChainId } from '@/chains/types'; - -const networkMenuItems = supportedSwapChainIds - .map(chainId => getDefaultChains()[chainId]) - .map(chain => ({ - actionKey: `${chain.id}`, - actionTitle: getChainsLabel()[chain.id], - icon: { - iconType: 'ASSET', - iconValue: `${getChainsName()[chain.id]}Badge${chain.id === ChainId.mainnet ? '' : 'NoShadow'}`, - }, - })); - -const androidNetworkMenuItems = supportedSwapChainIds.map(chainId => getDefaultChains()[chainId].id); - -const NetworkSwitcherv1 = ({ - colors, - hideDivider, - marginVertical = 12, - marginHorizontal = 19, - currentChainId, - setCurrentChainId, - testID, - prominent, -}) => { - const radialGradientProps = { - center: [0, 1], - colors: colors.gradients.lightGreyWhite, - pointerEvents: 'none', - style: { - ...position.coverAsObject, - overflow: 'hidden', - }, - }; - - const handleOnPressMenuItem = useCallback( - ({ nativeEvent: { actionKey } }) => { - setCurrentChainId(actionKey); - }, - [setCurrentChainId] - ); - const onPressAndroid = useCallback(() => { - const networkActions = androidNetworkMenuItems; - showActionSheetWithOptions( - { - options: networkActions, - showSeparators: true, - }, - idx => { - if (idx !== undefined) { - setCurrentChainId(networkActions[idx]); - } - } - ); - }, [setCurrentChainId]); - - return ( - <> - - - - - {currentChainId !== 1 ? : } - - - - {lang.t('expanded_state.swap.network_switcher', { - network: getChainsName()[currentChainId], - })} - - - - - 􀆈 - - - - - {hideDivider ? null : } - - ); -}; - -export default NetworkSwitcherv1; diff --git a/src/components/exchange/NetworkSwitcherv2.tsx b/src/components/exchange/NetworkSwitcherv2.tsx deleted file mode 100644 index e43ee284c83..00000000000 --- a/src/components/exchange/NetworkSwitcherv2.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import React, { useRef, useMemo } from 'react'; -import { ScrollView } from 'react-native'; -import RadialGradient from 'react-native-radial-gradient'; -import { ButtonPressAnimation } from '../animations'; -import ChainBadge from '../coin-icon/ChainBadge'; -import { Bleed, Box, Columns, Inline, Text } from '@/design-system'; -import { position } from '@rainbow-me/styles'; -import { useTheme } from '@/theme'; -import { EthCoinIcon } from '../coin-icon/EthCoinIcon'; -import { ChainId } from '@/state/backendNetworks/types'; -import { useBackendNetworksStore } from '@/state/backendNetworks/backendNetworks'; - -const NetworkSwitcherv2 = ({ - currentChainId, - setCurrentChainId, - testID, -}: { - currentChainId: number; - setCurrentChainId(chainId: number): void; - testID: string; -}) => { - const { colors } = useTheme(); - const scrollViewRef = useRef(null); - const networkMenuItems = useMemo(() => { - return Object.values(useBackendNetworksStore.getState().getDefaultChains()) - .filter(chain => useBackendNetworksStore.getState().getSupportedChainIds().includes(chain.id)) - .map(chain => ({ - chainId: chain.id, - title: useBackendNetworksStore.getState().getChainsLabel()[chain.id], - })); - }, []); - - const radialGradientProps = (chainId: ChainId) => { - return { - center: [0, 1], - colors: [colors.alpha(colors.networkColors[chainId], 0.1), colors.alpha(colors.networkColors[chainId], 0.02)], - pointerEvents: 'none', - style: { - ...position.coverAsObject, - overflow: 'hidden', - }, - }; - }; - - return ( - <> - - - - {networkMenuItems.map(({ chainId, title }) => { - const isSelected = currentChainId === chainId; - return ( - setCurrentChainId(chainId)} - padding="8px" - testID={`${testID}-${chainId}`} - > - {isSelected && ( - - )} - - {chainId === ChainId.mainnet ? ( - - ) : ( - - )} - - - {title} - - - - - ); - })} - - - - - ); -}; - -export default NetworkSwitcherv2; diff --git a/src/components/exchange/PriceImpactWarning.tsx b/src/components/exchange/PriceImpactWarning.tsx deleted file mode 100644 index 06d12345758..00000000000 --- a/src/components/exchange/PriceImpactWarning.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import lang from 'i18n-js'; -import React from 'react'; -import { StyleProp, ViewProps, ViewStyle } from 'react-native'; -import Animated from 'react-native-reanimated'; -import { ButtonPressAnimation } from '../animations'; -import { Box, ColorModeProvider, Inline, Text } from '@/design-system'; -import { position } from '@/styles'; -import { NO_PRICE_DATA_PERCENTAGE } from '@/hooks/usePriceImpactDetails'; - -interface PriceImpactWarningProps extends ViewProps { - onPress: () => void; - isHighPriceImpact: boolean; - priceImpactColor?: string; - priceImpactNativeAmount?: string | null; - priceImpactPercentDisplay?: string | null; - outputCurrencySymbol?: string | null; - style?: StyleProp; -} - -export default function PriceImpactWarning({ - onPress, - isHighPriceImpact, - priceImpactColor = 'primary', - priceImpactNativeAmount, - priceImpactPercentDisplay, - outputCurrencySymbol, - style, - ...props -}: PriceImpactWarningProps) { - const headingValue = priceImpactNativeAmount ?? priceImpactPercentDisplay; - const hasPriceData = priceImpactPercentDisplay !== NO_PRICE_DATA_PERCENTAGE; - const impactMsg = !hasPriceData - ? `${outputCurrencySymbol} ${lang.t('exchange.price_impact.no_data')}` - : lang.t('exchange.price_impact.small_market'); - return ( - - - {!isHighPriceImpact && headingValue && ( - - - - {`􀇿 `} - - {impactMsg} - - {hasPriceData && ( - {` • ${lang.t('exchange.price_impact.losing_prefix')} `} - )} - {hasPriceData && ( - - {headingValue} - - )} - - - - )} - - - ); -} diff --git a/src/components/exchange/index.ts b/src/components/exchange/index.ts deleted file mode 100644 index 851865261a9..00000000000 --- a/src/components/exchange/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -export { default as ConfirmExchangeButton } from './ConfirmExchangeButton'; -export { default as CurrencySelectionList } from './CurrencySelectionList'; -export { default as CurrencySelectModalHeader } from './CurrencySelectModalHeader'; -export { default as ExchangeAssetList } from './ExchangeAssetList'; -export { default as ExchangeDetailsButton } from './ExchangeDetailsButton'; -export { default as ExchangeDetailsRow } from './ExchangeDetailsRow'; -export { default as ExchangeFloatingPanels } from './ExchangeFloatingPanels'; -export { default as ExchangeHeader } from './ExchangeHeader'; -export { default as ExchangeInput } from './ExchangeInput'; -export { default as ExchangeInputField } from './ExchangeInputField'; -export { default as ExchangeNativeField } from './ExchangeNativeField'; -export { default as ExchangeNotch } from './ExchangeNotch'; -export { default as ExchangeOutputField } from './ExchangeOutputField'; -export { default as ExchangeSearch } from './ExchangeSearch'; -export { default as PriceImpactWarning } from './PriceImpactWarning'; diff --git a/src/components/exchange/exchangeAssetRowContextMenuProps.ts b/src/components/exchangeAssetRowContextMenuProps.ts similarity index 98% rename from src/components/exchange/exchangeAssetRowContextMenuProps.ts rename to src/components/exchangeAssetRowContextMenuProps.ts index f2c96d0b227..cc0cb066edd 100644 --- a/src/components/exchange/exchangeAssetRowContextMenuProps.ts +++ b/src/components/exchangeAssetRowContextMenuProps.ts @@ -1,7 +1,7 @@ import lang from 'i18n-js'; import { startCase } from 'lodash'; import { NativeSyntheticEvent } from 'react-native'; -import { setClipboard } from '../../hooks/useClipboard'; +import { setClipboard } from '@/hooks/useClipboard'; import { abbreviations, ethereumUtils, haptics, showActionSheetWithOptions } from '@/utils'; import { ChainId } from '@/state/backendNetworks/types'; import { useBackendNetworksStore } from '@/state/backendNetworks/backendNetworks'; diff --git a/src/components/expanded-state/AvailableNetworks.js b/src/components/expanded-state/AvailableNetworks.js index aebc7fc213d..5b469892f3d 100644 --- a/src/components/expanded-state/AvailableNetworks.js +++ b/src/components/expanded-state/AvailableNetworks.js @@ -13,8 +13,8 @@ import { ChainBadge } from '../coin-icon'; import Divider from '@/components/Divider'; import { Text } from '../text'; import { EthCoinIcon } from '../coin-icon/EthCoinIcon'; -import { ChainId } from '@/chains/types'; -import { getDefaultChains } from '@/chains'; +import { ChainId } from '@/state/backendNetworks/types'; +import { useBackendNetworksStore } from '@/state/backendNetworks/backendNetworks'; const AvailableNetworksv1 = ({ asset, networks, hideDivider, marginBottom = 24, marginHorizontal = 19, prominent }) => { const { colors } = useTheme(); @@ -87,7 +87,7 @@ const AvailableNetworksv1 = ({ asset, networks, hideDivider, marginBottom = 24, availableNetworks: availableChainIds?.length, }) : lang.t('expanded_state.asset.available_network', { - availableNetwork: getDefaultChains()[availableChainIds[0]]?.name, + availableNetwork: useBackendNetworksStore.getState().getChainsName()[availableChainIds[0]], })} diff --git a/src/components/expanded-state/AvailableNetworksv2.tsx b/src/components/expanded-state/AvailableNetworksv2.tsx index e5d67cf1099..b597edf481a 100644 --- a/src/components/expanded-state/AvailableNetworksv2.tsx +++ b/src/components/expanded-state/AvailableNetworksv2.tsx @@ -8,23 +8,20 @@ import { useNavigation } from '@/navigation'; import Routes from '@/navigation/routesNames'; import { position } from '@/styles'; import { watchingAlert } from '@/utils'; -import { CurrencySelectionTypes, ExchangeModalTypes } from '@/helpers'; -import { useSwapCurrencyHandlers, useWallets } from '@/hooks'; +import { useWallets } from '@/hooks'; import { RainbowToken } from '@/entities'; import { useTheme } from '@/theme'; import { ButtonPressAnimation } from '../animations'; import ContextMenuButton from '@/components/native-context-menu/contextMenu'; import { implementation } from '@/entities/dispersion'; import { EthCoinIcon } from '../coin-icon/EthCoinIcon'; -import { SWAPS_V2, enableActionsOnReadOnlyWallet, useExperimentalFlag } from '@/config'; -import { useRemoteConfig } from '@/model/remoteConfig'; +import { enableActionsOnReadOnlyWallet } from '@/config'; import { userAssetsStore } from '@/state/assets/userAssets'; import { parseSearchAsset } from '@/__swaps__/utils/assets'; import { AddressOrEth, AssetType } from '@/__swaps__/types/assets'; import { swapsStore } from '@/state/swaps/swapsStore'; import { InteractionManager } from 'react-native'; import { ChainId } from '@/state/backendNetworks/types'; -import { getUniqueId } from '@/utils/ethereumUtils'; import { useBackendNetworksStore } from '@/state/backendNetworks/backendNetworks'; const NOOP = () => null; @@ -42,8 +39,6 @@ const AvailableNetworksv2 = ({ }) => { const { colors } = useTheme(); const { goBack, navigate } = useNavigation(); - const { swaps_v2 } = useRemoteConfig(); - const swapsV2Enabled = useExperimentalFlag(SWAPS_V2); const { isReadOnlyWallet } = useWallets(); const radialGradientProps = { @@ -56,10 +51,6 @@ const AvailableNetworksv2 = ({ }, }; - const { updateInputCurrency } = useSwapCurrencyHandlers({ - shouldUpdate: true, - type: ExchangeModalTypes.swap, - }); const convertAssetAndNavigate = useCallback( (chainId: ChainId) => { if (isReadOnlyWallet && !enableActionsOnReadOnlyWallet) { @@ -76,76 +67,53 @@ const AvailableNetworksv2 = ({ goBack(); - if (swapsV2Enabled || swaps_v2) { - const uniqueId = `${newAsset.address}_${asset.chainId}`; - const userAsset = userAssetsStore.getState().userAssets.get(uniqueId); - - const chainName = useBackendNetworksStore.getState().getChainsName()[asset.chainId]; - - const parsedAsset = parseSearchAsset({ - assetWithPrice: { - ...newAsset, - uniqueId, - address: newAsset.address as AddressOrEth, - type: newAsset.type as AssetType, - chainId: asset.chainId, - chainName, - isNativeAsset: false, - native: {}, - }, - searchAsset: { - ...newAsset, - uniqueId, - chainId: asset.chainId, - chainName, - address: newAsset.address as AddressOrEth, - highLiquidity: newAsset.highLiquidity ?? false, - isRainbowCurated: newAsset.isRainbowCurated ?? false, - isVerified: newAsset.isVerified ?? false, - mainnetAddress: (newAsset.mainnet_address ?? '') as AddressOrEth, - networks: newAsset.networks ?? [], - type: newAsset.type as AssetType, - }, - userAsset, - }); - - const largestBalanceSameChainUserAsset = userAssetsStore - .getState() - .getUserAssets() - .find(userAsset => userAsset.chainId === asset.chainId && userAsset.address !== newAsset.address); - if (largestBalanceSameChainUserAsset) { - swapsStore.setState({ inputAsset: largestBalanceSameChainUserAsset }); - } else { - swapsStore.setState({ inputAsset: null }); - } - swapsStore.setState({ outputAsset: parsedAsset }); - - InteractionManager.runAfterInteractions(() => { - navigate(Routes.SWAP); - }); + const uniqueId = `${newAsset.address}_${asset.chainId}`; + const chainsName = useBackendNetworksStore.getState().getChainsName(); + const userAsset = userAssetsStore.getState().userAssets.get(uniqueId); + + const parsedAsset = parseSearchAsset({ + assetWithPrice: { + ...newAsset, + uniqueId, + address: newAsset.address as AddressOrEth, + type: newAsset.type as AssetType, + chainId: asset.chainId, + chainName: chainsName[asset.chainId], + isNativeAsset: false, + native: {}, + }, + searchAsset: { + ...newAsset, + uniqueId, + chainId: asset.chainId, + chainName: chainsName[asset.chainId], + address: newAsset.address as AddressOrEth, + highLiquidity: newAsset.highLiquidity ?? false, + isRainbowCurated: newAsset.isRainbowCurated ?? false, + isVerified: newAsset.isVerified ?? false, + mainnetAddress: (newAsset.mainnet_address ?? '') as AddressOrEth, + networks: newAsset.networks ?? [], + type: newAsset.type as AssetType, + }, + userAsset, + }); - return; + const largestBalanceSameChainUserAsset = userAssetsStore + .getState() + .getUserAssets() + .find(userAsset => userAsset.chainId === asset.chainId && userAsset.address !== newAsset.address); + if (largestBalanceSameChainUserAsset) { + swapsStore.setState({ inputAsset: largestBalanceSameChainUserAsset }); + } else { + swapsStore.setState({ inputAsset: null }); } + swapsStore.setState({ outputAsset: parsedAsset }); - newAsset.uniqueId = getUniqueId(asset.address, chainId); - newAsset.type = useBackendNetworksStore.getState().getChainsName()[chainId]; - - navigate(Routes.EXCHANGE_MODAL, { - params: { - fromDiscover: true, - ignoreInitialTypeCheck: true, - defaultOutputAsset: newAsset, - type: CurrencySelectionTypes.input, - showCoinIcon: true, - title: lang.t('swap.modal_types.get_symbol_with', { - symbol: newAsset?.symbol, - }), - onSelectCurrency: updateInputCurrency, - }, - screen: Routes.CURRENCY_SELECT_SCREEN, + InteractionManager.runAfterInteractions(() => { + navigate(Routes.SWAP); }); }, - [asset, goBack, isReadOnlyWallet, navigate, networks, swapsV2Enabled, swaps_v2, updateInputCurrency] + [asset, goBack, isReadOnlyWallet, navigate, networks] ); const handlePressContextMenu = useCallback( diff --git a/src/components/expanded-state/CustomGasState.js b/src/components/expanded-state/CustomGasState.js index 425befacda5..90fdb475d36 100644 --- a/src/components/expanded-state/CustomGasState.js +++ b/src/components/expanded-state/CustomGasState.js @@ -2,12 +2,11 @@ import { useIsFocused, useRoute } from '@react-navigation/native'; import React, { useEffect, useState } from 'react'; import { getSoftMenuBarHeight } from 'react-native-extra-dimensions-android'; import Divider from '@/components/Divider'; -import { ExchangeHeader } from '../exchange'; +import { ExchangeHeader } from '@/components/ExchangeHeader'; import { FloatingPanel } from '../floating-panels'; -import { GasSpeedButton } from '../gas'; -import { Column } from '../layout'; -import { SlackSheet } from '../sheet'; -import { FeesPanel, FeesPanelTabs } from './custom-gas'; +import { GasSpeedButton } from '@/components/gas'; +import { Column } from '@/components/layout'; +import { SlackSheet } from '@/components/sheet'; import { getTrendKey } from '@/helpers/gas'; import { useColorForAsset, useDimensions, useGas, useKeyboardHeight } from '@/hooks'; import { useNavigation } from '@/navigation'; @@ -15,8 +14,8 @@ import styled from '@/styled-thing'; import { margin } from '@/styles'; import { deviceUtils } from '@/utils'; import { IS_ANDROID } from '@/env'; -import { useSelector } from 'react-redux'; -import { getCrosschainSwapServiceTime } from '@/handlers/swap'; +import FeesPanel from '@/components/FeesPanel'; +import FeesPanelTabs from '@/components/FeesPanelTabs'; const FOOTER_HEIGHT = 120; const CONTENT_HEIGHT = 310; @@ -42,7 +41,6 @@ export default function CustomGasState({ asset }) { const colorForAsset = useColorForAsset(asset || {}, fallbackColor, false, true); const { selectedGasFee, currentBlockParams, chainId } = useGas(); const [canGoBack, setCanGoBack] = useState(true); - const { tradeDetails } = useSelector(state => state.swap); const validateGasParams = useRef(null); useAndroidDisableGesturesOnFocus(); @@ -99,7 +97,6 @@ export default function CustomGasState({ asset }) { theme="dark" validateGasParams={validateGasParams} marginTop={19} - crossChainServiceTime={getCrosschainSwapServiceTime(tradeDetails)} /> diff --git a/src/components/expanded-state/SwapDetailsState.js b/src/components/expanded-state/SwapDetailsState.js deleted file mode 100644 index cd05ed8d30c..00000000000 --- a/src/components/expanded-state/SwapDetailsState.js +++ /dev/null @@ -1,187 +0,0 @@ -import useSwapDetailsButtonPosition from './useSwapDetailsButtonPosition'; -import { useIsFocused, useRoute } from '@react-navigation/native'; -import lang from 'i18n-js'; -import React, { useCallback, useEffect, useState } from 'react'; -import Animated, { useSharedValue } from 'react-native-reanimated'; - -import { useSelector } from 'react-redux'; -import { ConfirmExchangeButton } from '../exchange'; -import { GasSpeedButton } from '../gas'; -import { Column } from '../layout'; -import { SheetHandleFixedToTopHeight, SheetKeyboardAnimation, SheetTitle, SlackSheet } from '../sheet'; -import { CopyToast, ToastPositionContainer } from '../toasts'; -import { SwapDetailsContent, SwapDetailsMasthead, SwapDetailsMastheadHeight, SwapDetailsSlippageMessage } from './swap-details'; -import { useHeight, usePrevious, usePriceImpactDetails, useSwapCurrencies } from '@/hooks'; -import { useNavigation } from '@/navigation'; -import styled from '@/styled-thing'; -import { padding, position } from '@/styles'; -import { abbreviations } from '@/utils'; -import { getCrosschainSwapServiceTime } from '@/handlers/swap'; -import { SwapPriceImpactType } from '@/hooks/usePriceImpactDetails'; -import { getChainsIdByName } from '@/chains'; - -const AnimatedContainer = styled(Animated.View)({ - ...position.sizeAsObject('100%'), -}); - -const Footer = styled(Column).attrs({ - grow: 1, - shrink: 0, -})({ - ...padding.object(6, 0, 0), -}); - -const Header = styled(Column).attrs({ - justify: 'start', -})({ - left: 0, - position: 'absolute', - right: 0, - top: -2, - width: '100%', -}); - -const FOOTER_MIN_HEIGHT = 143; -const FOOTER_CONTENT_MIN_HEIGHT = 160; - -function useAndroidDisableGesturesOnFocus() { - const { params } = useRoute(); - const isFocused = useIsFocused(); - useEffect(() => { - android && params?.toggleGestureEnabled?.(!isFocused); - }, [isFocused, params]); -} - -function useSwapDetailsClipboardState() { - const [copiedText, setCopiedText] = useState(undefined); - const [copyCount, setCopyCount] = useState(0); - const onCopySwapDetailsText = useCallback(text => { - setCopiedText(abbreviations.formatAddressForDisplay(text)); - setCopyCount(count => count + 1); - }, []); - return { - copiedText, - copyCount, - onCopySwapDetailsText, - }; -} - -export default function SwapDetailsState({ confirmButtonProps, restoreFocusOnSwapModal }) { - const { setParams } = useNavigation(); - const isFocused = useIsFocused(); - const prevIsFocused = usePrevious(isFocused); - const { params: { longFormHeight, currentNetwork, flashbotTransaction, isRefuelTx, onClose } = {} } = useRoute(); - const { inputCurrency, outputCurrency } = useSwapCurrencies(); - const chainId = getChainsIdByName()[currentNetwork]; - - const { - derivedValues: { inputAmount, outputAmount }, - displayValues: { inputAmountDisplay, outputAmountDisplay }, - tradeDetails, - } = useSelector(state => state.swap); - - const { priceImpact, inputNativeAmount, outputNativeAmount } = usePriceImpactDetails( - inputCurrency, - outputCurrency, - tradeDetails, - currentNetwork - ); - - const { copiedText, copyCount, onCopySwapDetailsText } = useSwapDetailsClipboardState(); - - const [footerHeight, setFooterHeight] = useHeight(FOOTER_MIN_HEIGHT); - const [slippageMessageHeight, setSlippageMessageHeight] = useHeight(); - const [contentHeight, setContentHeight] = useHeight(FOOTER_CONTENT_MIN_HEIGHT); - - const { onHeightChange, wrapperStyle, onPressMore, onWrapperLayout } = useSwapDetailsButtonPosition({ contentHeight }); - - const onContentHeightChange = useCallback( - event => { - onHeightChange(event); - setContentHeight(event); - }, - [onHeightChange, setContentHeight] - ); - - useEffect(() => { - if (!isFocused && prevIsFocused) { - return restoreFocusOnSwapModal(); - } - }, [isFocused, prevIsFocused, restoreFocusOnSwapModal]); - useAndroidDisableGesturesOnFocus(); - - const sheetHeightWithoutKeyboard = - SheetHandleFixedToTopHeight + SwapDetailsMastheadHeight + contentHeight + slippageMessageHeight + footerHeight; - - const contentScroll = useSharedValue(0); - - useEffect(() => { - setParams({ - longFormHeight: sheetHeightWithoutKeyboard, - transitionDuration: 0.7, - }); - }, [contentScroll, sheetHeightWithoutKeyboard, setParams]); - - useEffect(() => { - return () => { - onClose?.(); - }; - }, [onClose]); - - return ( - - -
- {lang.t('expanded_state.swap_details.review')} -
- - - - -
- - -
-
- - - -
-
- ); -} diff --git a/src/components/expanded-state/UniqueTokenExpandedState.tsx b/src/components/expanded-state/UniqueTokenExpandedState.tsx index 105c3f0e680..0bf5cdae75f 100644 --- a/src/components/expanded-state/UniqueTokenExpandedState.tsx +++ b/src/components/expanded-state/UniqueTokenExpandedState.tsx @@ -338,9 +338,10 @@ const UniqueTokenExpandedState = ({ asset: passedAsset, external }: UniqueTokenE const handleL2DisclaimerPress = useCallback(() => { navigate(Routes.EXPLAIN_SHEET, { - type: asset.network, + type: 'network', + chainId: asset.chainId, }); - }, [asset.network, navigate]); + }, [asset.chainId, navigate]); const isHiddenAsset = useMemo(() => hiddenTokens.includes(fullUniqueId) as boolean, [hiddenTokens, fullUniqueId]); const isShowcaseAsset = useMemo(() => showcaseTokens.includes(uniqueId) as boolean, [showcaseTokens, uniqueId]); diff --git a/src/components/expanded-state/asset/ChartExpandedState.js b/src/components/expanded-state/asset/ChartExpandedState.js index 07d8ad3ddc1..9fecf8ece49 100644 --- a/src/components/expanded-state/asset/ChartExpandedState.js +++ b/src/components/expanded-state/asset/ChartExpandedState.js @@ -16,7 +16,7 @@ import ExpandedStateSection from '../ExpandedStateSection'; import SocialLinks from './SocialLinks'; import { ChartPathProvider } from '@/react-native-animated-charts/src'; import { isL2Chain, isTestnetChain } from '@/handlers/web3'; -import AssetInputTypes from '@/helpers/assetInputTypes'; +import { SwapAssetType } from '@/__swaps__/types/swap'; import { useAccountSettings, useAdditionalAssetData, @@ -36,8 +36,8 @@ import { Box } from '@/design-system'; import { useExternalToken } from '@/resources/assets/externalAssetsQuery'; import { bigNumberFormat } from '@/helpers/bigNumberFormat'; import { greaterThanOrEqualTo } from '@/helpers/utilities'; -import { getChainsName, supportedSwapChainIds } from '@/chains'; -import { ChainId } from '@/chains/types'; +import { useBackendNetworksStore } from '@/state/backendNetworks/backendNetworks'; +import { ChainId } from '@/state/backendNetworks/types'; const defaultCarouselHeight = 60; const baseHeight = 386 + (android && 20 - getSoftMenuBarHeight()) - defaultCarouselHeight; @@ -166,7 +166,7 @@ export default function ChartExpandedState({ asset }) { chainId: asset.chainId, network: asset.network, address: asset.address, - mainnetAddress: asset?.networks?.[getChainsName()[ChainId.mainnet]]?.address, + mainnetAddress: asset?.networks?.[useBackendNetworksStore.getState().getChainsName()[ChainId.mainnet]]?.address, } : asset; }, [asset, genericAsset, hasBalance]); @@ -227,9 +227,10 @@ export default function ChartExpandedState({ asset }) { const handleL2DisclaimerPress = useCallback(() => { navigate(Routes.EXPLAIN_SHEET, { - type: assetWithPrice.network, + type: 'network', + chainId: assetWithPrice.chainId, }); - }, [assetWithPrice.network, navigate]); + }, [assetWithPrice.chainId, navigate]); const { layout } = useContext(ModalContext) || {}; @@ -242,7 +243,7 @@ export default function ChartExpandedState({ asset }) { const assetChainId = assetWithPrice.chainId; const { swagg_enabled, f2c_enabled } = useRemoteConfig(); - const swapEnabled = swagg_enabled && supportedSwapChainIds.includes(assetChainId); + const swapEnabled = swagg_enabled && useBackendNetworksStore.getState().getSupportedSwapChainIds().includes(assetChainId); const addCashEnabled = f2c_enabled; const format = useCallback( @@ -291,7 +292,7 @@ export default function ChartExpandedState({ asset }) { {!needsEth ? ( {hasBalance && !isTestnet && swapEnabled && ( - + )} {hasBalance ? ( isTransferable ? ( @@ -302,7 +303,7 @@ export default function ChartExpandedState({ asset }) { asset={assetWithPrice} color={color} fromDiscover={fromDiscover} - inputType={AssetInputTypes.out} + inputType={SwapAssetType.outputAsset} label={`􀖅 ${lang.t('expanded_state.asset.get_asset', { assetSymbol: asset?.symbol, })}`} @@ -314,7 +315,7 @@ export default function ChartExpandedState({ asset }) { ) : addCashEnabled ? ( - + ) : null} {!data?.networks && isL2 && ( diff --git a/src/components/expanded-state/custom-gas/index.js b/src/components/expanded-state/custom-gas/index.js deleted file mode 100644 index d0346c63712..00000000000 --- a/src/components/expanded-state/custom-gas/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export { default as FeesGweiInput } from './FeesGweiInput'; -export { default as FeesPanelTabs } from './FeesPanelTabs'; -export { default as FeesPanel } from './FeesPanel'; diff --git a/src/components/expanded-state/index.js b/src/components/expanded-state/index.ts similarity index 74% rename from src/components/expanded-state/index.js rename to src/components/expanded-state/index.ts index 2372a750190..82ec12ad6ec 100644 --- a/src/components/expanded-state/index.js +++ b/src/components/expanded-state/index.ts @@ -1,8 +1,6 @@ export { default as ChartExpandedState } from './asset/ChartExpandedState'; -export { default as ContactProfileState } from './ContactProfileState'; export { default as CustomGasState } from './CustomGasState'; -export { default as SwapDetailsState } from './SwapDetailsState'; -export { default as SwapSettingsState } from './swap-settings/SwapSettingsState'; +export { default as ContactProfileState } from './ContactProfileState'; export { default as UniqueTokenExpandedState } from './UniqueTokenExpandedState'; export { default as WalletProfileState } from './WalletProfileState'; export { default as NewWalletGroupState } from './NewWalletGroupState'; diff --git a/src/components/expanded-state/swap-details/CurrencyTile.js b/src/components/expanded-state/swap-details/CurrencyTile.js deleted file mode 100644 index 62072cc8930..00000000000 --- a/src/components/expanded-state/swap-details/CurrencyTile.js +++ /dev/null @@ -1,118 +0,0 @@ -import React from 'react'; -import { useSelector } from 'react-redux'; -import { Centered } from '../../layout'; -import { Text, TruncatedText } from '../../text'; -import { Box, Column, Columns, Row, Rows } from '@/design-system'; -import { useAccountSettings, useColorForAsset, useDimensions } from '@/hooks'; -import { SwapModalField } from '@/redux/swap'; -import styled from '@/styled-thing'; -import { position } from '@/styles'; -import { convertAmountToNativeDisplay } from '@/helpers/utilities'; -import RainbowCoinIcon from '@/components/coin-icon/RainbowCoinIcon'; -import { ethereumUtils } from '@/utils'; - -export const CurrencyTileHeight = android ? 153 : 143; - -const AmountText = styled(Text).attrs(({ theme: { colors } }) => ({ - color: colors.alpha(colors.blueGreyDark, 0.8), - letterSpacing: 'roundedTight', - size: 'smedium', - weight: 'semibold', -}))(); - -const Container = styled(Centered).attrs({ - direction: 'column', -})({ - borderRadius: 30, - flex: 1, - height: CurrencyTileHeight, - overflow: 'hidden', - zIndex: 0, - ...(android ? { paddingTop: 4 } : {}), -}); - -const Gradient = styled(Box).attrs(({ theme: { colors }, color }) => ({ - backgroundColor: colors.alpha(color, 0.08), -}))({ - ...position.coverAsObject, -}); - -const NativePriceText = styled(TruncatedText).attrs({ - letterSpacing: 'roundedTight', - size: 'lmedium', - weight: 'heavy', -})({ - maxWidth: ({ maxWidth }) => maxWidth, -}); - -const TruncatedAmountText = styled(AmountText)({ - flexGrow: 0, - flexShrink: 1, -}); - -export default function CurrencyTile({ - amount, - amountDisplay, - asset, - isHighPriceImpact, - priceImpactColor, - priceValue, - type = 'input', - ...props -}) { - const { width } = useDimensions(); - const inputAsExact = useSelector(state => state.swap.independentField !== SwapModalField.output); - const { nativeCurrency } = useAccountSettings(); - const colorForAsset = useColorForAsset(asset); - const theme = useTheme(); - const isOther = (inputAsExact && type === 'output') || (!inputAsExact && type === 'input'); - - const priceDisplay = priceValue ? convertAmountToNativeDisplay(priceValue, nativeCurrency) : '-'; - - return ( - - - - - - - - - - - - - - - {isOther && '~'} - {amountDisplay} - - - - {asset?.symbol} - - - - - - - {priceDisplay} - {isHighPriceImpact && {` 􀇿`}} - - - - - - - - - - ); -} diff --git a/src/components/expanded-state/swap-details/SwapDetailsContent.js b/src/components/expanded-state/swap-details/SwapDetailsContent.js deleted file mode 100644 index 56abec2e201..00000000000 --- a/src/components/expanded-state/swap-details/SwapDetailsContent.js +++ /dev/null @@ -1,127 +0,0 @@ -import lang from 'i18n-js'; -import React from 'react'; -import { useSelector, View } from 'react-redux'; -import { ButtonPressAnimation } from '../../animations'; -import SwapDetailsContractRow from './SwapDetailsContractRow'; -import SwapDetailsExchangeRow from './SwapDetailsExchangeRow'; -import SwapDetailsFeeRow from './SwapDetailsFeeRow'; -import SwapDetailsPriceRow from './SwapDetailsPriceRow'; -import SwapDetailsRefuelRow from './SwapDetailsRefuelRow'; -import SwapDetailsRow, { SwapDetailsValue } from './SwapDetailsRow'; -import { AccentColorProvider, Box, Rows, Separator } from '@/design-system'; -import { isNativeAsset } from '@/handlers/assets'; -import Routes from '@/navigation/routesNames'; -import { useAccountSettings, useColorForAsset, useSwapAdjustedAmounts, useSwapCurrencies } from '@/hooks'; -import { SwapModalField } from '@/redux/swap'; -import styled from '@/styled-thing'; -import { padding } from '@/styles'; - -import { useNavigation } from '@/navigation'; -import { SwapDetailsRewardRow } from './SwapDetailsRewardRow'; -import useExperimentalFlag, { OP_REWARDS } from '@rainbow-me/config/experimentalHooks'; -import { useRemoteConfig } from '@/model/remoteConfig'; -import { ChainId } from '@/chains/types'; - -const Container = styled(Box).attrs({ - flex: 1, -})(({ hasWarning }) => padding.object(hasWarning ? 24 : 30, 19, 30)); - -export default function SwapDetailsContent({ isHighPriceImpact, isRefuelTx, onCopySwapDetailsText, tradeDetails, onPressMore, ...props }) { - const { inputCurrency, outputCurrency } = useSwapCurrencies(); - const { amountReceivedSold, receivedSoldLabel } = useSwapAdjustedAmounts(tradeDetails); - const { navigate } = useNavigation(); - const { flashbotsEnabled } = useAccountSettings(); - const inputAsExact = useSelector(state => state.swap.independentField !== SwapModalField.output); - - const [detailsExpanded, setDetailsExpanded] = useState(false); - - const colorForAsset = useColorForAsset(outputCurrency, undefined, true, true); - const inputChainId = inputCurrency.chainId; - const outputChainId = outputCurrency.chainId; - const { op_rewards_enabled } = useRemoteConfig(); - const hasReward = (useExperimentalFlag(OP_REWARDS) || op_rewards_enabled) && !!tradeDetails.reward?.[0]; - - return ( - - - - - - {amountReceivedSold} {inputAsExact ? outputCurrency.symbol : inputCurrency.symbol} - - - - {(isRefuelTx || tradeDetails?.refuel) && } - - {tradeDetails?.protocols && ( - - )} - {tradeDetails.feePercentageBasisPoints !== 0 && ( - - )} - {hasReward && } - {flashbotsEnabled && inputChainId === ChainId.mainnet && ( - - navigate(Routes.EXPLAIN_SHEET, { - type: 'flashbots', - }) - } - label={`${lang.t('expanded_state.swap.flashbots_protect')} 􀅵`} - testID="swaps-details-flashbots-row" - > - {`${lang.t('expanded_state.swap.on')} 􀞜`} - - )} - {!detailsExpanded && ( - - { - setDetailsExpanded(!detailsExpanded); - onPressMore(); - }} - scaleTo={1.06} - style={{ - // enlarge tap target for details button - paddingVertical: 18, - }} - testID="swaps-details-show-details-button" - > - - {detailsExpanded ? '􀁮' : '􀁰'} - - - - )} - {detailsExpanded && ( - - - - {!isNativeAsset(inputCurrency?.address, inputChainId) && ( - - )} - {!isNativeAsset(outputCurrency?.address, outputChainId) && ( - - )} - - )} - - - - ); -} diff --git a/src/components/expanded-state/swap-details/SwapDetailsContractRow.js b/src/components/expanded-state/swap-details/SwapDetailsContractRow.js deleted file mode 100644 index 2f8d9a48795..00000000000 --- a/src/components/expanded-state/swap-details/SwapDetailsContractRow.js +++ /dev/null @@ -1,178 +0,0 @@ -import lang from 'i18n-js'; -import { startCase } from 'lodash'; -import React, { useCallback, useLayoutEffect, useMemo, useState } from 'react'; -import { ContextMenuButton } from 'react-native-ios-context-menu'; -import Animated, { interpolate, interpolateColor, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'; -import { ButtonPressAnimation } from '../../animations'; -import { Text, TruncatedAddress } from '../../text'; -import SwapDetailsRow from './SwapDetailsRow'; -import { useClipboard, useColorForAsset } from '@/hooks'; -import styled from '@/styled-thing'; -import { fonts, fontWithWidth } from '@/styles'; -import { abbreviations, ethereumUtils, showActionSheetWithOptions } from '@/utils'; - -const SwapDetailsText = styled(Text).attrs({ - lineHeight: android ? 18 : 17, -})(); - -export const SwapDetailsValue = styled(SwapDetailsText).attrs(({ theme: { colors }, color = colors.alpha(colors.blueGreyDark, 0.8) }) => ({ - color, -}))(fontWithWidth(fonts.weight.bold)); - -const AnimatedTruncatedAddress = Animated.createAnimatedComponent(TruncatedAddress); -const AnimatedText = Animated.createAnimatedComponent(Text); - -const ContractActionsEnum = { - blockExplorer: 'blockExplorer', - copyAddress: 'copyAddress', -}; - -const ContractActions = { - [ContractActionsEnum.copyAddress]: { - actionKey: ContractActionsEnum.copyAddress, - actionTitle: lang.t('wallet.action.copy_contract_address'), - icon: { - iconType: 'SYSTEM', - iconValue: 'doc.on.doc', - }, - }, -}; - -const buildBlockExplorerAction = chainId => { - const blockExplorerText = lang.t('expanded_state.swap.view_on', { - blockExplorerName: startCase(ethereumUtils.getBlockExplorer({ chainId })), - }); - return { - actionKey: ContractActionsEnum.blockExplorer, - actionTitle: blockExplorerText, - icon: { - iconType: 'SYSTEM', - iconValue: 'link', - }, - }; -}; - -function SwapDetailsContractRowContent({ asset, menuVisible, scaleTo = 1.06, ...props }) { - const { colors } = useTheme(); - const colorForAsset = useColorForAsset(asset); - const animation = useSharedValue(menuVisible ? 1 : 0); - const startingColor = useMemo(() => colors.alpha(colors.blueGreyDark, 0.8), [colors]); - - useLayoutEffect(() => { - animation.value = withTiming(menuVisible ? 1 : 0, { duration: 150 }); - }, [menuVisible, animation]); - - const colorStyle = useAnimatedStyle(() => { - const color = interpolateColor(animation.value, [0, 1], [startingColor, colorForAsset]); - return { - color, - }; - }, [animation, colorForAsset]); - - const scaleStyle = useAnimatedStyle(() => { - const scale = interpolate(animation.value, [0, 1], [1, scaleTo]); - - return { - transform: [{ scale }], - }; - }); - - return ( - - - - - {` 􀍡`} - - - - ); -} - -export default function SwapDetailsContractRow({ asset, onCopySwapDetailsText, ...props }) { - const { setClipboard } = useClipboard(); - const handleCopyContractAddress = useCallback( - address => { - setClipboard(address); - onCopySwapDetailsText(address); - }, - [onCopySwapDetailsText, setClipboard] - ); - const [menuVisible, setMenuVisible] = useState(false); - - const menuConfig = useMemo(() => { - const blockExplorerAction = buildBlockExplorerAction(asset?.chainId); - return { - menuItems: [ - blockExplorerAction, - { - ...ContractActions[ContractActionsEnum.copyAddress], - discoverabilityTitle: abbreviations.formatAddressForDisplay(asset?.address), - }, - ], - menuTitle: `${asset?.name} (${asset?.symbol})`, - }; - }, [asset?.chainId, asset?.address, asset?.name, asset?.symbol]); - - const handlePressMenuItem = useCallback( - ({ nativeEvent: { actionKey } }) => { - if (actionKey === ContractActionsEnum.copyAddress) { - handleCopyContractAddress(asset?.address); - } else if (actionKey === ContractActionsEnum.blockExplorer) { - ethereumUtils.openTokenEtherscanURL({ address: asset?.address, chainId: asset?.chainId }); - } - }, - [asset, handleCopyContractAddress] - ); - - const onPressAndroid = useCallback(() => { - const blockExplorerText = lang.t('expanded_state.swap.view_on', { - blockExplorerName: startCase(ethereumUtils.getBlockExplorer({ chainId: asset?.chainId })), - }); - const androidContractActions = [lang.t('wallet.action.copy_contract_address'), blockExplorerText, lang.t('button.cancel')]; - showActionSheetWithOptions( - { - cancelButtonIndex: 2, - options: androidContractActions, - showSeparators: true, - title: `${asset?.name} (${asset?.symbol})`, - }, - idx => { - if (idx === 0) { - handleCopyContractAddress(asset?.address); - } - if (idx === 1) { - ethereumUtils.openTokenEtherscanURL({ address: asset?.address, chainId: asset?.chainId }); - } - } - ); - }, [asset, handleCopyContractAddress]); - - const onShowMenu = () => setMenuVisible(true); - const onHideMenu = () => setMenuVisible(false); - - return ( - - - - ); -} diff --git a/src/components/expanded-state/swap-details/SwapDetailsExchangeRow.js b/src/components/expanded-state/swap-details/SwapDetailsExchangeRow.js deleted file mode 100644 index 1f4ab05de44..00000000000 --- a/src/components/expanded-state/swap-details/SwapDetailsExchangeRow.js +++ /dev/null @@ -1,265 +0,0 @@ -import lang from 'i18n-js'; -import { capitalize } from 'lodash'; -import React, { Fragment, useMemo } from 'react'; -import { convertAmountToPercentageDisplay } from '../../../helpers/utilities'; -import Pill from '../../Pill'; -import { ButtonPressAnimation } from '../../animations'; -import { SwapDetailsLabel, SwapDetailsValue } from './SwapDetailsRow'; -import { Bleed, Box, Column, Columns, Cover, Inline, Stack, Text, useForegroundColor } from '@/design-system'; -import { usePrevious, useStepper } from '@/hooks'; -import { ImgixImage } from '@/components/images'; -import { getExchangeIconUrl, magicMemo } from '@/utils'; -import { SocketBridges } from '@/references/swap/bridges'; -import { getDefaultChains } from '@/chains'; - -const parseExchangeName = name => { - const networks = Object.values(getDefaultChains()).map(network => network.name.toLowerCase()); - - const removeNetworks = name => - networks.some(network => name.toLowerCase().includes(network)) ? name.slice(name.indexOf('_') + 1, name.length) : name; - - const removeBridge = name => name.replace('-bridge', ''); - - return removeNetworks(removeBridge(name)); -}; - -const ExchangeIcon = magicMemo( - function ExchangeIcon({ index = 1, icon, protocol }) { - const { colors } = useTheme(); - const [error, setError] = useState(false); - const previousIcon = usePrevious(icon); - if (error && icon !== previousIcon) { - setError(false); - } - - return ( - - {icon && !error ? ( - setError(true)} - size={20} - source={{ uri: icon }} - style={{ - borderColor: colors.white, - borderRadius: 10, - borderWidth: 1.5, - height: 20, - width: 20, - zIndex: index, - }} - /> - ) : ( - - - - - - {typeof protocol === 'string' ? protocol?.substring(0, 1)?.toUpperCase() : 'U'} - - - - - - )} - - ); - }, - ['icon', 'protocol'] -); - -const ExchangeIconStack = magicMemo( - ({ protocols }) => { - return ( - - {protocols?.icons?.map((icon, index) => { - return ( - - - - ); - })} - - ); - }, - ['protocols'] -); - -const CrossChainIconStack = magicMemo( - ({ protocols }) => { - return ( - - {protocols?.icons?.map((icon, index) => { - return ( - - - - {index < protocols?.icons.length - 1 && ( - - - 􀆊 - - - )} - - ); - })} - - ); - }, - ['protocols'] -); - -export default function SwapDetailsExchangeRow({ routes, protocols, testID }) { - const bridges = routes?.[0]?.usedBridgeNames; - - const steps = useMemo(() => { - const sortedProtocols = protocols?.sort((a, b) => b.part - a.part); - const defaultCase = { - icons: sortedProtocols.map(({ name }) => getExchangeIconUrl(parseExchangeName(name))), - label: lang.t(bridges ? 'expanded_state.swap_details.number_of_steps' : 'expanded_state.swap_details.number_of_exchanges', { - number: sortedProtocols?.length, - }), - names: sortedProtocols.map(({ name }) => name), - }; - if (sortedProtocols.length === 1) { - const protocol = sortedProtocols[0]; - const protocolName = parseExchangeName(protocol.name); - const isBridge = bridges?.includes(protocol.name); - - return [ - { - icons: [getExchangeIconUrl(protocolName)], - label: capitalize(protocolName.replace('_', ' ')), - name: SocketBridges[protocol.name] ?? protocolName.slice('_'), - action: isBridge ? lang.t('expanded_state.swap.bridge') : lang.t('expanded_state.swap.swap'), - part: convertAmountToPercentageDisplay(protocol.part), - }, - ]; - } - const mappedExchanges = sortedProtocols.map(protocol => { - const protocolName = parseExchangeName(protocol.name); - const part = convertAmountToPercentageDisplay(protocol.part, 0, 3, true); - const isBridge = bridges?.includes(protocol.name); - - return { - icons: [getExchangeIconUrl(protocolName)], - label: capitalize(protocolName.replace('_', ' ')), - name: SocketBridges[protocol.name] ?? protocolName.slice('_'), - isBridge, - action: isBridge ? lang.t('expanded_state.swap.bridge') : lang.t('expanded_state.swap.swap'), - part, - }; - }); - return [defaultCase, ...mappedExchanges]; - }, [bridges, protocols]); - - const [step, nextStep] = useStepper(steps.length); - const defaultColor = useForegroundColor('secondary (Deprecated)'); - - if (protocols?.length > 1) { - return ( - - - - - {lang.t('expanded_state.swap.swapping_via')} - - - - - {bridges ? : } - - - - - {steps[step].label} - - {(steps?.[step]?.part || steps?.[step]?.action) && ( - - - - {bridges ? steps[step].action : steps[step].part} - - - - )} - - - - ); - } else if (protocols?.length > 0) { - return ( - - - {lang.t('expanded_state.swap.swapping_via')} - - - - - - - - - - {steps[step].label} - - - - {`${protocols[0].part}%`} - - - - ); - } - return null; -} diff --git a/src/components/expanded-state/swap-details/SwapDetailsFeeRow.js b/src/components/expanded-state/swap-details/SwapDetailsFeeRow.js deleted file mode 100644 index 675acd503c8..00000000000 --- a/src/components/expanded-state/swap-details/SwapDetailsFeeRow.js +++ /dev/null @@ -1,41 +0,0 @@ -import lang from 'i18n-js'; -import React from 'react'; -import useRainbowFee from '../../../hooks/useRainbowFee'; -import SwapDetailsRow from './SwapDetailsRow'; -import { convertAmountToNativeDisplay, convertAmountToPercentageDisplayWithThreshold, isZero } from '@/helpers/utilities'; -import { useAccountSettings, useStepper } from '@/hooks'; -import { useNavigation } from '@/navigation'; -import Routes from '@/navigation/routesNames'; - -export default function SwapDetailsFeeRow({ tradeDetails, chainId, testID }) { - const { navigate } = useNavigation(); - const { nativeCurrency } = useAccountSettings(); - const { rainbowFeeNative, rainbowFeePercentage } = useRainbowFee({ - chainId, - tradeDetails, - }); - const rainbowFeeNativeDisplay = rainbowFeeNative && convertAmountToNativeDisplay(rainbowFeeNative, nativeCurrency); - const rainbowFeePercentageDisplay = isZero(rainbowFeePercentage) - ? '0.00%' - : convertAmountToPercentageDisplayWithThreshold(rainbowFeePercentage); - const steps = rainbowFeeNativeDisplay ? [rainbowFeeNativeDisplay, rainbowFeePercentageDisplay] : [rainbowFeePercentageDisplay]; - const [step, nextStep] = useStepper(steps.length); - - const handleLabelPress = useCallback(() => { - navigate(Routes.EXPLAIN_SHEET, { - feePercentage: rainbowFeePercentageDisplay, - type: 'rainbow_fee', - }); - }, [navigate, rainbowFeePercentageDisplay]); - - return ( - - {steps[step]} - - ); -} diff --git a/src/components/expanded-state/swap-details/SwapDetailsMasthead.js b/src/components/expanded-state/swap-details/SwapDetailsMasthead.js deleted file mode 100644 index 7a00d7f8daa..00000000000 --- a/src/components/expanded-state/swap-details/SwapDetailsMasthead.js +++ /dev/null @@ -1,79 +0,0 @@ -import React from 'react'; -import { RowWithMargins } from '../../layout'; -import CurrencyTile, { CurrencyTileHeight } from './CurrencyTile'; -import { Bleed, Box, Columns, Cover, Text } from '@/design-system'; -import { useSwapCurrencies } from '@/hooks'; - -const containerPaddingTop = 34; -export const SwapDetailsMastheadHeight = CurrencyTileHeight + containerPaddingTop; - -const DoubleChevron = () => ( - - - - 􀯻 - - - - 􀯻 - - - - -); - -export default function SwapDetailsMasthead({ - inputAmount, - inputAmountDisplay, - inputPriceValue, - isHighPriceImpact, - outputAmount, - outputAmountDisplay, - outputPriceValue, - priceImpactColor, - ...props -}) { - const { inputCurrency, outputCurrency } = useSwapCurrencies(); - - return ( - - - - - - - - - - - ); -} diff --git a/src/components/expanded-state/swap-details/SwapDetailsPriceRow.js b/src/components/expanded-state/swap-details/SwapDetailsPriceRow.js deleted file mode 100644 index 303f2f1956a..00000000000 --- a/src/components/expanded-state/swap-details/SwapDetailsPriceRow.js +++ /dev/null @@ -1,49 +0,0 @@ -import lang from 'i18n-js'; -import React, { useMemo } from 'react'; -import { ButtonPressAnimation } from '../../animations'; -import SwapDetailsRow, { SwapDetailsValue } from './SwapDetailsRow'; -import { convertRawAmountToDecimalFormat, divide, handleSignificantDecimals } from '@/helpers/utilities'; -import { useStepper, useSwapCurrencies } from '@/hooks'; - -export default function SwapDetailsPriceRow({ tradeDetails, ...props }) { - const { inputCurrency, outputCurrency } = useSwapCurrencies(); - - const convertedSellAmount = convertRawAmountToDecimalFormat(tradeDetails?.sellAmount, inputCurrency.decimals); - - const convertedBuyAmount = convertRawAmountToDecimalFormat(tradeDetails?.buyAmount, outputCurrency.decimals); - - const outputExecutionRateRaw = divide(convertedSellAmount, convertedBuyAmount); - - const inputExecutionRateRaw = divide(convertedBuyAmount, convertedSellAmount); - - const inputExecutionRate = handleSignificantDecimals(inputExecutionRateRaw, 2); - - const outputExecutionRate = handleSignificantDecimals(outputExecutionRateRaw, 2); - - const steps = useMemo( - () => [ - lang.t('expanded_state.swap_details.output_exchange_rate', { - executionRate: outputExecutionRate, - inputSymbol: inputCurrency?.symbol, - outputSymbol: outputCurrency?.symbol, - }), - lang.t('expanded_state.swap_details.input_exchange_rate', { - executionRate: inputExecutionRate, - inputSymbol: inputCurrency?.symbol, - outputSymbol: outputCurrency?.symbol, - }), - ], - [inputCurrency, inputExecutionRate, outputCurrency, outputExecutionRate] - ); - - const [step, nextStep] = useStepper(steps.length); - - return ( - - - {steps[step]} - {` 􀅌`} - - - ); -} diff --git a/src/components/expanded-state/swap-details/SwapDetailsRefuelRow.js b/src/components/expanded-state/swap-details/SwapDetailsRefuelRow.js deleted file mode 100644 index f93113e0fb6..00000000000 --- a/src/components/expanded-state/swap-details/SwapDetailsRefuelRow.js +++ /dev/null @@ -1,92 +0,0 @@ -import lang from 'i18n-js'; -import React from 'react'; -import { SwapDetailsLabel, SwapDetailsValue } from './SwapDetailsRow'; -import { convertRawAmountToBalance } from '@/helpers/utilities'; -import { ethereumUtils } from '@/utils'; -import { Box, Column, Columns, Rows } from '@/design-system'; -import styled from '@/styled-thing'; -import { ImgixImage } from '@/components/images'; -import CaretImageSource from '@/assets/family-dropdown-arrow.png'; -import Spinner from '@/components/Spinner'; -import { useTheme } from '@/theme'; -import { useNativeAsset } from '@/utils/ethereumUtils'; -import RainbowCoinIcon from '@/components/coin-icon/RainbowCoinIcon'; - -const CaretIcon = styled(ImgixImage).attrs(({ theme: { colors } }) => ({ - resizeMode: ImgixImage.resizeMode.contain, - source: CaretImageSource, - tintColor: colors.blueGreyDark, - size: 30, -}))({ - height: 11, - top: 0, - width: 7, -}); - -const ICON_ALIGN_MARGIN = '-8px'; - -export default function SwapDetailsRefuelRow({ tradeDetails, testID }) { - const { colors } = useTheme(); - const fromAsset = tradeDetails?.refuel?.fromAsset; - const toAsset = tradeDetails?.refuel?.toAsset; - const toAmount = convertRawAmountToBalance(tradeDetails?.refuel?.toAmount, { - decimals: 18, - }); - const toSymbol = tradeDetails?.refuel?.toAsset?.symbol; - - const fromNativeAsset = useNativeAsset({ chainId: fromAsset?.chainId }); - const toNativeAsset = useNativeAsset({ chainId: toAsset?.chainId }); - - return ( - - - - {lang.t('expanded_state.swap_details.refuel')} - - {tradeDetails.refuel ? ( - <> - - - - - - - - - - - - - - - - - - {`${toAmount.display}${toSymbol}`} - - - ) : ( - - - - - - )} - - - ); -} diff --git a/src/components/expanded-state/swap-details/SwapDetailsRewardRow.tsx b/src/components/expanded-state/swap-details/SwapDetailsRewardRow.tsx deleted file mode 100644 index bd09a88a6e9..00000000000 --- a/src/components/expanded-state/swap-details/SwapDetailsRewardRow.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import React from 'react'; -import { Bleed, Box, Text } from '@/design-system'; -import { SwapDetailsLabel } from './SwapDetailsRow'; -import { ButtonPressAnimation } from '@/components/animations'; -import { Reward } from '@rainbow-me/swaps'; -import { useNavigation } from '@/navigation'; -import Routes from '@/navigation/routesNames'; -import { ChainBadge } from '@/components/coin-icon'; -import { useTheme } from '@/theme'; -import * as i18n from '@/languages'; -import { ChainId } from '@/state/backendNetworks/types'; -import { useBackendNetworksStore } from '@/state/backendNetworks/backendNetworks'; - -export function SwapDetailsRewardRow({ reward }: { reward: Reward }) { - const { navigate } = useNavigation(); - const { isDarkMode } = useTheme(); - - const roundedAmount = Math.round(reward.amount * 1000) / 1000; - const nativeAsset = useBackendNetworksStore.getState().getChainsNativeAsset()[ChainId.optimism]; - const accentColor = isDarkMode ? nativeAsset.colors.primary : nativeAsset.colors.fallback || nativeAsset.colors.primary; - - return ( - - navigate(Routes.OP_REWARDS_SHEET)}> - {i18n.t(i18n.l.expanded_state.swap_details.reward)} 􀅵 - - - - - - {roundedAmount || '<0.001'} {reward.token.symbol} - - - - - ); -} diff --git a/src/components/expanded-state/swap-details/SwapDetailsRow.js b/src/components/expanded-state/swap-details/SwapDetailsRow.js deleted file mode 100644 index 03734909643..00000000000 --- a/src/components/expanded-state/swap-details/SwapDetailsRow.js +++ /dev/null @@ -1,36 +0,0 @@ -import React, { Fragment } from 'react'; -import { ButtonPressAnimation } from '../../animations'; -import { Row } from '../../layout'; -import { Text } from '@/design-system'; - -export const SwapDetailsLabel = ({ children, color = 'secondary60 (Deprecated)' }) => { - return ( - - {children} - - ); -}; - -export const SwapDetailsValue = ({ children, color = 'secondary80 (Deprecated)' }) => { - return ( - - {children} - - ); -}; - -export default function SwapDetailsRow({ children, label, labelColor, labelPress, valuePress, ...props }) { - const LabelWrapper = labelPress ? ButtonPressAnimation : Fragment; - const ValueWrapper = valuePress ? ButtonPressAnimation : Fragment; - - return ( - - - {label} - - - {children} - - - ); -} diff --git a/src/components/expanded-state/swap-details/SwapDetailsSlippageMessage.js b/src/components/expanded-state/swap-details/SwapDetailsSlippageMessage.js deleted file mode 100644 index 135dd07202a..00000000000 --- a/src/components/expanded-state/swap-details/SwapDetailsSlippageMessage.js +++ /dev/null @@ -1,71 +0,0 @@ -import lang from 'i18n-js'; -import React from 'react'; -import Divider from '@/components/Divider'; -import { Centered, Column, ColumnWithMargins, Row } from '../../layout'; -import { Emoji, Text } from '../../text'; -import styled from '@/styled-thing'; -import { padding } from '@/styles'; -import { NO_PRICE_DATA_PERCENTAGE } from '@/hooks/usePriceImpactDetails'; - -const Container = styled(ColumnWithMargins).attrs({ - align: 'center', - margin: 8, -})(padding.object(30, 42, 24)); - -const Heading = styled(Text).attrs(({ weight = 'bold' }) => ({ - size: 'larger', - weight, -}))({}); - -const Message = styled(Text).attrs(({ theme: { colors } }) => ({ - align: 'center', - color: colors.alpha(colors.blueGreyDark, 0.5), - lineHeight: 22, - size: 'smedium', - weight: 'semibold', -}))({}); - -export default function SwapDetailsSlippageMessage({ - isHighPriceImpact, - priceImpactColor, - priceImpactNativeAmount, - priceImpactPercentDisplay, - outputCurrencySymbol, - ...props -}) { - const { colors } = useTheme(); - const headingValue = priceImpactNativeAmount ?? priceImpactPercentDisplay; - const hasPriceData = priceImpactPercentDisplay !== NO_PRICE_DATA_PERCENTAGE; - const impactMsg = `${outputCurrencySymbol} ${lang.t('exchange.price_impact.no_data')}`; - return isHighPriceImpact ? ( - - {hasPriceData ? ( - - - - {lang.t('expanded_state.swap.losing')}{' '} - - - {headingValue} - - 🥵 - - {lang.t('expanded_state.swap.slippage_message')} - - ) : ( - - - {`􀇿 `} - - {impactMsg} - - - {lang.t('exchange.price_impact.no_data_subtitle')} - - )} - - - - - ) : null; -} diff --git a/src/components/expanded-state/swap-details/index.js b/src/components/expanded-state/swap-details/index.js deleted file mode 100644 index 4815af0a17c..00000000000 --- a/src/components/expanded-state/swap-details/index.js +++ /dev/null @@ -1,8 +0,0 @@ -export { default as CurrencyTile, CurrencyTileHeight } from './CurrencyTile'; -export { default as SwapDetailsContent, SwapDetailsContentMinHeight } from './SwapDetailsContent'; -export { default as SwapDetailsContractRow } from './SwapDetailsContractRow'; -export { default as SwapDetailsMasthead, SwapDetailsMastheadHeight } from './SwapDetailsMasthead'; -export { default as SwapDetailsPriceRow } from './SwapDetailsPriceRow'; -export { default as SwapDetailsRow, SwapDetailsLabel, SwapDetailsRowHeight, SwapDetailsValue } from './SwapDetailsRow'; -export { default as SwapDetailsSlippageMessage } from './SwapDetailsSlippageMessage'; -export { default as SwapDetailsExchangeRow } from './SwapDetailsExchangeRow'; diff --git a/src/components/expanded-state/swap-settings/InputPill.js b/src/components/expanded-state/swap-settings/InputPill.js deleted file mode 100644 index 425010d1db7..00000000000 --- a/src/components/expanded-state/swap-settings/InputPill.js +++ /dev/null @@ -1,98 +0,0 @@ -import React, { useCallback } from 'react'; -import LinearGradient from 'react-native-linear-gradient'; -import TextInputMask from 'react-native-text-input-mask'; -import { Row } from '../../../components/layout'; -import { Text } from '../../text'; -import styled from '@/styled-thing'; -import { buildTextStyles, margin, padding } from '@/styles'; - -const ANDROID_EXTRA_LINE_HEIGHT = 8; - -const PillGradient = styled(LinearGradient).attrs(({ theme: { colors } }) => ({ - colors: colors.gradients.lightGreyTransparent, - end: { x: 0.5, y: 1 }, - start: { x: 0, y: 0 }, -}))({ - borderRadius: 15, - height: 40, - ...(ios ? { height: 40 } : padding.object(10, 12)), - maxWidth: 130, - minWidth: 108, - ...(android ? { marginHorizontal: 5 } : {}), -}); - -const NumberInput = styled(TextInputMask).attrs(({ theme: { colors }, value }) => ({ - color: !value && colors.alpha(colors.blueGreyDark, 0.4), - interval: 1, - keyboardAppearance: 'dark', - keyboardType: 'decimal-pad', - letterSpacing: 'rounded', - size: 'lmedium', - textAlign: 'left', - timing: 'linear', - weight: 'heavy', - ...(ios && { - height: '100%', - left: 22, - paddingLeft: 28, - paddingRight: 72, - paddingVertical: 10.5, - }), -}))(props => ({ - ...buildTextStyles.object(props), - ...(android ? padding.object(0, 0, 0, 0) : {}), - ...margin.object(android ? -ANDROID_EXTRA_LINE_HEIGHT : 0, 0, android ? -ANDROID_EXTRA_LINE_HEIGHT : 0, 0), -})); - -const Label = styled(Text).attrs(() => ({ - align: 'center', - pointerEvents: 'none', - size: 'large', - weight: 'heavy', -}))({ - ...margin.object(android ? -ANDROID_EXTRA_LINE_HEIGHT : 0, 0, android ? -ANDROID_EXTRA_LINE_HEIGHT : 0, 0), - ...(ios ? { right: 40 } : {}), - top: android ? -0.5 : 8.5, -}); - -function InputPill({ color, label, onChange: onChangeCallback, onFocus, onBlur, testID, value }, ref) { - const { colors } = useTheme(); - - const onChangeText = useCallback( - text => { - text = text === '.' || text === ',' ? `0${text}` : text; - onChangeCallback(text); - }, - [onChangeCallback] - ); - - return ( - - - - - - - ); -} - -export default React.forwardRef(InputPill); diff --git a/src/components/expanded-state/swap-settings/MaxToleranceInput.tsx b/src/components/expanded-state/swap-settings/MaxToleranceInput.tsx deleted file mode 100644 index d96eb26f373..00000000000 --- a/src/components/expanded-state/swap-settings/MaxToleranceInput.tsx +++ /dev/null @@ -1,149 +0,0 @@ -import lang from 'i18n-js'; -import React, { forwardRef, useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react'; -import { Keyboard, TextInput } from 'react-native'; -import { getDefaultSlippageFromConfig } from '../../../screens/ExchangeModal'; -import { ButtonPressAnimation } from '../../animations'; -import { Icon } from '../../icons'; -import StepButtonInput from './StepButtonInput'; -import { AccentColorProvider, Box, Column, Columns, Inline, Stack, Text } from '@/design-system'; -import { add, convertNumberToString, greaterThan } from '@/helpers/utilities'; -import { useMagicAutofocus, useSwapSettings } from '@/hooks'; -import { useNavigation } from '@/navigation'; -import Routes from '@/navigation/routesNames'; -import { colors } from '@/styles'; -import { ChainId } from '@/state/backendNetworks/types'; - -const convertBipsToPercent = (bips: number) => (bips / 100).toString(); -const convertPercentToBips = (percent: number) => (percent * 100).toString(); - -const SLIPPAGE_INCREMENT = 0.1; - -// eslint-disable-next-line react/display-name -export const MaxToleranceInput = forwardRef(({ colorForAsset, chainId }: { colorForAsset: string; chainId: ChainId }, ref) => { - const { slippageInBips, updateSwapSlippage } = useSwapSettings(); - const { navigate } = useNavigation(); - - const [slippageValue, setSlippageValue] = useState(convertBipsToPercent(slippageInBips)); - - const slippageRef = useRef(null); - - const { handleFocus } = useMagicAutofocus(slippageRef, undefined, true); - - const { hasPriceImpact, priceImpactColor } = useMemo(() => { - const hasPriceImpact = Number(slippageValue) >= 3; - const priceImpactColor = hasPriceImpact ? colors.orange : null; - return { hasPriceImpact, priceImpactColor }; - }, [slippageValue]); - - useImperativeHandle(ref, () => ({ - blur: () => { - slippageRef?.current?.blur(); - }, - reset: () => { - const slippage = getDefaultSlippageFromConfig(chainId) as unknown as number; - onSlippageChange(convertBipsToPercent(slippage)); - }, - })); - - const updateSlippage = useCallback( - (increment: any) => { - const newSlippage = add(slippageValue, increment); - const newSlippageValue = convertNumberToString(newSlippage); - if (greaterThan(0, newSlippageValue)) return; - - // @ts-expect-error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. - updateSwapSlippage(convertPercentToBips(parseFloat(newSlippageValue))); - setSlippageValue(newSlippageValue); - }, - [slippageValue, updateSwapSlippage] - ); - - const addSlippage = useCallback(() => { - updateSlippage(SLIPPAGE_INCREMENT); - }, [updateSlippage]); - - const minusSlippage = useCallback(() => { - updateSlippage(-SLIPPAGE_INCREMENT); - }, [updateSlippage]); - - const onSlippageChange = useCallback( - (value: any) => { - // @ts-expect-error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. - updateSwapSlippage(convertPercentToBips(value)); - setSlippageValue(value); - }, - [updateSwapSlippage, setSlippageValue] - ); - - const openSlippageExplainer = () => { - Keyboard.dismiss(); - navigate(Routes.EXPLAIN_SHEET, { - type: 'slippage', - }); - }; - - return ( - - - - - - - {`${lang.t('exchange.slippage_tolerance')} `} - {!hasPriceImpact && ( - - {' 􀅵'} - - )} - - {hasPriceImpact && ( - - - - )} - - - {hasPriceImpact && ( - - - - - {lang.t('exchange.high')} - - - {` · ${lang.t('exchange.price_impact.label')}`} - - - )} - - - - - - - ); -}); diff --git a/src/components/expanded-state/swap-settings/SourcePicker.js b/src/components/expanded-state/swap-settings/SourcePicker.js deleted file mode 100644 index a357d100991..00000000000 --- a/src/components/expanded-state/swap-settings/SourcePicker.js +++ /dev/null @@ -1,140 +0,0 @@ -import lang from 'i18n-js'; -import React, { useCallback } from 'react'; -import { Keyboard } from 'react-native'; -import RainbowExchange from '../../../assets/exchanges/both.png'; -import OneInchExchange from '../../../assets/exchanges/oneinch.png'; -import ZeroXExchange from '../../../assets/exchanges/zerox.png'; -import { ButtonPressAnimation } from '../../animations'; -import { ContextMenuButton } from '../../context-menu'; -import { Box, Column, Columns, Inline, Text } from '@/design-system'; -import { ImgixImage } from '@/components/images'; -import { useNavigation } from '@/navigation'; -import { Source } from '@/raps/references'; -import Routes from '@/navigation/routesNames'; - -import { showActionSheetWithOptions } from '@/utils'; - -const sourceMenuItems = () => { - return Object.values(Source).map(source => ({ - actionKey: source, - actionTitle: lang.t(`exchange.source.${source}`), - icon: { - iconType: 'ASSET', - iconValue: `${source}`, - }, - })); -}; - -const androidSourceMenuItems = () => { - return Object.values(Source).reduce((obj, key) => ((obj[key] = lang.t(`exchange.source.${key}`)), obj), {}); -}; - -export default function SourcePicker({ onSelect, currentSource }) { - const { navigate } = useNavigation(); - const imageSource = useMemo(() => { - let source = null; - switch (currentSource) { - case Source.Aggregator1inch: - source = OneInchExchange; - break; - case Source.Aggregator0x: - source = ZeroXExchange; - break; - default: - source = RainbowExchange; - break; - } - - return source; - }, [currentSource]); - - const handleOnPressMenuItem = useCallback( - ({ nativeEvent: { actionKey } }) => { - onSelect(actionKey); - }, - [onSelect] - ); - const onPressAndroid = useCallback(() => { - const menuOptions = Object.values(androidSourceMenuItems()); - showActionSheetWithOptions( - { - options: menuOptions, - showSeparators: true, - }, - idx => { - if (idx !== undefined) { - const menuOptionsKeys = Object.keys(androidSourceMenuItems()); - onSelect(menuOptionsKeys[idx]); - } - } - ); - }, [onSelect]); - - const openRoutesExplainer = () => { - Keyboard.dismiss(); - navigate(Routes.EXPLAIN_SHEET, { - type: 'routeSwaps', - }); - }; - - return ( - - - - - {lang.t('exchange.source_picker')} - - {' 􀅵'} - - - - - - - - > - - - - {`${lang.t(`exchange.source.${currentSource}`)} 􀆈`} - - - - - - - ); -} diff --git a/src/components/expanded-state/swap-settings/StepButtonInput.js b/src/components/expanded-state/swap-settings/StepButtonInput.js deleted file mode 100644 index 8e5c200a075..00000000000 --- a/src/components/expanded-state/swap-settings/StepButtonInput.js +++ /dev/null @@ -1,145 +0,0 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react'; -import ReactNativeHapticFeedback from 'react-native-haptic-feedback'; -import { ButtonPressAnimation } from '../../animations'; -import { Row } from '../../layout'; -import { Text } from '../../text'; -import InputPill from './InputPill'; -import { delay } from '@/helpers/utilities'; -import { usePrevious } from '@/hooks'; -import styled from '@/styled-thing'; - -const PLUS_ACTION_TYPE = 'plus'; -const MINUS_ACTION_TYPE = 'minus'; -const LONG_PRESS_DELAY_THRESHOLD = 69; -const MIN_LONG_PRESS_DELAY_THRESHOLD = 200; - -const Wrapper = styled(Row)({}); - -const StepButtonWrapper = styled(ButtonPressAnimation).attrs(() => ({ - paddingHorizontal: 7, - scaleTo: 0.75, -}))({}); - -const StepButtonText = styled(Text).attrs(({ theme: { colors }, color }) => ({ - color: color || colors.appleBlue, - lineHeight: 40, - size: 'lmedium', - weight: 'heavy', -}))({}); - -const StepButton = ({ type, onLongPress, onLongPressEnded, onPress, shouldLongPressHoldPress, buttonColor }) => { - return ( - - {type === 'plus' ? '􀁍' : '􀁏'} - - ); -}; - -export default function StepButtonInput({ - value, - inputLabel, - plusAction, - minusAction, - onBlur, - onChange, - onFocus, - buttonColor, - testID, - inputRef, -}) { - const longPressHandle = useRef(null); - const [trigger, setTrigger] = useState(false); - const [actionType, setActionType] = useState(null); - const prevTrigger = usePrevious(trigger); - - const onMinusPress = useCallback(() => { - longPressHandle.current = false; - minusAction(); - }, [minusAction]); - - const onPlusPress = useCallback(() => { - longPressHandle.current = false; - plusAction(); - }, [plusAction]); - - const onLongPressEnded = useCallback(() => { - longPressHandle.current = false; - setActionType(null); - }, [longPressHandle]); - - const onLongPressLoop = useCallback(async () => { - setTrigger(true); - setTrigger(false); - await delay(LONG_PRESS_DELAY_THRESHOLD); - longPressHandle.current && onLongPressLoop(); - }, []); - - const onLongPress = useCallback(async () => { - longPressHandle.current = true; - onLongPressLoop(); - }, [onLongPressLoop]); - - const onPlusLongPress = useCallback(() => { - setActionType(PLUS_ACTION_TYPE); - onLongPress(); - }, [onLongPress]); - - const onMinusLongPress = useCallback(() => { - setActionType(MINUS_ACTION_TYPE); - onLongPress(); - }, [onLongPress]); - - useEffect(() => { - if (!prevTrigger && trigger) { - if (actionType === PLUS_ACTION_TYPE) { - plusAction(); - if (!android) { - ReactNativeHapticFeedback.trigger('selection'); - } - } else if (actionType === MINUS_ACTION_TYPE) { - minusAction(); - if (!android) { - ReactNativeHapticFeedback.trigger('selection'); - } - } - } - }, [trigger, prevTrigger, actionType, plusAction, minusAction]); - - return ( - - - - - - ); -} diff --git a/src/components/expanded-state/swap-settings/SwapSettingsState.js b/src/components/expanded-state/swap-settings/SwapSettingsState.js deleted file mode 100644 index 7432ebece10..00000000000 --- a/src/components/expanded-state/swap-settings/SwapSettingsState.js +++ /dev/null @@ -1,201 +0,0 @@ -import { useIsFocused, useRoute } from '@react-navigation/native'; -import lang from 'i18n-js'; -import React, { useCallback, useEffect, useRef } from 'react'; -import { Keyboard } from 'react-native'; -import { Switch } from 'react-native-gesture-handler'; -import { useDispatch } from 'react-redux'; -import { ButtonPressAnimation } from '../../animations'; -import { ExchangeHeader } from '../../exchange'; -import { FloatingPanel } from '../../floating-panels'; -import { SlackSheet } from '../../sheet'; -import { MaxToleranceInput } from './MaxToleranceInput'; -import SourcePicker from './SourcePicker'; - -import { Box, ColorModeProvider, Column, Columns, Inset, Stack, Text } from '@/design-system'; - -import { useAccountSettings, useColorForAsset, useKeyboardHeight, useSwapSettings } from '@/hooks'; -import { useNavigation } from '@/navigation'; -import { Source } from '@/raps/references'; -import Routes from '@/navigation/routesNames'; -import { deviceUtils } from '@/utils'; -import { IS_ANDROID } from '@/env'; -const MAX_TEXT_WIDTH = 210; - -function useAndroidDisableGesturesOnFocus() { - const { params } = useRoute(); - const isFocused = useIsFocused(); - useEffect(() => { - android && params?.toggleGestureEnabled?.(!isFocused); - }, [isFocused, params]); -} - -export default function SwapSettingsState({ asset }) { - const { flashbotsEnabled, settingsChangeFlashbotsEnabled } = useAccountSettings(); - const { - params: { swapSupportsFlashbots = false, chainId }, - } = useRoute(); - const { colors } = useTheme(); - const { setParams, goBack } = useNavigation(); - const dispatch = useDispatch(); - const keyboardHeight = useKeyboardHeight(); - const slippageRef = useRef(null); - const { updateSwapSource, source } = useSwapSettings(); - const isFocused = useIsFocused(); - const { navigate } = useNavigation(); - const keyboardShowListener = useRef(null); - const keyboardHideListener = useRef(null); - useAndroidDisableGesturesOnFocus(); - - const handleKeyboardDidHide = useCallback(() => { - if (isFocused) { - goBack(); - } - }, [goBack, isFocused]); - const handleKeyboardDidShow = useCallback(() => { - if (!isFocused) { - Keyboard.dismiss(); - } - }, [isFocused]); - const toggleFlashbotsEnabled = useCallback(async () => { - await dispatch(settingsChangeFlashbotsEnabled(!flashbotsEnabled)); - }, [dispatch, flashbotsEnabled, settingsChangeFlashbotsEnabled]); - - useEffect(() => { - if (IS_ANDROID) { - keyboardShowListener.current = Keyboard.addListener('keyboardDidShow', handleKeyboardDidShow); - keyboardHideListener.current = Keyboard.addListener('keyboardDidHide', handleKeyboardDidHide); - } - return () => { - keyboardShowListener.current?.remove(); - keyboardHideListener.current?.remove(); - }; - }, [handleKeyboardDidHide, handleKeyboardDidShow]); - - const colorForAsset = useColorForAsset(asset || {}, null, false, true); - - const [currentSource, setCurrentSource] = useState(source); - const updateSource = useCallback( - newSource => { - setCurrentSource(newSource); - updateSwapSource(newSource); - }, - [updateSwapSource] - ); - - const sheetHeightWithoutKeyboard = (android ? 275 : 245) + (swapSupportsFlashbots ? 55 : 0); - - const sheetHeightWithKeyboard = sheetHeightWithoutKeyboard + keyboardHeight + (deviceUtils.isSmallPhone ? 30 : 0); - - useEffect(() => { - setParams({ longFormHeight: sheetHeightWithKeyboard }); - }, [sheetHeightWithKeyboard, setParams]); - - const resetToDefaults = useCallback(() => { - slippageRef?.current?.reset(); - settingsChangeFlashbotsEnabled(false); - updateSource(Source.AggregatorRainbow); - }, [settingsChangeFlashbotsEnabled, updateSource]); - - const openFlashbotsExplainer = () => { - Keyboard.dismiss(); - navigate(Routes.EXPLAIN_SHEET, { - type: 'flashbots', - }); - }; - - return ( - - - - - - - {lang.t('exchange.settings')} - - - {swapSupportsFlashbots && ( - - - - - {lang.t('exchange.use_flashbots')} - - {' 􀅵'} - - - - - - - - - )} - - - - - - - - - - - - - {lang.t('exchange.use_defaults')} - - - - - - - { - ios && slippageRef?.current?.blur(); - goBack(); - }} - style={{ maxWidth: 130 }} - > - - - - {lang.t('exchange.done')} - - - - - - - - - - ); -} diff --git a/src/components/expanded-state/unique-token/NFTBriefTokenInfoRow.tsx b/src/components/expanded-state/unique-token/NFTBriefTokenInfoRow.tsx index 8247be503b0..269fc3a8191 100644 --- a/src/components/expanded-state/unique-token/NFTBriefTokenInfoRow.tsx +++ b/src/components/expanded-state/unique-token/NFTBriefTokenInfoRow.tsx @@ -13,6 +13,7 @@ import { UniqueAsset } from '@/entities'; import { fetchReservoirNFTFloorPrice } from '@/resources/nfts/utils'; import { handleReviewPromptAction } from '@/utils/reviewAlert'; import { ReviewPromptAction } from '@/storage/schema'; +import { ChainId } from '@/state/backendNetworks/types'; const NONE = 'None'; @@ -83,7 +84,7 @@ export default function NFTBriefTokenInfoRow({ asset }: { asset: UniqueAsset }) }, [navigate]); const lastSalePrice = formatPrice(asset?.lastPrice, asset?.lastSalePaymentToken); - const priceOfEth = ethereumUtils.getEthPriceUnit() as number; + const priceOfEth = ethereumUtils.getPriceOfNativeAssetForNetwork({ chainId: ChainId.mainnet }); return ( diff --git a/src/components/fab/ExchangeFab.js b/src/components/fab/ExchangeFab.js deleted file mode 100644 index 744d69c8f31..00000000000 --- a/src/components/fab/ExchangeFab.js +++ /dev/null @@ -1,69 +0,0 @@ -import lang from 'i18n-js'; -import React, { useCallback } from 'react'; -import { delayNext } from '../../hooks/useMagicAutofocus'; -import { useNavigation } from '../../navigation/Navigation'; -import { lightModeThemeColors } from '../../styles/colors'; -import { Text } from '../text'; -import FloatingActionButton from './FloatingActionButton'; -import { enableActionsOnReadOnlyWallet } from '@/config/debug'; -import { CurrencySelectionTypes, ExchangeModalTypes } from '@/helpers'; -import { useSwapCurrencyHandlers } from '@/hooks'; -import Routes from '@/navigation/routesNames'; -import styled from '@/styled-thing'; -import { magicMemo, watchingAlert } from '@/utils'; - -const FabShadow = [ - [0, 10, 30, lightModeThemeColors.shadow, 0.8], - [0, 5, 15, lightModeThemeColors.swapPurple, 1], -]; - -const FabIcon = styled(Text).attrs(({ theme: { colors } }) => ({ - align: 'center', - color: colors.whiteLabel, - letterSpacing: 'zero', - size: 24, - weight: 'semibold', -}))({}); - -const ExchangeFab = ({ disabled, isReadOnlyWallet, ...props }) => { - const { navigate } = useNavigation(); - const { colors } = useTheme(); - - const { updateInputCurrency } = useSwapCurrencyHandlers({ - shouldUpdate: false, - type: ExchangeModalTypes.swap, - }); - - const handlePress = useCallback(() => { - if (!isReadOnlyWallet || enableActionsOnReadOnlyWallet) { - android && delayNext(); - navigate(Routes.EXCHANGE_MODAL, { - fromDiscover: true, - params: { - fromDiscover: true, - onSelectCurrency: updateInputCurrency, - title: lang.t('swap.modal_types.swap'), - type: CurrencySelectionTypes.input, - }, - screen: Routes.CURRENCY_SELECT_SCREEN, - }); - } else { - watchingAlert(); - } - }, [isReadOnlyWallet, navigate, updateInputCurrency]); - - return ( - - 􀖅 - - ); -}; - -export default magicMemo(ExchangeFab, ['disabled', 'isReadOnlyWallet']); diff --git a/src/components/fab/FabWrapper.js b/src/components/fab/FabWrapper.js deleted file mode 100644 index f988107eadc..00000000000 --- a/src/components/fab/FabWrapper.js +++ /dev/null @@ -1,40 +0,0 @@ -import React, { createElement } from 'react'; -import { safeAreaInsetValues } from '../../utils'; -import { FlexItem, RowWithMargins } from '../layout'; -import ExchangeFab from './ExchangeFab'; -import SendFab from './SendFab'; -import styled from '@/styled-thing'; - -export const FabWrapperBottomPosition = 21 + safeAreaInsetValues.bottom; -export const FabWrapperItemMargin = 15; - -const FabWrapperRow = styled(RowWithMargins).attrs({ margin: 13 })({ - bottom: ({ isEditMode }) => (isEditMode ? -60 : FabWrapperBottomPosition), - position: 'absolute', - right: FabWrapperItemMargin, - zIndex: 2, -}); - -export default function FabWrapper({ children, disabled, fabs = [ExchangeFab, SendFab], isCoinListEdited, isReadOnlyWallet, ...props }) { - const renderFab = React.useCallback( - (fab, index) => { - const id = `${index}`; - return createElement(fab, { - isReadOnlyWallet, - key: `fab-${id}`, - ...props, - }); - }, - [props, isReadOnlyWallet] - ); - return ( - - {children} - {!disabled && ( - - {fabs.map(renderFab)} - - )} - - ); -} diff --git a/src/components/fab/FloatingActionButton.js b/src/components/fab/FloatingActionButton.js index b82cf04f377..cd566a3da68 100644 --- a/src/components/fab/FloatingActionButton.js +++ b/src/components/fab/FloatingActionButton.js @@ -1,7 +1,7 @@ import React, { useCallback, useMemo } from 'react'; import { darkModeThemeColors } from '../../styles/colors'; import { useTheme } from '../../theme/ThemeContext'; -import { magicMemo } from '../../utils'; +import { magicMemo, safeAreaInsetValues } from '../../utils'; import ButtonPressAnimation, { ScaleButtonZoomableAndroid } from '../animations/ButtonPressAnimation'; import { Centered, InnerBorder } from '../layout'; import styled from '@/styled-thing'; @@ -9,6 +9,7 @@ import { borders, position } from '@/styles'; import ShadowStack from '@/react-native-shadow-stack'; export const FloatingActionButtonSize = 56; +export const FabWrapperBottomPosition = 21 + safeAreaInsetValues.bottom; export const FloatingActionButtonShadow = colors => [ [0, 2, 5, colors.shadow, 0.2], diff --git a/src/components/fab/SendFab.js b/src/components/fab/SendFab.js deleted file mode 100644 index 77ab1450770..00000000000 --- a/src/components/fab/SendFab.js +++ /dev/null @@ -1,51 +0,0 @@ -import React, { useCallback } from 'react'; -import { useNavigation } from '../../navigation/Navigation'; -import { lightModeThemeColors } from '../../styles/colors'; -import { useTheme } from '../../theme/ThemeContext'; -import { Text } from '../text'; -import FloatingActionButton from './FloatingActionButton'; -import { enableActionsOnReadOnlyWallet } from '@/config/debug'; -import Routes from '@/navigation/routesNames'; -import styled from '@/styled-thing'; -import { magicMemo, watchingAlert } from '@/utils'; - -const FabShadow = [ - [0, 10, 30, lightModeThemeColors.shadow, 0.8], - [0, 5, 15, lightModeThemeColors.paleBlue, 1], -]; - -const FabIcon = styled(Text).attrs(({ theme: { colors } }) => ({ - align: 'center', - color: colors.whiteLabel, - letterSpacing: 'zero', - size: 24, - weight: 'semibold', -}))({}); - -const SendFab = ({ disabled, isReadOnlyWallet, ...props }) => { - const { navigate } = useNavigation(); - const { colors } = useTheme(); - - const handlePress = useCallback(() => { - if (!isReadOnlyWallet || enableActionsOnReadOnlyWallet) { - navigate(Routes.SEND_FLOW); - } else { - watchingAlert(); - } - }, [navigate, isReadOnlyWallet]); - - return ( - - 􀈠 - - ); -}; - -export default magicMemo(SendFab, ['disabled', 'isReadOnlyWallet']); diff --git a/src/components/fab/index.js b/src/components/fab/index.js index 9f6597b65a5..06a513c4e6f 100644 --- a/src/components/fab/index.js +++ b/src/components/fab/index.js @@ -1,5 +1,7 @@ -export { default as ExchangeFab } from './ExchangeFab'; export { default as ExtraStates } from './ExtraStates'; -export { default as FabWrapper, FabWrapperBottomPosition } from './FabWrapper'; -export { default as FloatingActionButton, FloatingActionButtonSize } from './FloatingActionButton'; -export { default as SendFab } from './SendFab'; +export { + default as FloatingActionButton, + FloatingActionButtonSize, + FloatingActionButtonShadow, + FabWrapperBottomPosition, +} from './FloatingActionButton'; diff --git a/src/components/fields/BubbleField.js b/src/components/fields/BubbleField.js index e3cc4d7eb17..7f55d739fd2 100644 --- a/src/components/fields/BubbleField.js +++ b/src/components/fields/BubbleField.js @@ -1,7 +1,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useTheme } from '../../theme/ThemeContext'; import { MiniButton } from '../buttons'; -import { ExchangeInput } from '../exchange'; +import ExchangeInput from '@/components/ExchangeInput'; import { Column, Row } from '../layout'; import { useDimensions } from '@/hooks'; import styled from '@/styled-thing'; diff --git a/src/components/fields/SmallBubbleField.js b/src/components/fields/SmallBubbleField.js index 9c7697f12e2..f9898fe1861 100644 --- a/src/components/fields/SmallBubbleField.js +++ b/src/components/fields/SmallBubbleField.js @@ -1,10 +1,11 @@ import React, { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'; import { useTheme } from '../../theme/ThemeContext'; -import { ExchangeInput } from '../exchange'; +import ExchangeInput from '@/components/ExchangeInput'; import { Column, Row } from '../layout'; import { Text } from '../text'; import { useDimensions } from '@/hooks'; import styled from '@/styled-thing'; + const BubbleInput = styled(ExchangeInput).attrs(({ isSmallPhone, isTinyPhone, theme: { isDarkMode } }) => ({ disableTabularNums: true, keyboardAppearance: isDarkMode ? 'dark' : 'light', diff --git a/src/components/gas/GasSpeedButton.tsx b/src/components/gas/GasSpeedButton.tsx index 39ca7d145dd..f9f468803e8 100644 --- a/src/components/gas/GasSpeedButton.tsx +++ b/src/components/gas/GasSpeedButton.tsx @@ -15,14 +15,13 @@ import { Text } from '../text'; import { GasSpeedLabelPager } from '.'; import ContextMenuButton from '@/components/native-context-menu/contextMenu'; import { isL2Chain } from '@/handlers/web3'; -import { add, greaterThan, toFixedDecimals } from '@/helpers/utilities'; -import { getCrossChainTimeEstimate } from '@/utils/crossChainTimeEstimates'; -import { useAccountSettings, useColorForAsset, useGas, usePrevious, useSwapCurrencies } from '@/hooks'; +import { add, convertAmountToNativeDisplayWorklet, greaterThan, toFixedDecimals } from '@/helpers/utilities'; +import { useAccountSettings, useColorForAsset, useGas, usePrevious } from '@/hooks'; import { useNavigation } from '@/navigation'; import Routes from '@/navigation/routesNames'; import styled from '@/styled-thing'; import { fonts, fontWithWidth, margin, padding } from '@/styles'; -import { ethereumUtils, gasUtils } from '@/utils'; +import { gasUtils } from '@/utils'; import { IS_ANDROID } from '@/env'; import { ContextMenu } from '../context-menu'; import { EthCoinIcon } from '../coin-icon/EthCoinIcon'; @@ -117,13 +116,9 @@ const GasSpeedPagerCentered = styled(Centered).attrs(() => ({ const TextContainer = styled(Column).attrs(() => ({}))({}); -const TransactionTimeLabel = ({ formatter, isLongWait, theme }: { formatter: () => string; isLongWait: boolean; theme: string }) => { +const TransactionTimeLabel = ({ formatter, theme }: { formatter: () => string; theme: string }) => { const { colors } = useTheme(); - let color = theme === 'dark' ? colors.alpha(darkModeThemeColors.blueGreyDark, 0.6) : colors.alpha(colors.blueGreyDark, 0.6); - - if (isLongWait) { - color = colors.lightOrange; - } + const color = theme === 'dark' ? colors.alpha(darkModeThemeColors.blueGreyDark, 0.6) : colors.alpha(colors.blueGreyDark, 0.6); return (
)} diff --git a/src/screens/SwapModal.js b/src/screens/SwapModal.js deleted file mode 100644 index 5cf2e2dcc81..00000000000 --- a/src/screens/SwapModal.js +++ /dev/null @@ -1,28 +0,0 @@ -import { useRoute } from '@react-navigation/native'; -import React, { useEffect } from 'react'; -import ExchangeModal from './ExchangeModal'; -import { ExchangeModalTypes } from '@/helpers'; -import { useNavigation } from '@/navigation'; - -const SwapModal = (props, ref) => { - const { params = {} } = useRoute(); - const { ignoreInitialTypeCheck, fromDiscover } = params; - const { setParams } = useNavigation(); - useEffect(() => { - setParams({ focused: true }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return ( - - ); -}; - -export default React.forwardRef(SwapModal); diff --git a/src/screens/WalletConnectApprovalSheet.tsx b/src/screens/WalletConnectApprovalSheet.tsx index 2e0a27d3e00..e979e6f3ed3 100644 --- a/src/screens/WalletConnectApprovalSheet.tsx +++ b/src/screens/WalletConnectApprovalSheet.tsx @@ -37,7 +37,7 @@ import { RootStackParamList } from '@/navigation/types'; import { Address } from 'viem'; import { RainbowWallet } from '@/model/wallet'; import { IS_IOS } from '@/env'; -import { WalletconnectMeta } from '@/redux/walletconnect'; +import { WalletconnectMeta } from '@/walletConnect/types'; type WithThemeProps = { theme: ThemeContextProps; diff --git a/src/screens/claimables/ClaimingClaimableSharedUI.tsx b/src/screens/claimables/ClaimingClaimableSharedUI.tsx index d117e5b9cce..6d3ef2290e8 100644 --- a/src/screens/claimables/ClaimingClaimableSharedUI.tsx +++ b/src/screens/claimables/ClaimingClaimableSharedUI.tsx @@ -14,7 +14,7 @@ import { useBackendNetworksStore } from '@/state/backendNetworks/backendNetworks import { useNavigation } from '@/navigation'; import { TextColor } from '@/design-system/color/palettes'; import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'; -import { convertAmountToNativeDisplayWorklet } from '@/__swaps__/utils/numbers'; +import { convertAmountToNativeDisplayWorklet } from '@/helpers/utilities'; import { useAccountSettings, useWallets } from '@/hooks'; import { enableActionsOnReadOnlyWallet } from '@/config'; import { debounce } from 'lodash'; diff --git a/src/screens/claimables/ClaimingTransactionClaimable.tsx b/src/screens/claimables/ClaimingTransactionClaimable.tsx index c320467c28d..ac0adea0a73 100644 --- a/src/screens/claimables/ClaimingTransactionClaimable.tsx +++ b/src/screens/claimables/ClaimingTransactionClaimable.tsx @@ -9,7 +9,7 @@ import { useBackendNetworksStore } from '@/state/backendNetworks/backendNetworks import { logger, RainbowError } from '@/logger'; import { ClaimingClaimableSharedUI, ClaimStatus } from './ClaimingClaimableSharedUI'; import { TransactionRequest } from '@ethersproject/providers'; -import { convertAmountToNativeDisplayWorklet } from '@/__swaps__/utils/numbers'; +import { convertAmountToNativeDisplayWorklet } from '@/helpers/utilities'; import { useMutation } from '@tanstack/react-query'; import { loadWallet } from '@/model/wallet'; import { walletExecuteRap } from '@/rapsV2/execute'; diff --git a/src/screens/discover/components/DiscoverHome.tsx b/src/screens/discover/components/DiscoverHome.tsx index 866c7688096..7132c19c016 100644 --- a/src/screens/discover/components/DiscoverHome.tsx +++ b/src/screens/discover/components/DiscoverHome.tsx @@ -80,7 +80,11 @@ export default function DiscoverHome() { {!IS_TEST && nftOffersEnabled && } {/* We have both flags here to be able to override the remote flag and show the card anyway in Dev*/} {featuredResultsEnabled && ( - + + {({ featuredResult, handlePress }) => ( + + )} + )} {(opRewardsRemoteFlag || opRewardsLocalFlag) && } {hardwareWalletsEnabled && !hasHardwareWallets && } diff --git a/src/screens/discover/components/DiscoverSearch.js b/src/screens/discover/components/DiscoverSearch.js index a3172798530..4623723d9ee 100644 --- a/src/screens/discover/components/DiscoverSearch.js +++ b/src/screens/discover/components/DiscoverSearch.js @@ -7,7 +7,7 @@ import { useDebounce } from 'use-debounce'; import CurrencySelectionTypes from '@/helpers/currencySelectionTypes'; import deviceUtils from '@/utils/deviceUtils'; -import { CurrencySelectionList } from '@/components/exchange'; +import CurrencySelectionList from '@/components/CurrencySelectionList'; import { Row } from '@/components/layout'; import DiscoverSheetContext from '../DiscoverScreenContext'; import { analytics } from '@/analytics'; @@ -22,8 +22,8 @@ import { ethereumUtils } from '@/utils'; import { getPoapAndOpenSheetWithQRHash, getPoapAndOpenSheetWithSecretWord } from '@/utils/poaps'; import { navigateToMintCollection } from '@/resources/reservoir/mints'; import { TAB_BAR_HEIGHT } from '@/navigation/SwipeNavigator'; -import { ChainId, Network } from '@/chains/types'; -import { getChainsIdByName } from '@/chains'; +import { ChainId, Network } from '@/state/backendNetworks/types'; +import { useBackendNetworksStore } from '@/state/backendNetworks/backendNetworks'; export const SearchContainer = styled(Row)({ height: '100%', @@ -134,7 +134,7 @@ export default function DiscoverSearch() { network === Network.optimism; } const contractAddress = query.split('/')[1]; - navigateToMintCollection(contractAddress, getChainsIdByName()[network]); + navigateToMintCollection(contractAddress, useBackendNetworksStore.getState().getChainsIdByName()[network]); } }; checkAndHandleMint(searchQuery); diff --git a/src/screens/mints/MintSheet.tsx b/src/screens/mints/MintSheet.tsx index 288de66bc10..f9f08818ee4 100644 --- a/src/screens/mints/MintSheet.tsx +++ b/src/screens/mints/MintSheet.tsx @@ -171,7 +171,7 @@ const MintSheet = () => { symbol: mintCollection.publicMintInfo?.price?.currency?.symbol || 'ETH', }); - const priceOfEth = ethereumUtils.getEthPriceUnit() as number; + const priceOfEth = ethereumUtils.getPriceOfNativeAssetForNetwork({ chainId: ChainId.mainnet }); const nativeMintPriceDisplay = convertAmountToNativeDisplay(parseFloat(multiply(price.amount, quantity)) * priceOfEth, nativeCurrency); @@ -628,7 +628,6 @@ const MintSheet = () => { /> - {/* @ts-ignore */} = ({ trans const { colors } = useTheme(); const hash = useMemo(() => ethereumUtils.getHash(transaction), [transaction]); const { network, status, chainId } = transaction; - const isReadOnly = useSelector((state: AppState) => state.wallets.selected?.type === WalletTypes.readOnly ?? true); + const isReadOnly = useSelector((state: AppState) => state.wallets.selected?.type === WalletTypes.readOnly); // Retry swap related data const retrySwapMetadata = useMemo(() => { const data = swapMetadataStorage.getString(hash ?? ''); @@ -44,13 +44,9 @@ export const TransactionDetailsHashAndActionsSection: React.FC = ({ trans const onRetrySwap = useCallback(() => { Navigation.handleAction(Routes.WALLET_SCREEN, {}); - Navigation.handleAction(Routes.EXCHANGE_MODAL, { - params: { - meta: retrySwapMetadata, - inputAsset: retrySwapMetadata?.inputAsset, - outputAsset: retrySwapMetadata?.outputAsset, - }, - }); + + // TODO: Add retry swap logic back for swaps + Navigation.handleAction(Routes.SWAP, {}); }, [retrySwapMetadata]); if (!hash || !network) { diff --git a/src/screens/transaction-details/components/TransactionDetailsValueAndFeeSection.tsx b/src/screens/transaction-details/components/TransactionDetailsValueAndFeeSection.tsx index 5bb152da3f5..dfde595df20 100644 --- a/src/screens/transaction-details/components/TransactionDetailsValueAndFeeSection.tsx +++ b/src/screens/transaction-details/components/TransactionDetailsValueAndFeeSection.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { DoubleLineTransactionDetailsRow } from '@/screens/transaction-details/components/DoubleLineTransactionDetailsRow'; import { TransactionDetailsSymbol } from '@/screens/transaction-details/components/TransactionDetailsSymbol'; -import { RainbowTransaction, RainbowTransactionFee } from '@/entities/transactions/transaction'; +import { RainbowTransaction } from '@/entities/transactions/transaction'; import { Box, Stack, globalColors } from '@/design-system'; import { TransactionDetailsDivider } from '@/screens/transaction-details/components/TransactionDetailsDivider'; import * as i18n from '@/languages'; @@ -14,12 +14,11 @@ import { CardSize } from '@/components/unique-token/CardSize'; import ImgixImage from '@/components/images/ImgixImage'; import { View } from 'react-native'; import ChainBadge from '@/components/coin-icon/ChainBadge'; -import { checkForPendingSwap } from '../helpers/checkForPendingSwap'; +import { checkForPendingSwap } from '@/helpers/checkForPendingSwap'; import { ChainId } from '@/state/backendNetworks/types'; type Props = { transaction: RainbowTransaction; - fee?: RainbowTransactionFee; nativeCurrencyValue?: string; value?: string; }; diff --git a/src/screens/transaction-details/components/TransactionMasthead.tsx b/src/screens/transaction-details/components/TransactionMasthead.tsx index 95e7f078d6d..7b360be290c 100644 --- a/src/screens/transaction-details/components/TransactionMasthead.tsx +++ b/src/screens/transaction-details/components/TransactionMasthead.tsx @@ -33,7 +33,7 @@ import { addressHashedColorIndex, addressHashedEmoji } from '@/utils/profileUtil import ImageAvatar from '@/components/contacts/ImageAvatar'; import RainbowCoinIcon from '@/components/coin-icon/RainbowCoinIcon'; import * as lang from '@/languages'; -import { checkForPendingSwap } from '../helpers/checkForPendingSwap'; +import { checkForPendingSwap } from '@/helpers/checkForPendingSwap'; import { ChainId } from '@/state/backendNetworks/types'; const TransactionMastheadHeight = android ? 153 : 135; diff --git a/src/state/assets/userAssets.ts b/src/state/assets/userAssets.ts index 125ab01ba02..ce6893998bc 100644 --- a/src/state/assets/userAssets.ts +++ b/src/state/assets/userAssets.ts @@ -49,16 +49,34 @@ export interface UserAssetsState { setUserAssets: (userAssets: Map | ParsedSearchAsset[]) => void; } +type UserAssetsStateToPersist = Omit< + Partial, + | 'currentAbortController' + | 'inputSearchQuery' + | 'searchCache' + | 'getBalanceSortedChainList' + | 'getChainsWithBalance' + | 'getFilteredUserAssetIds' + | 'getHighestValueEth' + | 'getUserAsset' + | 'getUserAssets' + | 'selectUserAssetIds' + | 'selectUserAssets' + | 'setSearchCache' + | 'setSearchQuery' + | 'setUserAssets' +>; + // NOTE: We are serializing Map as an Array<[UniqueId, ParsedSearchAsset]> -type UserAssetsStateWithTransforms = Omit, 'chainBalances' | 'idsByChain' | 'userAssets'> & { +type UserAssetsStateToPersistWithTransforms = Omit & { chainBalances: Array<[ChainId, number]>; idsByChain: Array<[UserAssetFilter, UniqueId[]]>; userAssets: Array<[UniqueId, ParsedSearchAsset]>; }; -function serializeUserAssetsState(state: Partial, version?: number) { +function serializeUserAssetsState(state: UserAssetsStateToPersist, version?: number) { try { - const transformedStateToPersist: UserAssetsStateWithTransforms = { + const transformedStateToPersist: UserAssetsStateToPersistWithTransforms = { ...state, chainBalances: state.chainBalances ? Array.from(state.chainBalances.entries()) : [], idsByChain: state.idsByChain ? Array.from(state.idsByChain.entries()) : [], @@ -76,7 +94,7 @@ function serializeUserAssetsState(state: Partial, version?: num } function deserializeUserAssetsState(serializedState: string) { - let parsedState: { state: UserAssetsStateWithTransforms; version: number }; + let parsedState: { state: UserAssetsStateToPersistWithTransforms; version: number }; try { parsedState = JSON.parse(serializedState); } catch (error) { @@ -335,7 +353,13 @@ export const createUserAssetsStore = (address: Address | string) => }), { storageKey: `userAssets_${address}`, - version: 0, + partialize: state => ({ + chainBalances: state.chainBalances, + filter: state.filter, + idsByChain: state.idsByChain, + userAssets: state.userAssets, + }), + version: 1, serializer: serializeUserAssetsState, deserializer: deserializeUserAssetsState, } @@ -378,6 +402,6 @@ export function useUserAssetsStore(selector: (state: UserAssetsState) => T) { return useStore(store, useCallback(selector, [])); } -function getCurrentSearchCache(): Map | undefined { +function getCurrentSearchCache(): Map { return getOrCreateStore().getState().searchCache; } diff --git a/src/state/browser/browserStore.ts b/src/state/browser/browserStore.ts index 041913d506e..ee3bf8f7be3 100644 --- a/src/state/browser/browserStore.ts +++ b/src/state/browser/browserStore.ts @@ -2,7 +2,7 @@ import { debounce } from 'lodash'; import { MMKV } from 'react-native-mmkv'; import { create } from 'zustand'; import { PersistStorage, StorageValue, persist, subscribeWithSelector } from 'zustand/middleware'; -import { DEFAULT_TAB_URL } from '@/components/DappBrowser/constants'; +import { RAINBOW_HOME } from '@/components/DappBrowser/constants'; import { TabData, TabId } from '@/components/DappBrowser/types'; import { generateUniqueId, normalizeUrl } from '@/components/DappBrowser/utils'; import { RainbowError, logger } from '@/logger'; @@ -105,8 +105,8 @@ const persistedBrowserStorage: PersistStorage = { const INITIAL_ACTIVE_TAB_INDEX = 0; const INITIAL_TAB_IDS = [generateUniqueId()]; -const INITIAL_TABS_DATA = new Map([[INITIAL_TAB_IDS[0], { url: DEFAULT_TAB_URL }]]); -const INITIAL_PERSISTED_TAB_URLS: Record = { [INITIAL_TAB_IDS[0]]: DEFAULT_TAB_URL }; +const INITIAL_TABS_DATA = new Map([[INITIAL_TAB_IDS[0], { url: RAINBOW_HOME }]]); +const INITIAL_PERSISTED_TAB_URLS: Record = { [INITIAL_TAB_IDS[0]]: RAINBOW_HOME }; interface BrowserStore { activeTabIndex: number; @@ -146,7 +146,7 @@ export const useBrowserStore = create()( getActiveTabUrl: () => get().persistedTabUrls[get().getActiveTabId()], - getTabData: (tabId: string) => get().tabsData.get(tabId) || { url: DEFAULT_TAB_URL }, + getTabData: (tabId: string) => get().tabsData.get(tabId) || { url: RAINBOW_HOME }, goToPage: (url: string, tabId?: string) => set(state => { @@ -187,7 +187,7 @@ export const useBrowserStore = create()( const existingTabIds = state.tabIds.filter(id => newTabIds.includes(id)); const addedTabIds = newTabIds.filter(id => !state.tabIds.includes(id)); const newTabsData = new Map(state.tabsData); - addedTabIds.forEach(id => newTabsData.set(id, { url: DEFAULT_TAB_URL })); + addedTabIds.forEach(id => newTabsData.set(id, { url: RAINBOW_HOME })); return { tabIds: [...existingTabIds, ...addedTabIds], tabsData: newTabsData }; }), diff --git a/src/state/gas/gasStore.ts b/src/state/gas/gasStore.ts deleted file mode 100644 index b8357f1dcd4..00000000000 --- a/src/state/gas/gasStore.ts +++ /dev/null @@ -1,79 +0,0 @@ -import create from 'zustand'; - -// todo - absolute imports -import { GasFeeLegacyParams, GasFeeLegacyParamsBySpeed, GasFeeParams, GasFeeParamsBySpeed, GasSpeed } from '../../__swaps__/types/gas'; - -import { createStore } from '../internal/createStore'; -import { buildLocalizedTimeUnitString } from '@/__swaps__/utils/time'; -import { gasUtils } from '@/utils'; - -export interface GasStore { - selectedGas: GasFeeParams | GasFeeLegacyParams; - gasFeeParamsBySpeed: GasFeeParamsBySpeed | GasFeeLegacyParamsBySpeed; - customGasModified: boolean; - setCustomSpeed: (speed: GasFeeParams) => void; - setSelectedGas: ({ selectedGas }: { selectedGas: GasFeeParams | GasFeeLegacyParams }) => void; - setGasFeeParamsBySpeed: ({ gasFeeParamsBySpeed }: { gasFeeParamsBySpeed: GasFeeParamsBySpeed | GasFeeLegacyParamsBySpeed }) => void; - clearCustomGasModified: () => void; -} - -export const gasStore = createStore( - (set, get) => ({ - selectedGas: { - maxBaseFee: { - amount: '0', - display: '0.01', - gwei: '0', - }, - maxPriorityFeePerGas: { - amount: '0', - display: '0', - gwei: '0', - }, - option: GasSpeed.FAST, - estimatedTime: { - amount: 12, - display: buildLocalizedTimeUnitString({ plural: true, short: true, unit: '12' }), - }, - display: gasUtils.getGasLabel(gasUtils.FAST), - transactionGasParams: { - maxPriorityFeePerGas: '0', - maxFeePerGas: '0', - }, - gasFee: {}, - } as GasFeeParams | GasFeeLegacyParams, - gasFeeParamsBySpeed: {} as GasFeeParamsBySpeed | GasFeeLegacyParamsBySpeed, - customGasModified: false, - setSelectedGas: ({ selectedGas }) => { - set({ - selectedGas, - }); - }, - setGasFeeParamsBySpeed: ({ gasFeeParamsBySpeed }) => { - set({ - gasFeeParamsBySpeed, - }); - }, - setCustomSpeed: (speed: GasFeeParams) => { - const { gasFeeParamsBySpeed } = get(); - set({ - gasFeeParamsBySpeed: { - ...gasFeeParamsBySpeed, - [GasSpeed.CUSTOM]: speed, - } as GasFeeParamsBySpeed, - customGasModified: true, - }); - }, - clearCustomGasModified: () => { - set({ customGasModified: false }); - }, - }), - { - persist: { - name: 'gas', - version: 0, - }, - } -); - -export const useGasStore = create(gasStore); diff --git a/src/state/performance/performance.ts b/src/state/performance/performance.ts index d94921a13e7..5b489c5bc00 100644 --- a/src/state/performance/performance.ts +++ b/src/state/performance/performance.ts @@ -3,6 +3,7 @@ import { OperationForScreen, PerformanceLog, Screen } from '@/state/performance/ import { analyticsV2 } from '@/analytics'; import { logger } from '@/logger'; import { runOnJS } from 'react-native-reanimated'; +import store from '@/redux/store'; type AnyFunction = (...args: any[]) => any; @@ -23,6 +24,12 @@ interface PerformanceTrackingState { }; } +export function isEnabled() { + const isHardwareWallet = store.getState().wallets.selected?.deviceId; + + return !isHardwareWallet; +} + // Helper function to log performance to Rudderstack function logPerformance({ screen, @@ -33,6 +40,11 @@ function logPerformance({ endOfOperation, }: ExecuteFnParamsWithoutFn & { startTime: number; endTime: number }) { performanceTracking.setState(state => { + if (!isEnabled()) { + logger.debug('[performance]: Performance tracking is disabled'); + return state; + } + const timeToCompletion = endTime - startTime; const log: PerformanceLog = { completedAt: Date.now(), diff --git a/src/state/staleBalances/index.test.ts b/src/state/staleBalances/index.test.ts index f911b5ac423..7ac5bbaa270 100644 --- a/src/state/staleBalances/index.test.ts +++ b/src/state/staleBalances/index.test.ts @@ -1,7 +1,7 @@ import { Address } from 'viem'; import { staleBalancesStore } from '.'; -import { DAI_ADDRESS, OP_ADDRESS } from '@/references'; +import { DAI_ADDRESS } from '@/references'; import { ETH_ADDRESS } from '@rainbow-me/swaps'; import { ChainId } from '@/state/backendNetworks/types'; @@ -9,6 +9,7 @@ const TEST_ADDRESS_1 = '0xFOO'; const TEST_ADDRESS_2 = '0xBAR'; const THEN = Date.now() - 700000; const WHEN = Date.now() + 60000; +const OP_ADDRESS = '0x4200000000000000000000000000000000000042'; test('should be able to add asset information to the staleBalances object', async () => { const { addStaleBalance, staleBalances } = staleBalancesStore.getState(); diff --git a/src/state/swaps/swapsStore.ts b/src/state/swaps/swapsStore.ts index 3651156ba11..b74d961f507 100644 --- a/src/state/swaps/swapsStore.ts +++ b/src/state/swaps/swapsStore.ts @@ -6,7 +6,7 @@ import { ChainId } from '@/state/backendNetworks/types'; import { GasSpeed } from '@/__swaps__/types/gas'; import { RecentSwap } from '@/__swaps__/types/swap'; import { getCachedGasSuggestions } from '@/__swaps__/utils/meteorology'; -import { lessThan } from '@/__swaps__/utils/numbers'; +import { lessThan } from '@/helpers/utilities'; import { getDefaultSlippage } from '@/__swaps__/utils/swaps'; import { RainbowError, logger } from '@/logger'; import { getRemoteConfig } from '@/model/remoteConfig'; diff --git a/src/state/sync/UserAssetsSync.tsx b/src/state/sync/UserAssetsSync.tsx index 48c2e137597..ba0268b9a68 100644 --- a/src/state/sync/UserAssetsSync.tsx +++ b/src/state/sync/UserAssetsSync.tsx @@ -8,8 +8,9 @@ import { ChainId } from '@/state/backendNetworks/types'; export const UserAssetsSync = function UserAssetsSync() { const { accountAddress, nativeCurrency: currentCurrency } = useAccountSettings(); - const isSwapsOpen = useSwapsStore(state => state.isSwapsOpen); + const isUserAssetsStoreMissingData = userAssetsStore.getState().getUserAssets()?.length === 0; + const enabled = (!isSwapsOpen || isUserAssetsStoreMissingData) && !!accountAddress && !!currentCurrency; useUserAssets( { @@ -17,14 +18,14 @@ export const UserAssetsSync = function UserAssetsSync() { currency: currentCurrency, }, { - enabled: !isSwapsOpen, + enabled, select: data => selectorFilterByUserChains({ data, selector: selectUserAssetsList, }), onSuccess: data => { - if (!isSwapsOpen) { + if (!isSwapsOpen || isUserAssetsStoreMissingData) { userAssetsStore.getState().setUserAssets(data as ParsedSearchAsset[]); const inputAsset = userAssetsStore.getState().getHighestValueEth(); diff --git a/src/state/walletConnectRequests/index.ts b/src/state/walletConnectRequests/index.ts new file mode 100644 index 00000000000..9ca699c18e6 --- /dev/null +++ b/src/state/walletConnectRequests/index.ts @@ -0,0 +1,68 @@ +import { reverse, sortBy, values } from 'lodash'; +import { WalletconnectRequestData } from '@/walletConnect/types'; +import { createStore } from '../internal/createStore'; +import { omitFlatten } from '@/helpers/utilities'; + +interface RequestsState { + [requestId: number]: WalletconnectRequestData; +} + +export interface WalletConnectRequestsState { + walletConnectRequests: RequestsState; + getSortedWalletConnectRequests: () => WalletconnectRequestData[]; + addWalletConnectRequest: ({ walletConnectRequest }: { walletConnectRequest: WalletconnectRequestData }) => boolean; + removeWalletConnectRequest: ({ walletConnectRequestId }: { walletConnectRequestId: number }) => void; +} + +export const walletConnectRequestsStore = createStore( + (set, get) => ({ + walletConnectRequests: {}, + getSortedWalletConnectRequests: () => { + const { walletConnectRequests } = get(); + const sortedRequests = reverse(sortBy(values(walletConnectRequests), 'displayDetails.timestampInMs')); + return sortedRequests; + }, + addWalletConnectRequest: ({ walletConnectRequest }) => { + const { walletConnectRequests: currentWalletConnectRequests } = get(); + const requestAlreadyExists = currentWalletConnectRequests[walletConnectRequest.requestId]; + if (requestAlreadyExists) return false; + set({ + walletConnectRequests: { + ...currentWalletConnectRequests, + [walletConnectRequest.requestId]: walletConnectRequest, + }, + }); + return true; + }, + removeWalletConnectRequest: ({ walletConnectRequestId }) => { + const { walletConnectRequests: currentWalletConnectRequests } = get(); + const updatedRequests = omitFlatten(currentWalletConnectRequests, [walletConnectRequestId]); + set({ + walletConnectRequests: { + ...updatedRequests, + }, + }); + }, + }), + { + persist: { + name: 'walletConnectRequests', + version: 1, + }, + } +); + +export const getSortedWalletConnectRequests = (): WalletconnectRequestData[] => { + const { getSortedWalletConnectRequests: getSortedWCRequests } = walletConnectRequestsStore.getState(); + return getSortedWCRequests(); +}; + +export const addNewWalletConnectRequest = ({ walletConnectRequest }: { walletConnectRequest: WalletconnectRequestData }): boolean => { + const { addWalletConnectRequest } = walletConnectRequestsStore.getState(); + return addWalletConnectRequest({ walletConnectRequest }); +}; + +export const removeWalletConnectRequest = ({ walletConnectRequestId }: { walletConnectRequestId: number }): void => { + const { removeWalletConnectRequest: removeWCRequest } = walletConnectRequestsStore.getState(); + removeWCRequest({ walletConnectRequestId }); +}; diff --git a/src/utils/__mocks__/ethereumUtils.ts b/src/utils/__mocks__/ethereumUtils.ts index 0d46f512a7d..071d65ae714 100644 --- a/src/utils/__mocks__/ethereumUtils.ts +++ b/src/utils/__mocks__/ethereumUtils.ts @@ -12,10 +12,7 @@ export default { getBlockExplorer: jest.fn(), getDataString: jest.fn(), getEtherscanHostForNetwork: jest.fn(), - getEthPriceUnit: jest.fn(), getHash: jest.fn(), - getMaticPriceUnit: jest.fn(), - getBnbPriceUnit: jest.fn(), getMultichainAssetAddress: jest.fn(), getNativeAssetForNetwork: jest.fn(), getNetworkNativeAsset: jest.fn(), diff --git a/src/utils/ethereumUtils.ts b/src/utils/ethereumUtils.ts index 854a08646b8..7c095eb3676 100644 --- a/src/utils/ethereumUtils.ts +++ b/src/utils/ethereumUtils.ts @@ -29,17 +29,7 @@ import { convertRawAmountToDecimalFormat, fromWei, greaterThan, isZero, subtract import { Navigation } from '@/navigation'; import { parseAssetNative } from '@/parsers'; import store from '@/redux/store'; -import { - ETH_ADDRESS, - ethUnits, - MATIC_MAINNET_ADDRESS, - optimismGasOracleAbi, - OVM_GAS_PRICE_ORACLE, - BNB_MAINNET_ADDRESS, - AVAX_AVALANCHE_ADDRESS, - DEGEN_CHAIN_DEGEN_ADDRESS, - APECOIN_APECHAIN_ADDRESS, -} from '@/references'; +import { ETH_ADDRESS, ethUnits, optimismGasOracleAbi, OVM_GAS_PRICE_ORACLE } from '@/references'; import Routes from '@/navigation/routesNames'; import { logger, RainbowError } from '@/logger'; import { IS_IOS } from '@/env'; @@ -54,6 +44,44 @@ import { AddressOrEth } from '@/__swaps__/types/assets'; import { useBackendNetworksStore } from '@/state/backendNetworks/backendNetworks'; import { useConnectedToHardhatStore } from '@/state/connectedToHardhat'; +/** + * @deprecated - use `getUniqueId` instead for chainIds + * @desc Get the unique ID for an address and network + * @param address - The address to get the unique ID for + * @param network - The network to get the unique ID for + * @returns `${address}_${network}` + */ +export const getUniqueIdNetwork = (address: EthereumAddress, network: Network) => `${address}_${network}`; + +export const getUniqueId = (address: EthereumAddress, chainId: ChainId) => { + 'worklet'; + return `${address}_${chainId}`; +}; + +/** + * @desc Get the address and chainId from a unique ID + * @param uniqueId - The unique ID to get the address & (chainId || network) from + * @returns { address: AddressOrEth; chainId: ChainId } + */ +export const getAddressAndChainIdFromUniqueId = (uniqueId: string): { address: AddressOrEth; chainId: ChainId } => { + const parts = uniqueId.split('_'); + + // If the unique ID does not contain '_', it's a mainnet address + if (parts.length === 1) { + return { address: parts[0] as AddressOrEth, chainId: ChainId.mainnet }; + } + + const address = parts[0] as AddressOrEth; + const networkOrChainId = parts[1]; + // if the second part is a string, it's probably a network + if (isNaN(Number(networkOrChainId))) { + const chainId = useBackendNetworksStore.getState().getChainsIdByName()[networkOrChainId] || ChainId.mainnet; // Default to mainnet if unknown + return { address, chainId }; + } + + return { address, chainId: +networkOrChainId }; +}; + const getNetworkNativeAsset = ({ chainId }: { chainId: ChainId }) => { const nativeAssetAddress = useBackendNetworksStore.getState().getChainsNativeAsset()[chainId].address; const nativeAssetUniqueId = getUniqueId(nativeAssetAddress, chainId); @@ -152,7 +180,7 @@ const getExternalAssetFromCache = (uniqueId: string) => { return cachedExternalAsset; } catch (e) { - console.log(e); + logger.warn(`[ethereumUtils]: Error retrieving external asset from cache: ${e}`); } }; @@ -169,10 +197,16 @@ const getAccountAsset = (uniqueId: EthereumAddress | undefined): ParsedAddressAs return accountAsset; }; -const getAssetPrice = (address: EthereumAddress = ETH_ADDRESS): number => { - const externalAsset = getExternalAssetFromCache(address); +const getAssetPrice = ( + { address, chainId }: { address: EthereumAddress; chainId: ChainId } = { + address: ETH_ADDRESS, + chainId: ChainId.mainnet, + } +) => { + const uniqueId = getUniqueId(address, chainId); + const externalAsset = getExternalAssetFromCache(uniqueId); const genericPrice = externalAsset?.price?.value; - return genericPrice || getAccountAsset(address)?.price?.value || 0; + return genericPrice || getAccountAsset(uniqueId)?.price?.value || 0; }; export const useNativeAsset = ({ chainId }: { chainId: ChainId }) => { @@ -188,28 +222,12 @@ export const useNativeAsset = ({ chainId }: { chainId: ChainId }) => { return nativeAsset; }; -// anotha 1 const getPriceOfNativeAssetForNetwork = ({ chainId }: { chainId: ChainId }) => { - if (chainId === ChainId.polygon) { - return getMaticPriceUnit(); - } else if (chainId === ChainId.bsc) { - return getBnbPriceUnit(); - } else if (chainId === ChainId.avalanche) { - return getAvaxPriceUnit(); - } else if (chainId === ChainId.degen) { - return getDegenPriceUnit(); - } - return getEthPriceUnit(); + const chainsNativeAsset = useBackendNetworksStore.getState().getChainsNativeAsset(); + const address = (chainsNativeAsset[chainId]?.address || ETH_ADDRESS) as AddressOrEth; + return getAssetPrice({ address, chainId }); }; -const getEthPriceUnit = () => getAssetPrice(); - -const getMaticPriceUnit = () => getAssetPrice(MATIC_MAINNET_ADDRESS); -const getBnbPriceUnit = () => getAssetPrice(BNB_MAINNET_ADDRESS); -const getAvaxPriceUnit = () => getAssetPrice(getUniqueId(AVAX_AVALANCHE_ADDRESS, ChainId.avalanche)); -const getDegenPriceUnit = () => getAssetPrice(getUniqueId(DEGEN_CHAIN_DEGEN_ADDRESS, ChainId.degen)); -const getApechainPriceUnit = () => getAssetPrice(getUniqueId(APECOIN_APECHAIN_ADDRESS, ChainId.apechain)); - const getBalanceAmount = ( selectedGasFee: SelectedGasFee | LegacySelectedGasFee, selected: ParsedAddressAsset, @@ -442,26 +460,6 @@ async function parseEthereumUrl(data: string) { }); } -export const getUniqueIdNetwork = (address: EthereumAddress, network: Network) => `${address}_${network}`; - -export const getUniqueId = (address: EthereumAddress, chainId: ChainId) => `${address}_${chainId}`; - -export const getAddressAndChainIdFromUniqueId = (uniqueId: string): { address: AddressOrEth; chainId: ChainId } => { - const parts = uniqueId.split('_'); - - // If the unique ID does not contain '_', it's a mainnet address - if (parts.length === 1) { - return { address: parts[0] as AddressOrEth, chainId: ChainId.mainnet }; - } - - // If the unique ID contains '_', the last part is the network and the rest is the address - const network = parts[1] as Network; // Assuming the last part is a valid Network enum value - const address = parts[0] as AddressOrEth; - const chainId = useBackendNetworksStore.getState().getChainsIdByName()[network]; - - return { address, chainId }; -}; - const calculateL1FeeOptimism = async ( tx: RainbowTransaction | TransactionRequest, provider: StaticJsonRpcProvider @@ -523,13 +521,7 @@ export default { getBlockExplorer, getDataString, getEtherscanHostForNetwork, - getEthPriceUnit, getHash, - getMaticPriceUnit, - getBnbPriceUnit, - getAvaxPriceUnit, - getDegenPriceUnit, - getApechainPriceUnit, getNativeAssetForNetwork, getNetworkNativeAsset, getPriceOfNativeAssetForNetwork, diff --git a/src/utils/isLowerCaseMatch.ts b/src/utils/isLowerCaseMatch.ts index 24cd7c45ed4..3404eaaa706 100644 --- a/src/utils/isLowerCaseMatch.ts +++ b/src/utils/isLowerCaseMatch.ts @@ -1,3 +1,3 @@ -export default function isLowerCaseMatch(a: string, b: string) { +export default function isLowerCaseMatch(a: string, b?: string) { return a?.toLowerCase() === b?.toLowerCase(); } diff --git a/src/utils/requestNavigationHandlers.ts b/src/utils/requestNavigationHandlers.ts index 7d4f67ba750..0c3d1d21622 100644 --- a/src/utils/requestNavigationHandlers.ts +++ b/src/utils/requestNavigationHandlers.ts @@ -1,18 +1,16 @@ import { Navigation } from '@/navigation'; import Routes from '@/navigation/routesNames'; -// we should move these types since import from redux is not kosher -import { RequestData, WalletconnectRequestData, removeRequest } from '@/redux/requests'; import store from '@/redux/store'; -import { - WalletconnectApprovalSheetRouteParams, - WalletconnectResultType, - walletConnectRemovePendingRedirect, - walletConnectSendStatus, -} from '@/redux/walletconnect'; import { InteractionManager } from 'react-native'; import { SEND_TRANSACTION } from './signingMethods'; import { handleSessionRequestResponse } from '@/walletConnect'; +import { + RequestData, + WalletconnectRequestData, + WalletconnectApprovalSheetRouteParams, + WalletconnectResultType, +} from '@/walletConnect/types'; import { getRequestDisplayDetails } from '@/parsers'; import { maybeSignUri } from '@/handlers/imgix'; import { getActiveRoute } from '@/navigation/Navigation'; @@ -38,6 +36,7 @@ import { ChainId } from '@/state/backendNetworks/types'; import { useBackendNetworksStore } from '@/state/backendNetworks/backendNetworks'; import { MobileWalletProtocolUserErrors } from '@/components/MobileWalletProtocolListener'; import { hideWalletConnectToast } from '@/components/toasts/WalletConnectToast'; +import { removeWalletConnectRequest } from '@/state/walletConnectRequests'; export enum RequestSource { WALLETCONNECT = 'walletconnect', @@ -402,14 +401,10 @@ export const handleDappBrowserRequest = async (request: Omit { - const pendingRedirect = store.getState().walletconnect.pendingRedirect; - const walletConnector = store.getState().walletconnect.walletConnectors[request.peerId]; - - // @ts-expect-error Property '_chainId' is private and only accessible within class 'Connector'.ts(2341) - const chainId = request?.walletConnectV2RequestValues?.chainId || walletConnector?._chainId; + const chainId = request?.walletConnectV2RequestValues?.chainId; + if (!chainId) return; const network = useBackendNetworksStore.getState().getChainsName()[chainId]; - // @ts-expect-error Property '_accounts' is private and only accessible within class 'Connector'.ts(2341) - const address = request?.walletConnectV2RequestValues?.address || walletConnector?._accounts?.[0]; + const address = request?.walletConnectV2RequestValues?.address; const onSuccess = async (result: string) => { if (request?.walletConnectV2RequestValues) { @@ -417,10 +412,10 @@ export const handleWalletConnectRequest = async (request: WalletconnectRequestDa result: result, error: null, }); - } else { - await store.dispatch(walletConnectSendStatus(request?.peerId, request?.requestId, { result })); } - store.dispatch(removeRequest(request?.requestId)); + removeWalletConnectRequest({ + walletConnectRequestId: request.requestId, + }); }; const onCancel = async (error?: Error) => { @@ -430,15 +425,11 @@ export const handleWalletConnectRequest = async (request: WalletconnectRequestDa result: null, error: error || 'User cancelled the request', }); - } else { - await store.dispatch( - walletConnectSendStatus(request?.peerId, request?.requestId, { - error: error || 'User cancelled the request', - }) - ); } - store.dispatch(removeRequest(request?.requestId)); } + removeWalletConnectRequest({ + walletConnectRequestId: request.requestId, + }); }; const onCloseScreen = (canceled: boolean) => { @@ -447,12 +438,6 @@ export const handleWalletConnectRequest = async (request: WalletconnectRequestDa type = `${type}-canceled`; } - if (pendingRedirect) { - InteractionManager.runAfterInteractions(() => { - store.dispatch(walletConnectRemovePendingRedirect(type, request?.dappScheme)); - }); - } - if (request?.walletConnectV2RequestValues?.onComplete) { InteractionManager.runAfterInteractions(() => { request?.walletConnectV2RequestValues?.onComplete?.(type); diff --git a/src/walletConnect/__tests__/index.test b/src/walletConnect/__tests__/index.test index 63109a6cfa7..d01302ed333 100644 --- a/src/walletConnect/__tests__/index.test +++ b/src/walletConnect/__tests__/index.test @@ -36,15 +36,12 @@ jest.mock('react-native', () => ({ jest.mock('@react-native-firebase/messaging', () => ({})); jest.mock('@/redux/store'); -jest.mock('@/redux/walletconnect'); -jest.mock('@/redux/requests'); jest.mock('@/navigation/Navigation', () => ({ handleAction: jest.fn(), })); jest.mock('@/handlers/imgix'); jest.mock('@/utils/ethereumUtils'); jest.mock('@/parsers/requests'); -jest.mock('@/handlers/localstorage/walletconnectRequests'); jest.mock('@/handlers/appEvents'); jest.mock('@/notifications/tokens'); jest.mock('@/model/wallet'); diff --git a/src/walletConnect/index.tsx b/src/walletConnect/index.tsx index 8e16c7760e5..81894116634 100644 --- a/src/walletConnect/index.tsx +++ b/src/walletConnect/index.tsx @@ -1,3 +1,4 @@ +import { addNewWalletConnectRequest, removeWalletConnectRequest } from '@/state/walletConnectRequests'; import React from 'react'; import { InteractionManager } from 'react-native'; import { SignClientTypes, SessionTypes } from '@walletconnect/types'; @@ -12,9 +13,7 @@ import WalletConnectCore, { Core } from '@walletconnect/core'; import { WalletKit, WalletKitTypes, IWalletKit } from '@reown/walletkit'; import { isHexString } from '@ethersproject/bytes'; import { toUtf8String } from '@ethersproject/strings'; - import { logger, RainbowError } from '@/logger'; -import { WalletconnectApprovalSheetRouteParams } from '@/redux/walletconnect'; import Navigation, { getActiveRoute } from '@/navigation/Navigation'; import Routes from '@/navigation/routesNames'; import { analyticsV2 as analytics } from '@/analytics'; @@ -25,8 +24,6 @@ import store from '@/redux/store'; import { findWalletWithAccount } from '@/helpers/findWalletWithAccount'; import WalletTypes from '@/helpers/walletTypes'; import { getRequestDisplayDetails } from '@/parsers/requests'; -import { WalletconnectRequestData, REQUESTS_UPDATE_REQUESTS_TO_APPROVE, removeRequest } from '@/redux/requests'; -import { saveLocalRequests } from '@/handlers/localstorage/walletconnectRequests'; import { events } from '@/handlers/appEvents'; import { getFCMToken } from '@/notifications/tokens'; import { IS_DEV, IS_ANDROID, IS_IOS } from '@/env'; @@ -34,7 +31,14 @@ import { loadWallet } from '@/model/wallet'; import * as portal from '@/screens/Portal'; import * as explain from '@/screens/Explain'; import { Box } from '@/design-system'; -import { AuthRequestAuthenticateSignature, AuthRequestResponseErrorReason, RPCMethod, RPCPayload } from '@/walletConnect/types'; +import { + AuthRequestAuthenticateSignature, + AuthRequestResponseErrorReason, + RPCMethod, + RPCPayload, + WalletconnectApprovalSheetRouteParams, + WalletconnectRequestData, +} from '@/walletConnect/types'; import { AuthRequest } from '@/walletConnect/sheets/AuthRequest'; import { getProvider } from '@/handlers/web3'; import { uniq } from 'lodash'; @@ -507,10 +511,6 @@ export async function onSessionProposal(proposal: WalletKitTypes.SessionProposal try { if (namespaces.success) { /** - * This is equivalent handling of setPendingRequest and - * walletConnectApproveSession, since setPendingRequest is only used - * within the /redux/walletconnect handlers - * * WC v2 stores existing _pairings_ itself, so we don't need to persist * ourselves */ @@ -711,7 +711,7 @@ export async function onSessionRequest(event: SignClientTypes.EventArguments['se return; } - const { nativeCurrency, network } = store.getState().settings; + const { nativeCurrency } = store.getState().settings; const chainId = Number(event.params.chainId.split(':')[1]); logger.debug(`[walletConnect]: getting session for topic`, { session }); @@ -753,21 +753,9 @@ export async function onSessionRequest(event: SignClientTypes.EventArguments['se }, }; - const { requests: pendingRequests } = store.getState().requests; - - if (!pendingRequests[request.requestId]) { - const updatedRequests = { - ...pendingRequests, - [request.requestId]: request, - }; - store.dispatch({ - payload: updatedRequests, - type: REQUESTS_UPDATE_REQUESTS_TO_APPROVE, - }); - saveLocalRequests(updatedRequests, address, network); - + const addedNewRequest = addNewWalletConnectRequest({ walletConnectRequest: request }); + if (addedNewRequest) { logger.debug(`[walletConnect]: navigating to CONFIRM_REQUEST sheet`, {}, logger.DebugContext.walletconnect); - handleWalletConnectRequest(request); analytics.track(analytics.event.wcShowingSigningRequest, { @@ -832,8 +820,7 @@ export async function handleSessionRequestResponse( logger.debug(`[walletConnect]: handleSessionRequestResponse reject`, {}, logger.DebugContext.walletconnect); await client.respondSessionRequest(payload); } - - store.dispatch(removeRequest(sessionRequestEvent.id)); + removeWalletConnectRequest({ walletConnectRequestId: sessionRequestEvent.id }); } export async function onSessionAuthenticate(event: WalletKitTypes.SessionAuthenticate) { diff --git a/src/walletConnect/types.ts b/src/walletConnect/types.ts index 8edeb5c867b..239265a1220 100644 --- a/src/walletConnect/types.ts +++ b/src/walletConnect/types.ts @@ -1,3 +1,115 @@ +import { ChainId } from '@/state/backendNetworks/types'; +import { Address } from 'viem'; +import { SignClientTypes, Verify } from '@walletconnect/types'; +import { RequestSource } from '@/utils/requestNavigationHandlers'; + +/** + * Display details loaded for a request. + */ +interface RequestDisplayDetails { + /** + * Data loaded for the request, depending on the type of request. + */ + request: any; + + /** + * The timestamp for the request. + */ + timestampInMs: number; +} + +export interface RequestData { + dappName: string; + imageUrl: string | undefined; + address: string; + chainId: ChainId; + dappUrl: string; + payload: any; + displayDetails: RequestDisplayDetails | null | Record; +} + +/** + * A request stored in state. + */ +export interface WalletconnectRequestData extends RequestData { + /** + * The WalletConnect client ID for the request. + */ + clientId: string; + + /** + * The WalletConnect peer ID for the request. + */ + peerId: string; + + /** + * The request ID. + */ + requestId: number; + + /** + * The URL scheme to use for re-opening the dapp, or null. + */ + dappScheme: string | null; + + /** + * The display details loaded for the request. + */ + displayDetails: RequestDisplayDetails | null | Record; + + /** + * Adds additional data to the request and serves as a notice that this + * request originated from a WC v2 session + */ + walletConnectV2RequestValues?: { + sessionRequestEvent: SignClientTypes.EventArguments['session_request']; + address: string; + chainId: number; + onComplete?: (type: string) => void; + }; +} + +/** + * Represents a WalletConnect result passed to a callback function. + */ +export type WalletconnectResultType = 'timedOut' | 'sign' | 'transaction' | 'sign-canceled' | 'transaction-canceled' | 'connect' | 'reject'; + +export type WalletconnectMeta = { + /** + * WC v2 introduced multi-chain support, while v1 only supported a single + * chain at a time. To avoid confusion, we now send both as an array + * `chainIds`. WC v1 will always be an array with length `1`, while v2 can + * have more than one. + */ + chainIds: number[]; + isWalletConnectV2?: boolean; + proposedChainId?: number; + proposedAddress?: string; +} & Pick; + +/** + * Route parameters sent to a WalletConnect approval sheet. + */ +export interface WalletconnectApprovalSheetRouteParams { + callback: ( + approved: boolean, + chainId: ChainId, + accountAddress: Address, + peerId: WalletconnectRequestData['peerId'], + dappScheme: WalletconnectRequestData['dappScheme'], + dappName: WalletconnectRequestData['dappName'], + dappUrl: WalletconnectRequestData['dappUrl'] + ) => Promise; + receivedTimestamp: number; + currentChainId?: ChainId; + meta?: WalletconnectMeta; + timeout?: ReturnType | null; + timedOut?: boolean; + failureExplainSheetVariant?: string; + verifiedData?: Verify.Context['verified']; + source?: RequestSource; +} + export enum RPCMethod { Sign = 'eth_sign', PersonalSign = 'personal_sign',